diff --git a/public/jsoneditor.css b/public/jsoneditor.css index af7c835..ddfc136 100644 --- a/public/jsoneditor.css +++ b/public/jsoneditor.css @@ -1,6 +1,8 @@ .jsoneditor { border: 1px solid #3883fa; overflow: auto; + height: 100%; + min-height: 150px; } .jsoneditor-node { diff --git a/src/JSONNode.js b/src/JSONNode.js index b5d3552..6750b20 100644 --- a/src/JSONNode.js +++ b/src/JSONNode.js @@ -1,7 +1,6 @@ import { h, Component } from 'preact' import isObject from './utils/isObject' -import escapeHTML from './utils/escapeHTML' -import unescapeHTML from './utils/unescapeHTML' +import { escapeHTML, unescapeHTML } from './utils/escape' import getInnerText from './utils/getInnerText' import stringConvert from './utils/stringConvert' import valueType, {isUrl} from './utils/valueType' @@ -83,7 +82,7 @@ export default class JSONNode extends Component { ? this.renderReadonly(index) : this.renderField(field, parent, this.onChangeField), this.renderSeparator(), - this.renderValue(value, this.onChangeValue) + this.renderValue(value, this.onChangeValue, this.onClickUrl, this.onKeyDownUrl) ]) ]) } @@ -99,7 +98,7 @@ export default class JSONNode extends Component { return h('div', { class: 'jsoneditor-field' + (hasParent ? '' : ' jsoneditor-readonly'), contentEditable: hasParent, - spellCheck: "false", // FIXME: turning off spellcheck doesn't work + spellCheck: 'false', onBlur: onChangeField }, content) } @@ -108,7 +107,7 @@ export default class JSONNode extends Component { return h('div', {class: 'jsoneditor-separator'}, ':') } - renderValue (value, onChangeValue) { + renderValue (value, onChangeValue, onClickUrl, onKeyDownUrl) { const type = valueType (value) const _isUrl = isUrl(value) const valueClass = 'jsoneditor-value jsoneditor-' + type + (_isUrl ? ' jsoneditor-url' : '') @@ -116,10 +115,10 @@ export default class JSONNode extends Component { return h('div', { class: valueClass, contentEditable: true, - spellCheck: "false", // FIXME: turning off spellcheck doesn't work + spellCheck: 'false', // FIXME: turning off spellcheck doesn't work onInput: onChangeValue, - onClick: _isUrl ? this.onClickUrl : null, - onKeyDown: _isUrl ? this.onKeyDownUrl: null, + onClick: _isUrl ? onClickUrl : null, + onKeyDown: _isUrl ? onKeyDownUrl: null, title: _isUrl ? 'Ctrl+Click or ctrl+Enter to open url' : null }, escapeHTML(value)) } diff --git a/src/utils/escape.js b/src/utils/escape.js new file mode 100644 index 0000000..cedaeaf --- /dev/null +++ b/src/utils/escape.js @@ -0,0 +1,93 @@ +import parseJSON from './parseJSON' + +/** + * escape a text, such that it can be displayed safely in an HTML element + * @param {String} text + * @param {boolean} [escapeUnicode=false] + * @return {String} escapedText + */ +export function escapeHTML (text, escapeUnicode = false) { + if (typeof text !== 'string') { + return String(text) + } + else { + var htmlEscaped = String(text) + .replace(/ /g, ' \u00a0') // replace double space with an nbsp and space + .replace(/^ /, '\u00a0') // space at start + .replace(/ $/, '\u00a0') // space at end + + var json = JSON.stringify(htmlEscaped) + var html = json.substring(1, json.length - 1) + if (escapeUnicode === true) { + html = escapeUnicodeChars(html) + } + return html + } +} + +/** + * Escape unicode characters. + * For example input '\u2661' (length 1) will output '\\u2661' (length 5). + * @param {string} text + * @return {string} + */ +function escapeUnicodeChars (text) { + // see https://www.wikiwand.com/en/UTF-16 + // note: we leave surrogate pairs as two individual chars, + // as JSON doesn't interpret them as a single unicode char. + return text.replace(/[\u007F-\uFFFF]/g, function(c) { + return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4) + }) +} + +/** + * unescape a string. + * @param {String} escapedText + * @return {String} text + */ +export function unescapeHTML (escapedText) { + var json = '"' + escapeJSON(escapedText) + '"' + var htmlEscaped = parseJSON(json) + + return htmlEscaped.replace(/\u00A0/g, ' ') // nbsp character +} + +/** + * escape a text to make it a valid JSON string. The method will: + * - replace unescaped double quotes with '\"' + * - replace unescaped backslash with '\\' + * - replace returns with '\n' + * @param {String} text + * @return {String} escapedText + * @private + */ +export function escapeJSON (text) { + // TODO: replace with some smart regex (only when a new solution is faster!) + var escaped = '' + var i = 0 + while (i < text.length) { + var c = text.charAt(i) + if (c == '\n') { + escaped += '\\n' + } + else if (c == '\\') { + escaped += c + i++ + + c = text.charAt(i) + if (c === '' || '"\\/bfnrtu'.indexOf(c) == -1) { + escaped += '\\' // no valid escape character + } + escaped += c + } + else if (c == '"') { + escaped += '\\"' + } + else { + escaped += c + } + i++ + } + + return escaped +} \ No newline at end of file diff --git a/src/utils/escapeHTML.js b/src/utils/escapeHTML.js deleted file mode 100644 index 0ce7dec..0000000 --- a/src/utils/escapeHTML.js +++ /dev/null @@ -1,43 +0,0 @@ -/** - * escape a text, such that it can be displayed safely in an HTML element - * @param {String} text - * @param {boolean} [escapeUnicode=false] - * @return {String} escapedText - */ -export default function escapeHTML (text, escapeUnicode = false) { - if (typeof text !== 'string') { - return String(text) - } - else { - var htmlEscaped = String(text) - // TODO: cleanup redundant character replacements - // .replace(/&/g, '&') // must be replaced first! - // .replace(//g, '>') - .replace(/ /g, ' \u00a0') // replace double space with an nbsp and space - .replace(/^ /, '\u00a0') // space at start - .replace(/ $/, '\u00a0') // space at end - - var json = JSON.stringify(htmlEscaped) - var html = json.substring(1, json.length - 1) - if (escapeUnicode === true) { - html = escapeUnicodeChars(html) - } - return html - } -} - -/** - * Escape unicode characters. - * For example input '\u2661' (length 1) will output '\\u2661' (length 5). - * @param {string} text - * @return {string} - */ -function escapeUnicodeChars (text) { - // see https://www.wikiwand.com/en/UTF-16 - // note: we leave surrogate pairs as two individual chars, - // as JSON doesn't interpret them as a single unicode char. - return text.replace(/[\u007F-\uFFFF]/g, function(c) { - return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4) - }) -} \ No newline at end of file diff --git a/src/utils/unescapeHTML.js b/src/utils/unescapeHTML.js deleted file mode 100644 index 2b3a2b7..0000000 --- a/src/utils/unescapeHTML.js +++ /dev/null @@ -1,56 +0,0 @@ -import parseJSON from './parseJSON' -/** - * unescape a string. - * @param {String} escapedText - * @return {String} text - */ -export default function unescapeHTML (escapedText) { - var json = '"' + escapeJSON(escapedText) + '"' - var htmlEscaped = parseJSON(json) - - return htmlEscaped - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/ |\u00A0/g, ' ') - .replace(/&/g, '&') // must be replaced last -} - -/** - * escape a text to make it a valid JSON string. The method will: - * - replace unescaped double quotes with '\"' - * - replace unescaped backslash with '\\' - * - replace returns with '\n' - * @param {String} text - * @return {String} escapedText - * @private - */ -export function escapeJSON (text) { - // TODO: replace with some smart regex (only when a new solution is faster!) - var escaped = '' - var i = 0 - while (i < text.length) { - var c = text.charAt(i) - if (c == '\n') { - escaped += '\\n' - } - else if (c == '\\') { - escaped += c - i++ - - c = text.charAt(i) - if (c === '' || '"\\/bfnrtu'.indexOf(c) == -1) { - escaped += '\\' // no valid escape character - } - escaped += c - } - else if (c == '"') { - escaped += '\\"' - } - else { - escaped += c - } - i++ - } - - return escaped -} \ No newline at end of file diff --git a/test/util.test.js b/test/util.test.js index 8ae25c2..85e743d 100644 --- a/test/util.test.js +++ b/test/util.test.js @@ -1,5 +1,5 @@ var assert = require('assert'); -var util = require('../src/js/util'); +var util = require('./js/util'); // console.log('TEST', util.parsePath('.items[3].name')); // console.log('TEST', util.parsePath('.items[*].name'));