UNPKG

@soon92/react-json-editor-ajrm

Version:

A stylish, editor-like, modular, react component for viewing, editing, and debugging javascript object syntax!

1,422 lines (1,250 loc) 75.5 kB
/** @license @soon92/react-json-editor-ajrm v2.5.16 * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ "use strict"; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof")); var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized")); var _react = _interopRequireWildcard(require("react")); var _themes = _interopRequireDefault(require("./themes")); var _mitsuketa = require("./mitsuketa"); var _err = _interopRequireDefault(require("./err")); var _locale = require("./locale"); var _en = _interopRequireDefault(require("./locale/en")); var JSONInput = /*#__PURE__*/ function (_Component) { (0, _inherits2.default)(JSONInput, _Component); function JSONInput(props) { var _this; (0, _classCallCheck2.default)(this, JSONInput); _this = (0, _possibleConstructorReturn2.default)(this, (0, _getPrototypeOf2.default)(JSONInput).call(this, props)); _this.updateInternalProps = _this.updateInternalProps.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.createMarkup = _this.createMarkup.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onClick = _this.onClick.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onBlur = _this.onBlur.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.update = _this.update.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.getCursorPosition = _this.getCursorPosition.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.setCursorPosition = _this.setCursorPosition.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.scheduledUpdate = _this.scheduledUpdate.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.setUpdateTime = _this.setUpdateTime.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.renderLabels = _this.renderLabels.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.newSpan = _this.newSpan.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.renderErrorMessage = _this.renderErrorMessage.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onScroll = _this.onScroll.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.showPlaceholder = _this.showPlaceholder.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.tokenize = _this.tokenize.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onKeyPress = _this.onKeyPress.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onKeyDown = _this.onKeyDown.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.onPaste = _this.onPaste.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.stopEvent = _this.stopEvent.bind((0, _assertThisInitialized2.default)((0, _assertThisInitialized2.default)(_this))); _this.refContent = null; _this.refLabels = null; _this.updateInternalProps(); _this.renderCount = 1; _this.state = { prevPlaceholder: '', markupText: '', plainText: '', json: '', jsObject: undefined, lines: false, error: false }; if (!_this.props.locale) { console.warn("[react-json-editor-ajrm - Deprecation Warning] You did not provide a 'locale' prop for your JSON input - This will be required in a future version. English has been set as a default."); } return _this; } (0, _createClass2.default)(JSONInput, [{ key: "updateInternalProps", value: function updateInternalProps() { var colors = {}, style = {}, theme = _themes.default.dark_vscode_tribute; if ('theme' in this.props) if (typeof this.props.theme === 'string') if (this.props.theme in _themes.default) theme = _themes.default[this.props.theme]; colors = theme; if ('colors' in this.props) colors = { default: 'default' in this.props.colors ? this.props.colors.default : colors.default, string: 'string' in this.props.colors ? this.props.colors.string : colors.string, number: 'number' in this.props.colors ? this.props.colors.number : colors.number, colon: 'colon' in this.props.colors ? this.props.colors.colon : colors.colon, keys: 'keys' in this.props.colors ? this.props.colors.keys : colors.keys, keys_whiteSpace: 'keys_whiteSpace' in this.props.colors ? this.props.colors.keys_whiteSpace : colors.keys_whiteSpace, primitive: 'primitive' in this.props.colors ? this.props.colors.primitive : colors.primitive, error: 'error' in this.props.colors ? this.props.colors.error : colors.error, background: 'background' in this.props.colors ? this.props.colors.background : colors.background, background_warning: 'background_warning' in this.props.colors ? this.props.colors.background_warning : colors.background_warning }; this.colors = colors; if ('style' in this.props) style = { outerBox: 'outerBox' in this.props.style ? this.props.style.outerBox : {}, container: 'container' in this.props.style ? this.props.style.container : {}, warningBox: 'warningBox' in this.props.style ? this.props.style.warningBox : {}, errorMessage: 'errorMessage' in this.props.style ? this.props.style.errorMessage : {}, body: 'body' in this.props.style ? this.props.style.body : {}, labelColumn: 'labelColumn' in this.props.style ? this.props.style.labelColumn : {}, labels: 'labels' in this.props.style ? this.props.style.labels : {}, contentBox: 'contentBox' in this.props.style ? this.props.style.contentBox : {} };else style = { outerBox: {}, container: {}, warningBox: {}, errorMessage: {}, body: {}, labelColumn: {}, labels: {}, contentBox: {} }; this.style = style; this.confirmGood = 'confirmGood' in this.props ? this.props.confirmGood : true; var totalHeight = this.props.height || '610px', totalWidth = this.props.width || '479px'; this.totalHeight = totalHeight; this.totalWidth = totalWidth; if (!('onKeyPressUpdate' in this.props) || this.props.onKeyPressUpdate) { if (!this.timer) this.timer = setInterval(this.scheduledUpdate, 100); } else if (this.timer) { clearInterval(this.timer); this.timer = false; } this.updateTime = false; this.waitAfterKeyPress = 'waitAfterKeyPress' in this.props ? this.props.waitAfterKeyPress : 1000; this.resetConfiguration = 'reset' in this.props ? this.props.reset : false; } }, { key: "render", value: function render() { var _this2 = this; var id = this.props.id, markupText = this.state.markupText, error = this.props.error || this.state.error, colors = this.colors, style = this.style, confirmGood = this.confirmGood, totalHeight = this.totalHeight, totalWidth = this.totalWidth, hasError = !!this.props.error || (error ? 'token' in error : false); this.renderCount++; return _react.default.createElement("div", { name: "outer-box", id: id && id + '-outer-box', style: (0, _objectSpread2.default)({ display: 'block', overflow: 'none', height: totalHeight, width: totalWidth, margin: 0, boxSizing: 'border-box', position: 'relative' }, style.outerBox) }, confirmGood ? _react.default.createElement("div", { style: { opacity: hasError ? 0 : 1, height: '30px', width: '30px', position: 'absolute', top: 0, right: 0, transform: 'translate(-25%,25%)', pointerEvents: 'none', transitionDuration: '0.2s', transitionTimingFunction: 'cubic-bezier(0, 1, 0.5, 1)' } }, _react.default.createElement("svg", { height: "30px", width: "30px", viewBox: "0 0 100 100" }, _react.default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", fill: "green", opacity: "0.85", d: "M39.363,79L16,55.49l11.347-11.419L39.694,56.49L72.983,23L84,34.085L39.363,79z" }))) : void 0, _react.default.createElement("div", { name: "container", id: id && id + '-container', style: (0, _objectSpread2.default)({ display: 'block', height: totalHeight, width: totalWidth, margin: 0, boxSizing: 'border-box', overflow: 'hidden', fontFamily: 'Roboto, sans-serif' }, style.container), onClick: this.onClick }, _react.default.createElement("div", { name: "warning-box", id: id && id + '-warning-box', style: (0, _objectSpread2.default)({ display: 'block', overflow: 'hidden', height: hasError ? '60px' : '0px', width: '100%', margin: 0, backgroundColor: colors.background_warning, transitionDuration: '0.2s', transitionTimingFunction: 'cubic-bezier(0, 1, 0.5, 1)' }, style.warningBox), onClick: this.onClick }, _react.default.createElement("span", { style: { display: 'inline-block', height: '60px', width: '60px', margin: 0, boxSizing: 'border-box', overflow: 'hidden', verticalAlign: 'top', pointerEvents: 'none' }, onClick: this.onClick }, _react.default.createElement("div", { style: { position: 'relative', top: 0, left: 0, height: '60px', width: '60px', margin: 0, pointerEvents: 'none' }, onClick: this.onClick }, _react.default.createElement("div", { style: { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', pointerEvents: 'none' }, onClick: this.onClick }, _react.default.createElement("svg", { height: "25px", width: "25px", viewBox: "0 0 100 100" }, _react.default.createElement("path", { fillRule: "evenodd", clipRule: "evenodd", fill: "red", d: "M73.9,5.75c0.467-0.467,1.067-0.7,1.8-0.7c0.7,0,1.283,0.233,1.75,0.7l16.8,16.8 c0.467,0.5,0.7,1.084,0.7,1.75c0,0.733-0.233,1.334-0.7,1.801L70.35,50l23.9,23.95c0.5,0.467,0.75,1.066,0.75,1.8 c0,0.667-0.25,1.25-0.75,1.75l-16.8,16.75c-0.534,0.467-1.117,0.7-1.75,0.7s-1.233-0.233-1.8-0.7L50,70.351L26.1,94.25 c-0.567,0.467-1.167,0.7-1.8,0.7c-0.667,0-1.283-0.233-1.85-0.7L5.75,77.5C5.25,77,5,76.417,5,75.75c0-0.733,0.25-1.333,0.75-1.8 L29.65,50L5.75,26.101C5.25,25.667,5,25.066,5,24.3c0-0.666,0.25-1.25,0.75-1.75l16.8-16.8c0.467-0.467,1.05-0.7,1.75-0.7 c0.733,0,1.333,0.233,1.8,0.7L50,29.65L73.9,5.75z" }))))), _react.default.createElement("span", { style: { display: 'inline-block', height: '60px', width: 'calc(100% - 60px)', margin: 0, overflow: 'hidden', verticalAlign: 'top', position: 'absolute', pointerEvents: 'none' }, onClick: this.onClick }, this.renderErrorMessage())), _react.default.createElement("div", { name: "body", id: id && id + '-body', style: (0, _objectSpread2.default)({ display: 'flex', overflow: 'none', height: hasError ? 'calc(100% - 60px)' : '100%', width: '', margin: 0, resize: 'none', fontFamily: 'Roboto Mono, Monaco, monospace', fontSize: '11px', backgroundColor: colors.background, transitionDuration: '0.2s', transitionTimingFunction: 'cubic-bezier(0, 1, 0.5, 1)' }, style.body), onClick: this.onClick }, _react.default.createElement("span", { name: "labels", id: id && id + '-labels', ref: function ref(_ref) { return _this2.refLabels = _ref; }, style: (0, _objectSpread2.default)({ display: 'inline-block', boxSizing: 'border-box', verticalAlign: 'top', height: '100%', width: '44px', margin: 0, padding: '5px 0px 5px 10px', overflow: 'hidden', color: '#D4D4D4' }, style.labelColumn), onClick: this.onClick }, this.renderLabels()), _react.default.createElement("span", { id: id, ref: function ref(_ref2) { return _this2.refContent = _ref2; }, contentEditable: true, style: (0, _objectSpread2.default)({ display: 'inline-block', boxSizing: 'border-box', verticalAlign: 'top', height: '100%', width: '', flex: 1, margin: 0, padding: '5px', overflowX: 'hidden', overflowY: 'auto', wordWrap: 'break-word', whiteSpace: 'pre-line', color: '#D4D4D4', outline: 'none' }, style.contentBox), dangerouslySetInnerHTML: this.createMarkup(markupText), onKeyPress: this.onKeyPress, onKeyDown: this.onKeyDown, onClick: this.onClick, onBlur: this.onBlur, onScroll: this.onScroll, onPaste: this.onPaste, autoComplete: "off", autoCorrect: "off", autoCapitalize: "off", spellCheck: false })))); } }, { key: "renderErrorMessage", value: function renderErrorMessage() { var locale = this.props.locale || _en.default, error = this.props.error || this.state.error, style = this.style; if (!error) return void 0; return _react.default.createElement("p", { style: (0, _objectSpread2.default)({ color: 'red', fontSize: '12px', position: 'absolute', width: 'calc(100% - 60px)', height: '60px', boxSizing: 'border-box', margin: 0, padding: 0, paddingRight: '10px', overflowWrap: 'break-word', display: 'flex', flexDirection: 'column', justifyContent: 'center' }, style.errorMessage) }, (0, _locale.format)(locale.format, error)); } }, { key: "renderLabels", value: function renderLabels() { var colors = this.colors, style = this.style, error = this.props.error || this.state.error, errorLine = error ? error.line : -1, lines = this.state.lines ? this.state.lines : 1; var labels = new Array(lines); for (var i = 0; i < lines - 1; i++) { labels[i] = i + 1; } return labels.map(function (number) { var color = number !== errorLine ? colors.default : 'red'; return _react.default.createElement("div", { key: number, style: (0, _objectSpread2.default)({}, style.labels, { color: color }) }, number); }); } }, { key: "createMarkup", value: function createMarkup(markupText) { if (markupText === undefined) return { __html: '' }; return { __html: '' + markupText }; } }, { key: "newSpan", value: function newSpan(i, token, depth) { var colors = this.colors, type = token.type, string = token.string; var color = ''; switch (type) { case 'string': case 'number': case 'primitive': case 'error': color = colors[token.type]; break; case 'key': if (string === ' ') color = colors.keys_whiteSpace;else color = colors.keys; break; case 'symbol': if (string === ':') color = colors.colon;else color = colors.default; break; default: color = colors.default; break; } if (string.length !== string.replace(/</g, '').replace(/>/g, '').length) string = '<xmp style=display:inline;>' + string + '</xmp>'; return '<span' + ' type="' + type + '"' + ' value="' + string + '"' + ' depth="' + depth + '"' + ' style="color:' + color + '"' + '>' + string + '</span>'; } }, { key: "getCursorPosition", value: function getCursorPosition(countBR) { var _this3 = this; /** * Need to deprecate countBR * It is used to differenciate between good markup render, and aux render when error found * Adjustments based on coundBR account for usage of <br> instead of <span> for linebreaks to determine acurate cursor position * Find a way to consolidate render styles */ var isChildOf = function isChildOf(node) { while (node !== null) { if (node === _this3.refContent) return true; node = node.parentNode; } return false; }; var selection = window.getSelection(), charCount = -1, linebreakCount = 0, node; if (selection.focusNode && isChildOf(selection.focusNode)) { node = selection.focusNode; charCount = selection.focusOffset; while (node) { if (node === this.refContent) break; if (node.previousSibling) { node = node.previousSibling; if (countBR) if (node.nodeName === 'BR') linebreakCount++; charCount += node.textContent.length; } else { node = node.parentNode; if (node === null) break; } } } return charCount + linebreakCount; } }, { key: "setCursorPosition", value: function setCursorPosition(nextPosition) { var _this4 = this; if ([false, null, undefined].indexOf(nextPosition) > -1) return; var createRange = function createRange(node, chars, range) { if (!range) { range = document.createRange(); range.selectNode(node); range.setStart(node, 0); } if (chars.count === 0) { range.setEnd(node, chars.count); } else if (node && chars.count > 0) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent.length < chars.count) chars.count -= node.textContent.length;else { range.setEnd(node, chars.count); chars.count = 0; } } else for (var lp = 0; lp < node.childNodes.length; lp++) { range = createRange(node.childNodes[lp], chars, range); if (chars.count === 0) break; } } return range; }; var setPosition = function setPosition(chars) { if (chars < 0) return; var selection = window.getSelection(), range = createRange(_this4.refContent, { count: chars }); if (!range) return; range.collapse(false); selection.removeAllRanges(); selection.addRange(range); }; if (nextPosition > 0) setPosition(nextPosition);else this.refContent.focus(); } }, { key: "update", value: function update() { var cursorOffset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var updateCursorPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var container = this.refContent, data = this.tokenize(container); if ('onChange' in this.props) this.props.onChange({ plainText: data.indented, markupText: data.markup, json: data.json, jsObject: data.jsObject, lines: data.lines, error: data.error }); var cursorPosition = this.getCursorPosition(data.error) + cursorOffset; this.setState({ plainText: data.indented, markupText: data.markup, json: data.json, jsObject: data.jsObject, lines: data.lines, error: data.error }); this.updateTime = false; if (updateCursorPosition) this.setCursorPosition(cursorPosition); } }, { key: "scheduledUpdate", value: function scheduledUpdate() { if ('onKeyPressUpdate' in this.props) if (this.props.onKeyPressUpdate === false) return; var updateTime = this.updateTime; if (updateTime === false) return; if (updateTime > new Date().getTime()) return; this.update(); } }, { key: "setUpdateTime", value: function setUpdateTime() { if ('onKeyPressUpdate' in this.props) if (this.props.onKeyPressUpdate === false) return; this.updateTime = new Date().getTime() + this.waitAfterKeyPress; } }, { key: "stopEvent", value: function stopEvent(event) { if (!event) return; event.preventDefault(); event.stopPropagation(); } }, { key: "onKeyPress", value: function onKeyPress(event) { var ctrlOrMetaIsPressed = event.ctrlKey || event.metaKey; if (this.props.viewOnly && !ctrlOrMetaIsPressed) this.stopEvent(event); if (!ctrlOrMetaIsPressed) this.setUpdateTime(); } }, { key: "onKeyDown", value: function onKeyDown(event) { var viewOnly = !!this.props.viewOnly; var ctrlOrMetaIsPressed = event.ctrlKey || event.metaKey; switch (event.key) { case 'Tab': this.stopEvent(event); if (viewOnly) break; document.execCommand("insertText", false, " "); this.setUpdateTime(); break; case 'Backspace': case 'Delete': if (viewOnly) this.stopEvent(event); this.setUpdateTime(); break; case 'ArrowLeft': case 'ArrowRight': case 'ArrowUp': case 'ArrowDown': this.setUpdateTime(); break; case 'a': case 'c': if (viewOnly && !ctrlOrMetaIsPressed) this.stopEvent(event); break; default: if (viewOnly) this.stopEvent(event); break; } } }, { key: "onPaste", value: function onPaste(event) { if (this.props.viewOnly) { this.stopEvent(event); } else { event.preventDefault(); var text = event.clipboardData.getData('text/plain').replace(/(\r\n|\n|\r|\t|\u200B)/gm, ''); document.execCommand('insertText', false, text); } this.update(); } }, { key: "onClick", value: function onClick() { if ('viewOnly' in this.props) if (this.props.viewOnly) return; } }, { key: "onBlur", value: function onBlur() { if ('viewOnly' in this.props) if (this.props.viewOnly) return; var container = this.refContent, data = this.tokenize(container); if ('onBlur' in this.props) this.props.onBlur({ plainText: data.indented, markupText: data.markup, json: data.json, jsObject: data.jsObject, lines: data.lines, error: data.error }); } }, { key: "onScroll", value: function onScroll(event) { this.refLabels.scrollTop = event.target.scrollTop; } }, { key: "componentDidUpdate", value: function componentDidUpdate() { this.updateInternalProps(); this.showPlaceholder(); } }, { key: "componentDidMount", value: function componentDidMount() { this.showPlaceholder(); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { if (this.timer) clearInterval(this.timer); } }, { key: "showPlaceholder", value: function showPlaceholder() { var placeholderDoesNotExist = !('placeholder' in this.props); if (placeholderDoesNotExist) return; var placeholder = this.props.placeholder; var placeholderHasEmptyValues = [undefined, null].indexOf(placeholder) > -1; if (placeholderHasEmptyValues) return; var _this$state = this.state, prevPlaceholder = _this$state.prevPlaceholder, jsObject = _this$state.jsObject; var resetConfiguration = this.resetConfiguration; var placeholderDataType = (0, _mitsuketa.getType)(placeholder); var unexpectedDataType = ['object', 'array'].indexOf(placeholderDataType) === -1; if (unexpectedDataType) _err.default.throwError('showPlaceholder', 'placeholder', 'either an object or an array'); var samePlaceholderValues = (0, _mitsuketa.identical)(placeholder, prevPlaceholder); // Component will always re-render when new placeholder value is any different from previous placeholder value. var componentShouldUpdate = !samePlaceholderValues; if (!componentShouldUpdate) { if (resetConfiguration) { /** * If 'reset' property is set true or is truthy, * any difference between placeholder and current value * should trigger component re-render */ if (jsObject !== undefined) componentShouldUpdate = !(0, _mitsuketa.identical)(placeholder, jsObject); } } if (!componentShouldUpdate) return; var data = this.tokenize(placeholder); this.setState({ prevPlaceholder: placeholder, plainText: data.indentation, markupText: data.markup, lines: data.lines, error: data.error }); } }, { key: "tokenize", value: function tokenize(something) { if ((0, _typeof2.default)(something) !== 'object') return console.error('tokenize() expects object type properties only. Got \'' + (0, _typeof2.default)(something) + '\' type instead.'); var locale = this.props.locale || _en.default; var newSpan = this.newSpan; /** * DOM NODE || ONBLUR OR UPDATE */ if ('nodeType' in something) { var quarkize = function quarkize(text) { var prefix = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; var buffer = { active: false, string: '', number: '', symbol: '', space: '', delimiter: '', quarks: [] }; function pushAndStore(char, type) { switch (type) { case 'symbol': case 'delimiter': if (buffer.active) buffer.quarks.push({ string: buffer[buffer.active], type: prefix + '-' + buffer.active }); buffer[buffer.active] = ''; buffer.active = type; buffer[buffer.active] = char; break; default: if (type !== buffer.active || [buffer.string, char].indexOf('\n') > -1) { if (buffer.active) buffer.quarks.push({ string: buffer[buffer.active], type: prefix + '-' + buffer.active }); buffer[buffer.active] = ''; buffer.active = type; buffer[buffer.active] = char; } else buffer[type] += char; break; } } function finalPush() { if (buffer.active) { buffer.quarks.push({ string: buffer[buffer.active], type: prefix + '-' + buffer.active }); buffer[buffer.active] = ''; buffer.active = false; } } for (var i = 0; i < text.length; i++) { var char = text.charAt(i); switch (char) { case '"': case "'": pushAndStore(char, 'delimiter'); break; case ' ': case "\xA0": pushAndStore(char, 'space'); break; case '{': case '}': case '[': case ']': case ':': case ',': pushAndStore(char, 'symbol'); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (buffer.active === 'string') pushAndStore(char, 'string');else pushAndStore(char, 'number'); break; case '-': if (i < text.length - 1) if ('0123456789'.indexOf(text.charAt(i + 1)) > -1) { pushAndStore(char, 'number'); break; } case '.': if (i < text.length - 1 && i > 0) if ('0123456789'.indexOf(text.charAt(i + 1)) > -1 && '0123456789'.indexOf(text.charAt(i - 1)) > -1) { pushAndStore(char, 'number'); break; } default: pushAndStore(char, 'string'); break; } } finalPush(); return buffer.quarks; }; var validToken = function validToken(string, type) { var quotes = '\'"'; var firstChar = '', lastChar = '', quoteType = false; switch (type) { case 'primitive': if (['true', 'false', 'null', 'undefined'].indexOf(string) === -1) return false; break; case 'string': if (string.length < 2) return false; firstChar = string.charAt(0), lastChar = string.charAt(string.length - 1), quoteType = quotes.indexOf(firstChar); if (quoteType === -1) return false; if (firstChar !== lastChar) return false; for (var i = 0; i < string.length; i++) { if (i > 0 && i < string.length - 1) if (string.charAt(i) === quotes[quoteType]) if (string.charAt(i - 1) !== '\\') return false; } break; case 'key': if (string.length === 0) return false; firstChar = string.charAt(0), lastChar = string.charAt(string.length - 1), quoteType = quotes.indexOf(firstChar); if (quoteType > -1) { if (string.length === 1) return false; if (firstChar !== lastChar) return false; for (var i = 0; i < string.length; i++) { if (i > 0 && i < string.length - 1) if (string.charAt(i) === quotes[quoteType]) if (string.charAt(i - 1) !== '\\') return false; } } else { var nonAlphanumeric = '\'"`.,:;{}[]&<>=~*%\\|/-+!?@^ \xa0'; for (var i = 0; i < nonAlphanumeric.length; i++) { var nonAlpha = nonAlphanumeric.charAt(i); if (string.indexOf(nonAlpha) > -1) return false; } } break; case 'number': for (var i = 0; i < string.length; i++) { if ('0123456789'.indexOf(string.charAt(i)) === -1) if (i === 0) { if ('-' !== string.charAt(0)) return false; } else if ('.' !== string.charAt(i)) return false; } break; case 'symbol': if (string.length > 1) return false; if ('{[:]},'.indexOf(string) === -1) return false; break; case 'colon': if (string.length > 1) return false; if (':' !== string) return false; break; default: return true; break; } return true; }; var tokenFollowed = function tokenFollowed() { var last = buffer.tokens_normalize.length - 1; if (last < 1) return false; for (var i = last; i >= 0; i--) { var previousToken = buffer.tokens_normalize[i]; switch (previousToken.type) { case 'space': case 'linebreak': break; default: return previousToken; break; } } return false; }; var setError = function setError(tokenID, reason) { var offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; error = { token: tokenID, line: line, reason: reason }; buffer.tokens_merge[tokenID + offset].type = 'error'; }; var followedBySymbol = function followedBySymbol(tokenID, options) { if (tokenID === undefined) console.error('tokenID argument must be an integer.'); if (options === undefined) console.error('options argument must be an array.'); if (tokenID === buffer.tokens_merge.length - 1) return false; for (var i = tokenID + 1; i < buffer.tokens_merge.length; i++) { var _nextToken = buffer.tokens_merge[i]; switch (_nextToken.type) { case 'space': case 'linebreak': break; case 'symbol': case 'colon': if (options.indexOf(_nextToken.string) > -1) return i;else return false; break; default: return false; break; } } return false; }; var followsSymbol = function followsSymbol(tokenID, options) { if (tokenID === undefined) console.error('tokenID argument must be an integer.'); if (options === undefined) console.error('options argument must be an array.'); if (tokenID === 0) return false; for (var i = tokenID - 1; i >= 0; i--) { var _previousToken = buffer.tokens_merge[i]; switch (_previousToken.type) { case 'space': case 'linebreak': break; case 'symbol': case 'colon': if (options.indexOf(_previousToken.string) > -1) return true; return false; break; default: return false; break; } } return false; }; var typeFollowed = function typeFollowed(tokenID) { if (tokenID === undefined) console.error('tokenID argument must be an integer.'); if (tokenID === 0) return false; for (var i = tokenID - 1; i >= 0; i--) { var _previousToken2 = buffer.tokens_merge[i]; switch (_previousToken2.type) { case 'space': case 'linebreak': break; default: return _previousToken2.type; break; } } return false; }; var newIndent = function newIndent() { var space = []; for (var i = 0; i < _depth * 2; i++) { space.push('&nbsp;'); } return space.join(''); }; var newLineBreak = function newLineBreak() { var byPass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; _line++; if (_depth > 0 || byPass) { return '<br>'; } return ''; }; var newLineBreakAndIndent = function newLineBreakAndIndent() { var byPass = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; return newLineBreak(byPass) + newIndent(); }; var containerNode = something.cloneNode(true), hasChildren = containerNode.hasChildNodes(); if (!hasChildren) return ''; var children = containerNode.childNodes; var buffer = { tokens_unknown: [], tokens_proto: [], tokens_split: [], tokens_fallback: [], tokens_normalize: [], tokens_merge: [], tokens_plainText: '', indented: '', json: '', jsObject: undefined, markup: '' }; for (var i = 0; i < children.length; i++) { var child = children[i]; var info = {}; switch (child.nodeName) { case 'SPAN': info = { string: child.textContent, type: child.attributes.type.textContent }; buffer.tokens_unknown.push(info); break; case 'DIV': buffer.tokens_unknown.push({ string: child.textContent, type: 'unknown' }); break; case 'BR': if (child.textContent === '') buffer.tokens_unknown.push({ string: '\n', type: 'unknown' }); break; case '#text': buffer.tokens_unknown.push({ string: child.wholeText, type: 'unknown' }); break; case 'FONT': buffer.tokens_unknown.push({ string: child.textContent, type: 'unknown' }); break; default: console.error('Unrecognized node:', { child: child }); break; } } for (var i = 0; i < buffer.tokens_unknown.length; i++) { var token = buffer.tokens_unknown[i]; buffer.tokens_proto = buffer.tokens_proto.concat(quarkize(token.string, 'proto')); } for (var i = 0; i < buffer.tokens_proto.length; i++) { var _token = buffer.tokens_proto[i]; if (_token.type.indexOf('proto') === -1) { if (!validToken(_token.string, _token.type)) { buffer.tokens_split = buffer.tokens_split.concat(quarkize(_token.string, 'split')); } else buffer.tokens_split.push(_token); } else buffer.tokens_split.push(_token); } for (var i = 0; i < buffer.tokens_split.length; i++) { var _token2 = buffer.tokens_split[i]; var type = _token2.type, string = _token2.string, length = string.length, fallback = []; if (type.indexOf('-') > -1) { type = type.slice(type.indexOf('-') + 1); if (type !== 'string') fallback.push('string'); fallback.push('key'); fallback.push('error'); } var tokul = { string: string, length: length, type: type, fallback: fallback }; buffer.tokens_fallback.push(tokul); } var buffer2 = { brackets: [], stringOpen: false, isValue: false }; for (var i = 0; i < buffer.tokens_fallback.length; i++) { var _token3 = buffer.tokens_fallback[i]; var _type2 = _token3.type, _string2 = _token3.string; var normalToken = { type: _type2, string: _string2 }; switch (_type2) { case 'symbol': case 'colon': if (buffer2.stringOpen) { if (buffer2.isValue) normalToken.type = 'string';else normalToken.type = 'key'; break; } switch (_string2) { case '[': case '{': buffer2.brackets.push(_string2); buffer2.isValue = buffer2.brackets[buffer2.brackets.length - 1] === '['; break; case ']': case '}': buffer2.brackets.pop(); buffer2.isValue = buffer2.brackets[buffer2.brackets.length - 1] === '['; break; case ',': if (tokenFollowed().type === 'colon') break; buffer2.isValue = buffer2.brackets[buffer2.brackets.length - 1] === '['; break; case ':': normalToken.type = 'colon'; buffer2.isValue = true; break; } break; case 'delimiter': if (buffer2.isValue) normalToken.type = 'string';else normalToken.type = 'key'; if (!buffer2.stringOpen) { buffer2.stringOpen = _string2; break; } if (i > 0) { var previousToken = buffer.tokens_fallback[i - 1], _string = previousToken.string, _type = previousToken.type, _char = _string.charAt(_string.length - 1); if (_type === 'string' && _char === '\\') break; } if (buffer2.stringOpen === _string2) { buffer2.stringOpen = false; break; } break; case 'primitive': case 'string': if (['false', 'true', 'null', 'undefined'].indexOf(_string2) > -1) { var lastIndex = buffer.tokens_normalize.length - 1; if (lastIndex >= 0) { if (buffer.tokens_normalize[lastIndex].type !== 'string') { normalToken.type = 'primitive'; break; } normalToken.type = 'string'; break; } normalToken.type = 'primitive'; break; } if (_string2 === '\n') if (!buffer2.stringOpen) { normalToken.type = 'linebreak'; break; } if (buffer2.isValue) normalToken.type = 'string';else normalToken.type = 'key'; break; case 'space': if (buffer2.stringOpen) if (buffer2.isValue) normalToken.type = 'string';else normalToken.type = 'key'; break; case 'number': if (buffer2.stringOpen) if (buffer2.isValue) normalToken.type = 'string';else normalToken.type = 'key'; break; default: break; } buffer.tokens_normalize.push(normalToken); } for (var i = 0; i < buffer.tokens_normalize.length; i++) { var _token4 = buffer.tokens_normalize[i]; var mergedToken = { string: _token4.string, type: _token4.type, tokens: [i] }; if (['symbol', 'colon'].indexOf(_token4.type) === -1) if (i + 1 < buffer.tokens_normalize.length) { var count = 0; for (var u = i + 1; u < buffer.tokens_normalize.length; u++) { var nextToken = buffer.tokens_normalize[u]; if (_token4.type !== nextToken.type) break; mergedToken.string += nextToken.string; mergedToken.tokens.push(u); count++; } i += count; } buffer.tokens_merge.push(mergedToken); } var quotes = '\'"', alphanumeric = 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + '0123456789' + '_$'; var error = false, line = buffer.tokens_merge.length > 0 ? 1 : 0; buffer2 = { brackets: [], stringOpen: false, isValue: false }; var bracketList = []; for (var i = 0; i < buffer.tokens_merge.length; i++) { if (error) break; var _token5 = buffer.tokens_merge[i], _string3 = _token5.string, _type3 = _token5.type, found = false; switch (_type3) { case 'space': break; case 'linebreak': line++; break; case 'symbol': switch (_string3) { case '{': case '[': found = followsSymbol(i, ['}', ']']); if (found) { setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.prohibited, { firstToken: buffer.tokens_merge[found].string, secondToken: _string3 })); break; } if (_string3 === '[' && i > 0) if (!followsSymbol(i, [':', '[', ','])) { setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.permitted, { firstToken: "[", secondToken: [":", "[", ","] })); break; } if (_string3 === '{') if (followsSymbol(i, ['{'])) { setError(i, (0, _locale.format)(locale.invalidToken.double, { token: "{" })); break; } buffer2.brackets.push(_string3); buffer2.isValue = buffer2.brackets[buffer2.brackets.length - 1] === '['; bracketList.push({ i: i, line: line, string: _string3 }); break; case '}': case ']': if (_string3 === '}') if (buffer2.brackets[buffer2.brackets.length - 1] !== '{') { setError(i, (0, _locale.format)(locale.brace.curly.missingOpen)); break; } if (_string3 === '}') if (followsSymbol(i, [','])) { setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.prohibited, { firstToken: ",", secondToken: "}" })); break; } if (_string3 === ']') if (buffer2.brackets[buffer2.brackets.length - 1] !== '[') { setError(i, (0, _locale.format)(locale.brace.square.missingOpen)); break; } if (_string3 === ']') if (followsSymbol(i, [':'])) { setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.prohibited, { firstToken: ":", secondToken: "]" })); break; } buffer2.brackets.pop(); buffer2.isValue = buffer2.brackets[buffer2.brackets.length - 1] === '['; bracketList.push({ i: i, line: line, string: _string3 }); break; case ',': found = followsSymbol(i, ['{']); if (found) { if (followedBySymbol(i, ['}'])) { setError(i, (0, _locale.format)(locale.brace.curly.cannotWrap, { token: "," })); break; } setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.prohibited, { firstToken: "{", secondToken: "," })); break; } if (followedBySymbol(i, ['}', ',', ']'])) { setError(i, (0, _locale.format)(locale.noTrailingOrLeadingComma)); break; } found = typeFollowed(i); switch (found) { case 'key': case 'colon': setError(i, (0, _locale.format)(locale.invalidToken.termSequence.prohibited, { firstTerm: found === 'key' ? locale.types.key : locale.symbols.colon, secondTerm: locale.symbols.comma })); break; case 'symbol': if (followsSymbol(i, ['{'])) { setError(i, (0, _locale.format)(locale.invalidToken.tokenSequence.prohibited, { firstToken: "{"