Son CV dans un terminal web en Javascript!
https://terminal-cv.gregandev.fr
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
11 KiB
465 lines
11 KiB
'use strict';
|
|
|
|
var escapedChars = {
|
|
'b': '\b',
|
|
'f': '\f',
|
|
'n': '\n',
|
|
'r': '\r',
|
|
't': '\t',
|
|
'"': '"',
|
|
'/': '/',
|
|
'\\': '\\'
|
|
};
|
|
|
|
var A_CODE = 'a'.charCodeAt();
|
|
|
|
|
|
exports.parse = function (source, _, options) {
|
|
var pointers = {};
|
|
var line = 0;
|
|
var column = 0;
|
|
var pos = 0;
|
|
var bigint = options && options.bigint && typeof BigInt != 'undefined';
|
|
return {
|
|
data: _parse('', true),
|
|
pointers: pointers
|
|
};
|
|
|
|
function _parse(ptr, topLevel) {
|
|
whitespace();
|
|
var data;
|
|
map(ptr, 'value');
|
|
var char = getChar();
|
|
switch (char) {
|
|
case 't': read('rue'); data = true; break;
|
|
case 'f': read('alse'); data = false; break;
|
|
case 'n': read('ull'); data = null; break;
|
|
case '"': data = parseString(); break;
|
|
case '[': data = parseArray(ptr); break;
|
|
case '{': data = parseObject(ptr); break;
|
|
default:
|
|
backChar();
|
|
if ('-0123456789'.indexOf(char) >= 0)
|
|
data = parseNumber();
|
|
else
|
|
unexpectedToken();
|
|
}
|
|
map(ptr, 'valueEnd');
|
|
whitespace();
|
|
if (topLevel && pos < source.length) unexpectedToken();
|
|
return data;
|
|
}
|
|
|
|
function whitespace() {
|
|
loop:
|
|
while (pos < source.length) {
|
|
switch (source[pos]) {
|
|
case ' ': column++; break;
|
|
case '\t': column += 4; break;
|
|
case '\r': column = 0; break;
|
|
case '\n': column = 0; line++; break;
|
|
default: break loop;
|
|
}
|
|
pos++;
|
|
}
|
|
}
|
|
|
|
function parseString() {
|
|
var str = '';
|
|
var char;
|
|
while (true) {
|
|
char = getChar();
|
|
if (char == '"') {
|
|
break;
|
|
} else if (char == '\\') {
|
|
char = getChar();
|
|
if (char in escapedChars)
|
|
str += escapedChars[char];
|
|
else if (char == 'u')
|
|
str += getCharCode();
|
|
else
|
|
wasUnexpectedToken();
|
|
} else {
|
|
str += char;
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
function parseNumber() {
|
|
var numStr = '';
|
|
var integer = true;
|
|
if (source[pos] == '-') numStr += getChar();
|
|
|
|
numStr += source[pos] == '0'
|
|
? getChar()
|
|
: getDigits();
|
|
|
|
if (source[pos] == '.') {
|
|
numStr += getChar() + getDigits();
|
|
integer = false;
|
|
}
|
|
|
|
if (source[pos] == 'e' || source[pos] == 'E') {
|
|
numStr += getChar();
|
|
if (source[pos] == '+' || source[pos] == '-') numStr += getChar();
|
|
numStr += getDigits();
|
|
integer = false;
|
|
}
|
|
|
|
var result = +numStr;
|
|
return bigint && integer && (result > Number.MAX_SAFE_INTEGER || result < Number.MIN_SAFE_INTEGER)
|
|
? BigInt(numStr)
|
|
: result;
|
|
}
|
|
|
|
function parseArray(ptr) {
|
|
whitespace();
|
|
var arr = [];
|
|
var i = 0;
|
|
if (getChar() == ']') return arr;
|
|
backChar();
|
|
|
|
while (true) {
|
|
var itemPtr = ptr + '/' + i;
|
|
arr.push(_parse(itemPtr));
|
|
whitespace();
|
|
var char = getChar();
|
|
if (char == ']') break;
|
|
if (char != ',') wasUnexpectedToken();
|
|
whitespace();
|
|
i++;
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
function parseObject(ptr) {
|
|
whitespace();
|
|
var obj = {};
|
|
if (getChar() == '}') return obj;
|
|
backChar();
|
|
|
|
while (true) {
|
|
var loc = getLoc();
|
|
if (getChar() != '"') wasUnexpectedToken();
|
|
var key = parseString();
|
|
var propPtr = ptr + '/' + escapeJsonPointer(key);
|
|
mapLoc(propPtr, 'key', loc);
|
|
map(propPtr, 'keyEnd');
|
|
whitespace();
|
|
if (getChar() != ':') wasUnexpectedToken();
|
|
whitespace();
|
|
obj[key] = _parse(propPtr);
|
|
whitespace();
|
|
var char = getChar();
|
|
if (char == '}') break;
|
|
if (char != ',') wasUnexpectedToken();
|
|
whitespace();
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
function read(str) {
|
|
for (var i=0; i<str.length; i++)
|
|
if (getChar() !== str[i]) wasUnexpectedToken();
|
|
}
|
|
|
|
function getChar() {
|
|
checkUnexpectedEnd();
|
|
var char = source[pos];
|
|
pos++;
|
|
column++; // new line?
|
|
return char;
|
|
}
|
|
|
|
function backChar() {
|
|
pos--;
|
|
column--;
|
|
}
|
|
|
|
function getCharCode() {
|
|
var count = 4;
|
|
var code = 0;
|
|
while (count--) {
|
|
code <<= 4;
|
|
var char = getChar().toLowerCase();
|
|
if (char >= 'a' && char <= 'f')
|
|
code += char.charCodeAt() - A_CODE + 10;
|
|
else if (char >= '0' && char <= '9')
|
|
code += +char;
|
|
else
|
|
wasUnexpectedToken();
|
|
}
|
|
return String.fromCharCode(code);
|
|
}
|
|
|
|
function getDigits() {
|
|
var digits = '';
|
|
while (source[pos] >= '0' && source[pos] <= '9')
|
|
digits += getChar();
|
|
|
|
if (digits.length) return digits;
|
|
checkUnexpectedEnd();
|
|
unexpectedToken();
|
|
}
|
|
|
|
function map(ptr, prop) {
|
|
mapLoc(ptr, prop, getLoc());
|
|
}
|
|
|
|
function mapLoc(ptr, prop, loc) {
|
|
pointers[ptr] = pointers[ptr] || {};
|
|
pointers[ptr][prop] = loc;
|
|
}
|
|
|
|
function getLoc() {
|
|
return {
|
|
line: line,
|
|
column: column,
|
|
pos: pos
|
|
};
|
|
}
|
|
|
|
function unexpectedToken() {
|
|
throw new SyntaxError('Unexpected token ' + source[pos] + ' in JSON at position ' + pos);
|
|
}
|
|
|
|
function wasUnexpectedToken() {
|
|
backChar();
|
|
unexpectedToken();
|
|
}
|
|
|
|
function checkUnexpectedEnd() {
|
|
if (pos >= source.length)
|
|
throw new SyntaxError('Unexpected end of JSON input');
|
|
}
|
|
};
|
|
|
|
|
|
exports.stringify = function (data, _, options) {
|
|
if (!validType(data)) return;
|
|
var wsLine = 0;
|
|
var wsPos, wsColumn;
|
|
var whitespace = typeof options == 'object'
|
|
? options.space
|
|
: options;
|
|
switch (typeof whitespace) {
|
|
case 'number':
|
|
var len = whitespace > 10
|
|
? 10
|
|
: whitespace < 0
|
|
? 0
|
|
: Math.floor(whitespace);
|
|
whitespace = len && repeat(len, ' ');
|
|
wsPos = len;
|
|
wsColumn = len;
|
|
break;
|
|
case 'string':
|
|
whitespace = whitespace.slice(0, 10);
|
|
wsPos = 0;
|
|
wsColumn = 0;
|
|
for (var j=0; j<whitespace.length; j++) {
|
|
var char = whitespace[j];
|
|
switch (char) {
|
|
case ' ': wsColumn++; break;
|
|
case '\t': wsColumn += 4; break;
|
|
case '\r': wsColumn = 0; break;
|
|
case '\n': wsColumn = 0; wsLine++; break;
|
|
default: throw new Error('whitespace characters not allowed in JSON');
|
|
}
|
|
wsPos++;
|
|
}
|
|
break;
|
|
default:
|
|
whitespace = undefined;
|
|
}
|
|
|
|
var json = '';
|
|
var pointers = {};
|
|
var line = 0;
|
|
var column = 0;
|
|
var pos = 0;
|
|
var es6 = options && options.es6 && typeof Map == 'function';
|
|
_stringify(data, 0, '');
|
|
return {
|
|
json: json,
|
|
pointers: pointers
|
|
};
|
|
|
|
function _stringify(_data, lvl, ptr) {
|
|
map(ptr, 'value');
|
|
switch (typeof _data) {
|
|
case 'number':
|
|
case 'bigint':
|
|
case 'boolean':
|
|
out('' + _data); break;
|
|
case 'string':
|
|
out(quoted(_data)); break;
|
|
case 'object':
|
|
if (_data === null) {
|
|
out('null');
|
|
} else if (typeof _data.toJSON == 'function') {
|
|
out(quoted(_data.toJSON()));
|
|
} else if (Array.isArray(_data)) {
|
|
stringifyArray();
|
|
} else if (es6) {
|
|
if (_data.constructor.BYTES_PER_ELEMENT)
|
|
stringifyArray();
|
|
else if (_data instanceof Map)
|
|
stringifyMapSet();
|
|
else if (_data instanceof Set)
|
|
stringifyMapSet(true);
|
|
else
|
|
stringifyObject();
|
|
} else {
|
|
stringifyObject();
|
|
}
|
|
}
|
|
map(ptr, 'valueEnd');
|
|
|
|
function stringifyArray() {
|
|
if (_data.length) {
|
|
out('[');
|
|
var itemLvl = lvl + 1;
|
|
for (var i=0; i<_data.length; i++) {
|
|
if (i) out(',');
|
|
indent(itemLvl);
|
|
var item = validType(_data[i]) ? _data[i] : null;
|
|
var itemPtr = ptr + '/' + i;
|
|
_stringify(item, itemLvl, itemPtr);
|
|
}
|
|
indent(lvl);
|
|
out(']');
|
|
} else {
|
|
out('[]');
|
|
}
|
|
}
|
|
|
|
function stringifyObject() {
|
|
var keys = Object.keys(_data);
|
|
if (keys.length) {
|
|
out('{');
|
|
var propLvl = lvl + 1;
|
|
for (var i=0; i<keys.length; i++) {
|
|
var key = keys[i];
|
|
var value = _data[key];
|
|
if (validType(value)) {
|
|
if (i) out(',');
|
|
var propPtr = ptr + '/' + escapeJsonPointer(key);
|
|
indent(propLvl);
|
|
map(propPtr, 'key');
|
|
out(quoted(key));
|
|
map(propPtr, 'keyEnd');
|
|
out(':');
|
|
if (whitespace) out(' ');
|
|
_stringify(value, propLvl, propPtr);
|
|
}
|
|
}
|
|
indent(lvl);
|
|
out('}');
|
|
} else {
|
|
out('{}');
|
|
}
|
|
}
|
|
|
|
function stringifyMapSet(isSet) {
|
|
if (_data.size) {
|
|
out('{');
|
|
var propLvl = lvl + 1;
|
|
var first = true;
|
|
var entries = _data.entries();
|
|
var entry = entries.next();
|
|
while (!entry.done) {
|
|
var item = entry.value;
|
|
var key = item[0];
|
|
var value = isSet ? true : item[1];
|
|
if (validType(value)) {
|
|
if (!first) out(',');
|
|
first = false;
|
|
var propPtr = ptr + '/' + escapeJsonPointer(key);
|
|
indent(propLvl);
|
|
map(propPtr, 'key');
|
|
out(quoted(key));
|
|
map(propPtr, 'keyEnd');
|
|
out(':');
|
|
if (whitespace) out(' ');
|
|
_stringify(value, propLvl, propPtr);
|
|
}
|
|
entry = entries.next();
|
|
}
|
|
indent(lvl);
|
|
out('}');
|
|
} else {
|
|
out('{}');
|
|
}
|
|
}
|
|
}
|
|
|
|
function out(str) {
|
|
column += str.length;
|
|
pos += str.length;
|
|
json += str;
|
|
}
|
|
|
|
function indent(lvl) {
|
|
if (whitespace) {
|
|
json += '\n' + repeat(lvl, whitespace);
|
|
line++;
|
|
column = 0;
|
|
while (lvl--) {
|
|
if (wsLine) {
|
|
line += wsLine;
|
|
column = wsColumn;
|
|
} else {
|
|
column += wsColumn;
|
|
}
|
|
pos += wsPos;
|
|
}
|
|
pos += 1; // \n character
|
|
}
|
|
}
|
|
|
|
function map(ptr, prop) {
|
|
pointers[ptr] = pointers[ptr] || {};
|
|
pointers[ptr][prop] = {
|
|
line: line,
|
|
column: column,
|
|
pos: pos
|
|
};
|
|
}
|
|
|
|
function repeat(n, str) {
|
|
return Array(n + 1).join(str);
|
|
}
|
|
};
|
|
|
|
|
|
var VALID_TYPES = ['number', 'bigint', 'boolean', 'string', 'object'];
|
|
function validType(data) {
|
|
return VALID_TYPES.indexOf(typeof data) >= 0;
|
|
}
|
|
|
|
|
|
var ESC_QUOTE = /"|\\/g;
|
|
var ESC_B = /[\b]/g;
|
|
var ESC_F = /\f/g;
|
|
var ESC_N = /\n/g;
|
|
var ESC_R = /\r/g;
|
|
var ESC_T = /\t/g;
|
|
function quoted(str) {
|
|
str = str.replace(ESC_QUOTE, '\\$&')
|
|
.replace(ESC_F, '\\f')
|
|
.replace(ESC_B, '\\b')
|
|
.replace(ESC_N, '\\n')
|
|
.replace(ESC_R, '\\r')
|
|
.replace(ESC_T, '\\t');
|
|
return '"' + str + '"';
|
|
}
|
|
|
|
|
|
var ESC_0 = /~/g;
|
|
var ESC_1 = /\//g;
|
|
function escapeJsonPointer(str) {
|
|
return str.replace(ESC_0, '~0')
|
|
.replace(ESC_1, '~1');
|
|
}
|
|
|