Refactored `escape` utils

This commit is contained in:
jos 2016-07-12 21:20:11 +02:00
parent f011e3f107
commit 1c3a323387
6 changed files with 103 additions and 108 deletions

View File

@ -1,6 +1,8 @@
.jsoneditor {
border: 1px solid #3883fa;
overflow: auto;
height: 100%;
min-height: 150px;
}
.jsoneditor-node {

View File

@ -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))
}

93
src/utils/escape.js Normal file
View File

@ -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
}

View File

@ -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, '&amp;') // must be replaced first!
// .replace(/</g, '&lt;')
// .replace(/>/g, '&gt;')
.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)
})
}

View File

@ -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(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&nbsp;|\u00A0/g, ' ')
.replace(/&amp;/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
}

View File

@ -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'));