Linting fixes
This commit is contained in:
parent
d3c3d47ec0
commit
023c57f492
|
@ -26,6 +26,6 @@ export const SIMPLE_MODAL_OPTIONS = {
|
||||||
},
|
},
|
||||||
styleContent: {
|
styleContent: {
|
||||||
padding: '0px',
|
padding: '0px',
|
||||||
overflow: 'visible', // needed for select box dropdowns which are larger than the modal
|
overflow: 'visible' // needed for select box dropdowns which are larger than the modal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ export function patchProps (state, operations) {
|
||||||
return updatedState
|
return updatedState
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNextKeys(props, key, includeKey = false) {
|
export function getNextKeys (props, key, includeKey = false) {
|
||||||
if (props) {
|
if (props) {
|
||||||
const index = props.findIndex(prop => prop.key === key)
|
const index = props.findIndex(prop => prop.key === key)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
|
|
|
@ -10,8 +10,8 @@ import { syncState, updateProps } from './documentState.js'
|
||||||
describe('documentState', () => {
|
describe('documentState', () => {
|
||||||
it('syncState', () => {
|
it('syncState', () => {
|
||||||
const document = {
|
const document = {
|
||||||
array: [1, 2, {c: 6}],
|
array: [1, 2, { c: 6 }],
|
||||||
object: {a: 4, b: 5},
|
object: { a: 4, b: 5 },
|
||||||
value: 'hello'
|
value: 'hello'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,10 +23,10 @@ describe('documentState', () => {
|
||||||
|
|
||||||
const expectedState = {}
|
const expectedState = {}
|
||||||
expectedState[STATE_EXPANDED] = true
|
expectedState[STATE_EXPANDED] = true
|
||||||
expectedState[STATE_PROPS] = [
|
expectedState[STATE_PROPS] = [
|
||||||
{ 'id': state[STATE_PROPS][0].id, 'key': 'array' },
|
{ id: state[STATE_PROPS][0].id, key: 'array' },
|
||||||
{ 'id': state[STATE_PROPS][1].id, 'key': 'object' },
|
{ id: state[STATE_PROPS][1].id, key: 'object' },
|
||||||
{ 'id': state[STATE_PROPS][2].id, 'key': 'value' }
|
{ id: state[STATE_PROPS][2].id, key: 'value' }
|
||||||
]
|
]
|
||||||
expectedState.array = []
|
expectedState.array = []
|
||||||
expectedState.array[STATE_EXPANDED] = true
|
expectedState.array[STATE_EXPANDED] = true
|
||||||
|
@ -34,30 +34,30 @@ describe('documentState', () => {
|
||||||
expectedState.array[2] = {}
|
expectedState.array[2] = {}
|
||||||
expectedState.array[2][STATE_EXPANDED] = false
|
expectedState.array[2][STATE_EXPANDED] = false
|
||||||
expectedState.array[2][STATE_PROPS] = [
|
expectedState.array[2][STATE_PROPS] = [
|
||||||
{ 'id': state.array[2][STATE_PROPS][0].id, 'key': 'c' } // FIXME: props should not be created because node is not expanded
|
{ id: state.array[2][STATE_PROPS][0].id, key: 'c' } // FIXME: props should not be created because node is not expanded
|
||||||
]
|
]
|
||||||
expectedState.object = {}
|
expectedState.object = {}
|
||||||
expectedState.object[STATE_EXPANDED] = true
|
expectedState.object[STATE_EXPANDED] = true
|
||||||
expectedState.object[STATE_PROPS] = [
|
expectedState.object[STATE_PROPS] = [
|
||||||
{ 'id': state.object[STATE_PROPS][0].id, 'key': 'a' },
|
{ id: state.object[STATE_PROPS][0].id, key: 'a' },
|
||||||
{ 'id': state.object[STATE_PROPS][1].id, 'key': 'b' }
|
{ id: state.object[STATE_PROPS][1].id, key: 'b' }
|
||||||
]
|
]
|
||||||
|
|
||||||
assert.deepStrictEqual(state, expectedState)
|
assert.deepStrictEqual(state, expectedState)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updateProps (1)', () => {
|
it('updateProps (1)', () => {
|
||||||
const props1 = updateProps({b: 2})
|
const props1 = updateProps({ b: 2 })
|
||||||
assert.deepStrictEqual(props1.map(item => item.key), ['b'])
|
assert.deepStrictEqual(props1.map(item => item.key), ['b'])
|
||||||
|
|
||||||
const props2 = updateProps({a: 1, b: 2}, props1)
|
const props2 = updateProps({ a: 1, b: 2 }, props1)
|
||||||
assert.deepStrictEqual(props2.map(item => item.key), ['b', 'a'])
|
assert.deepStrictEqual(props2.map(item => item.key), ['b', 'a'])
|
||||||
assert.deepStrictEqual(props2[0], props1[0]) // b must still have the same id
|
assert.deepStrictEqual(props2[0], props1[0]) // b must still have the same id
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updateProps (2)', () => {
|
it('updateProps (2)', () => {
|
||||||
const props1 = updateProps({a: 1, b: 2})
|
const props1 = updateProps({ a: 1, b: 2 })
|
||||||
const props2 = updateProps({a: 1, b: 2}, props1)
|
const props2 = updateProps({ a: 1, b: 2 }, props1)
|
||||||
assert.deepStrictEqual(props2, props1)
|
assert.deepStrictEqual(props2, props1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -113,4 +113,4 @@ export function createHistory (options = {}) {
|
||||||
undo,
|
undo,
|
||||||
redo
|
redo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { isObjectOrArray } from '../utils/typeUtils.js'
|
||||||
* key will maintain it's position above these keys
|
* key will maintain it's position above these keys
|
||||||
* @return {JSONPatchDocument}
|
* @return {JSONPatchDocument}
|
||||||
*/
|
*/
|
||||||
export function insertBefore (json, path, values, nextKeys) { // TODO: find a better name and define datastructure for values
|
export function insertBefore (json, path, values, nextKeys) { // TODO: find a better name and define datastructure for values
|
||||||
const parentPath = initial(path)
|
const parentPath = initial(path)
|
||||||
const parent = getIn(json, parentPath)
|
const parent = getIn(json, parentPath)
|
||||||
|
|
||||||
|
@ -33,8 +33,7 @@ export function insertBefore (json, path, values, nextKeys) { // TODO: find a b
|
||||||
path: compileJSONPointer(parentPath.concat(offset + index)),
|
path: compileJSONPointer(parentPath.concat(offset + index)),
|
||||||
value: entry.value
|
value: entry.value
|
||||||
}))
|
}))
|
||||||
}
|
} else { // 'object'
|
||||||
else { // 'object'
|
|
||||||
return [
|
return [
|
||||||
// insert new values
|
// insert new values
|
||||||
...values.map(entry => {
|
...values.map(entry => {
|
||||||
|
@ -65,7 +64,7 @@ export function insertBefore (json, path, values, nextKeys) { // TODO: find a b
|
||||||
* @param {Array.<{key?: string, value: JSON}>} values
|
* @param {Array.<{key?: string, value: JSON}>} values
|
||||||
* @return {JSONPatchDocument}
|
* @return {JSONPatchDocument}
|
||||||
*/
|
*/
|
||||||
export function append (json, path, values) { // TODO: find a better name and define datastructure for values
|
export function append (json, path, values) { // TODO: find a better name and define datastructure for values
|
||||||
const parent = getIn(json, path)
|
const parent = getIn(json, path)
|
||||||
|
|
||||||
if (Array.isArray(parent)) {
|
if (Array.isArray(parent)) {
|
||||||
|
@ -75,8 +74,7 @@ export function append (json, path, values) { // TODO: find a better name and d
|
||||||
path: compileJSONPointer(path.concat(offset + index)),
|
path: compileJSONPointer(path.concat(offset + index)),
|
||||||
value: entry.value
|
value: entry.value
|
||||||
}))
|
}))
|
||||||
}
|
} else { // 'object'
|
||||||
else { // 'object'
|
|
||||||
return values.map(entry => {
|
return values.map(entry => {
|
||||||
const newProp = findUniqueName(entry.key, parent)
|
const newProp = findUniqueName(entry.key, parent)
|
||||||
return {
|
return {
|
||||||
|
@ -100,7 +98,7 @@ export function append (json, path, values) { // TODO: find a better name and d
|
||||||
* key will maintain it's position above these keys
|
* key will maintain it's position above these keys
|
||||||
* @returns {JSONPatchDocument}
|
* @returns {JSONPatchDocument}
|
||||||
*/
|
*/
|
||||||
export function rename(parentPath, oldKey, newKey, nextKeys) {
|
export function rename (parentPath, oldKey, newKey, nextKeys) {
|
||||||
return [
|
return [
|
||||||
// rename a key
|
// rename a key
|
||||||
{
|
{
|
||||||
|
@ -129,7 +127,7 @@ export function rename(parentPath, oldKey, newKey, nextKeys) {
|
||||||
* key will maintain it's position above these keys
|
* key will maintain it's position above these keys
|
||||||
* @return {JSONPatchDocument}
|
* @return {JSONPatchDocument}
|
||||||
*/
|
*/
|
||||||
export function replace (json, paths, values, nextKeys) { // TODO: find a better name and define datastructure for values
|
export function replace (json, paths, values, nextKeys) { // TODO: find a better name and define datastructure for values
|
||||||
const firstPath = first(paths)
|
const firstPath = first(paths)
|
||||||
const parentPath = initial(firstPath)
|
const parentPath = initial(firstPath)
|
||||||
const parent = getIn(json, parentPath)
|
const parent = getIn(json, parentPath)
|
||||||
|
@ -149,8 +147,7 @@ export function replace (json, paths, values, nextKeys) { // TODO: find a bette
|
||||||
value: entry.value
|
value: entry.value
|
||||||
}))
|
}))
|
||||||
]
|
]
|
||||||
}
|
} else { // parent is Object
|
||||||
else { // parent is Object
|
|
||||||
// if we're going to replace an existing object with key "a" with a new
|
// if we're going to replace an existing object with key "a" with a new
|
||||||
// key "a", we must not create a new unique name "a (copy)".
|
// key "a", we must not create a new unique name "a (copy)".
|
||||||
const removeKeys = new Set(paths.map(path => last(path)))
|
const removeKeys = new Set(paths.map(path => last(path)))
|
||||||
|
@ -191,13 +188,13 @@ export function replace (json, paths, values, nextKeys) { // TODO: find a bette
|
||||||
*/
|
*/
|
||||||
export function duplicate (doc, state, paths) {
|
export function duplicate (doc, state, paths) {
|
||||||
// FIXME: here we assume selection.paths is sorted correctly, that's a dangerous assumption
|
// FIXME: here we assume selection.paths is sorted correctly, that's a dangerous assumption
|
||||||
const lastPath = last(paths)
|
const lastPath = last(paths)
|
||||||
const parentPath = initial(lastPath)
|
const parentPath = initial(lastPath)
|
||||||
const beforeKey = last(lastPath)
|
const beforeKey = last(lastPath)
|
||||||
const props = getIn(state, parentPath.concat(STATE_PROPS))
|
const props = getIn(state, parentPath.concat(STATE_PROPS))
|
||||||
const nextKeys = getNextKeys(props, beforeKey, false)
|
const nextKeys = getNextKeys(props, beforeKey, false)
|
||||||
const parent = getIn(doc, parentPath)
|
const parent = getIn(doc, parentPath)
|
||||||
|
|
||||||
if (Array.isArray(parent)) {
|
if (Array.isArray(parent)) {
|
||||||
const lastPath = last(paths)
|
const lastPath = last(paths)
|
||||||
const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0
|
const offset = lastPath ? (parseInt(last(lastPath), 10) + 1) : 0
|
||||||
|
@ -243,7 +240,7 @@ export function insert (doc, state, selection, values) {
|
||||||
const nextKeys = getNextKeys(props, beforeKey, true)
|
const nextKeys = getNextKeys(props, beforeKey, true)
|
||||||
const operations = insertBefore(doc, selection.beforePath, values, nextKeys)
|
const operations = insertBefore(doc, selection.beforePath, values, nextKeys)
|
||||||
// TODO: move calculation of nextKeys inside insertBefore?
|
// TODO: move calculation of nextKeys inside insertBefore?
|
||||||
|
|
||||||
return operations
|
return operations
|
||||||
} else if (selection.appendPath) {
|
} else if (selection.appendPath) {
|
||||||
const operations = append(doc, selection.appendPath, values)
|
const operations = append(doc, selection.appendPath, values)
|
||||||
|
@ -257,23 +254,23 @@ export function insert (doc, state, selection, values) {
|
||||||
const nextKeys = getNextKeys(props, beforeKey, true)
|
const nextKeys = getNextKeys(props, beforeKey, true)
|
||||||
const operations = replace(doc, selection.paths, values, nextKeys)
|
const operations = replace(doc, selection.paths, values, nextKeys)
|
||||||
// TODO: move calculation of nextKeys inside replace?
|
// TODO: move calculation of nextKeys inside replace?
|
||||||
|
|
||||||
return operations
|
return operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createNewValue (doc, selection, type) {
|
export function createNewValue (doc, selection, type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'value':
|
case 'value':
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
case 'object':
|
case 'object':
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
case 'array':
|
case 'array':
|
||||||
return []
|
return []
|
||||||
|
|
||||||
case 'structure':
|
case 'structure':
|
||||||
const parentPath = getParentPath(selection)
|
const parentPath = getParentPath(selection)
|
||||||
const parent = getIn(doc, parentPath)
|
const parent = getIn(doc, parentPath)
|
||||||
|
|
||||||
|
@ -281,8 +278,8 @@ export function createNewValue (doc, selection, type) {
|
||||||
const jsonExample = first(parent)
|
const jsonExample = first(parent)
|
||||||
const structure = cloneDeepWith(jsonExample, (value) => {
|
const structure = cloneDeepWith(jsonExample, (value) => {
|
||||||
return isObjectOrArray(value)
|
return isObjectOrArray(value)
|
||||||
? undefined // leave as is
|
? undefined // leave as is
|
||||||
: ''
|
: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('structure', jsonExample, structure)
|
console.log('structure', jsonExample, structure)
|
||||||
|
@ -316,16 +313,16 @@ export function remove (path) {
|
||||||
*/
|
*/
|
||||||
export function removeAll (paths) {
|
export function removeAll (paths) {
|
||||||
return paths
|
return paths
|
||||||
.map(path => ({
|
.map(path => ({
|
||||||
op: 'remove',
|
op: 'remove',
|
||||||
path: compileJSONPointer(path)
|
path: compileJSONPointer(path)
|
||||||
}))
|
}))
|
||||||
.reverse() // reverse is needed for arrays: delete the last index first
|
.reverse() // reverse is needed for arrays: delete the last index first
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper function to move a key down in an object,
|
// helper function to move a key down in an object,
|
||||||
// so another key can get positioned before the moved down keys
|
// so another key can get positioned before the moved down keys
|
||||||
function moveDown(parentPath, key) {
|
function moveDown (parentPath, key) {
|
||||||
return {
|
return {
|
||||||
op: 'move',
|
op: 'move',
|
||||||
from: compileJSONPointer(parentPath.concat(key)),
|
from: compileJSONPointer(parentPath.concat(key)),
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { STATE_SEARCH_PROPERTY, STATE_SEARCH_VALUE } from '../constants.js'
|
||||||
import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
|
import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
|
||||||
import { valueType } from '../utils/typeUtils.js'
|
import { valueType } from '../utils/typeUtils.js'
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{path: Path, what: Symbol}} SearchItem
|
* @typedef {{path: Path, what: Symbol}} SearchItem
|
||||||
*/
|
*/
|
||||||
|
@ -101,7 +100,7 @@ export function searchPrevious (searchResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchRecursive (key, doc, searchText) {
|
function searchRecursive (key, doc, searchText) {
|
||||||
let results = undefined
|
let results
|
||||||
|
|
||||||
if (typeof key === 'string' && containsCaseInsensitive(key, searchText)) {
|
if (typeof key === 'string' && containsCaseInsensitive(key, searchText)) {
|
||||||
results = createOrAdd(results, STATE_SEARCH_PROPERTY, 'search')
|
results = createOrAdd(results, STATE_SEARCH_PROPERTY, 'search')
|
||||||
|
@ -110,14 +109,14 @@ function searchRecursive (key, doc, searchText) {
|
||||||
const type = valueType(doc)
|
const type = valueType(doc)
|
||||||
if (type === 'array') {
|
if (type === 'array') {
|
||||||
doc.forEach((item, index) => {
|
doc.forEach((item, index) => {
|
||||||
let childResults = searchRecursive(index, item, searchText)
|
const childResults = searchRecursive(index, item, searchText)
|
||||||
if (childResults) {
|
if (childResults) {
|
||||||
results = createOrAdd(results, index, childResults)
|
results = createOrAdd(results, index, childResults)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else if (type === 'object') {
|
} else if (type === 'object') {
|
||||||
Object.keys(doc).forEach(prop => {
|
Object.keys(doc).forEach(prop => {
|
||||||
let childResults = searchRecursive(prop, doc[prop], searchText)
|
const childResults = searchRecursive(prop, doc[prop], searchText)
|
||||||
if (childResults) {
|
if (childResults) {
|
||||||
results = createOrAdd(results, prop, childResults)
|
results = createOrAdd(results, prop, childResults)
|
||||||
}
|
}
|
||||||
|
@ -167,7 +166,7 @@ function flattenSearch (searchResult) {
|
||||||
return resultArray
|
return resultArray
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOrAdd(object, key, value) {
|
function createOrAdd (object, key, value) {
|
||||||
if (object) {
|
if (object) {
|
||||||
object[key] = value
|
object[key] = value
|
||||||
return object
|
return object
|
||||||
|
|
|
@ -3,14 +3,14 @@ import { VALIDATION_ERROR } from '../constants.js'
|
||||||
import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
|
import { existsIn, setIn } from '../utils/immutabilityHelpers.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a nested map with validation errors,
|
* Create a nested map with validation errors,
|
||||||
* and also create error messages for the parent nodes of the nodes having an error.
|
* and also create error messages for the parent nodes of the nodes having an error.
|
||||||
*
|
*
|
||||||
* @param {ValidationError[]} validationErrors
|
* @param {ValidationError[]} validationErrors
|
||||||
* @return {Object.<string, string> | undefined} Returns a nested object containing
|
* @return {Object.<string, string> | undefined} Returns a nested object containing
|
||||||
*/
|
*/
|
||||||
export function mapValidationErrors (validationErrors) {
|
export function mapValidationErrors (validationErrors) {
|
||||||
let object = undefined
|
let object
|
||||||
|
|
||||||
validationErrors.forEach(validationError => {
|
validationErrors.forEach(validationError => {
|
||||||
const errorPath = validationError.path.concat([VALIDATION_ERROR])
|
const errorPath = validationError.path.concat([VALIDATION_ERROR])
|
||||||
|
|
|
@ -30,7 +30,7 @@ describe('validation', () => {
|
||||||
expected[VALIDATION_ERROR] = { isChildError: true, path: [], message: message3 }
|
expected[VALIDATION_ERROR] = { isChildError: true, path: [], message: message3 }
|
||||||
expected.pupils[VALIDATION_ERROR] = { isChildError: true, path: ['pupils'], message: message3 }
|
expected.pupils[VALIDATION_ERROR] = { isChildError: true, path: ['pupils'], message: message3 }
|
||||||
expected.pupils[2][VALIDATION_ERROR] = { isChildError: true, path: ['pupils', 2], message: message3 }
|
expected.pupils[2][VALIDATION_ERROR] = { isChildError: true, path: ['pupils', 2], message: message3 }
|
||||||
|
|
||||||
const actual = mapValidationErrors(validationErrorsList)
|
const actual = mapValidationErrors(validationErrorsList)
|
||||||
|
|
||||||
assert.deepStrictEqual(actual, expected)
|
assert.deepStrictEqual(actual, expected)
|
||||||
|
@ -51,9 +51,9 @@ describe('validation', () => {
|
||||||
},
|
},
|
||||||
[VALIDATION_ERROR]: error2
|
[VALIDATION_ERROR]: error2
|
||||||
}
|
}
|
||||||
|
|
||||||
const actual = mapValidationErrors(validationErrorsList)
|
const actual = mapValidationErrors(validationErrorsList)
|
||||||
|
|
||||||
assert.deepStrictEqual(actual, expected)
|
assert.deepStrictEqual(actual, expected)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { isObject } from './typeUtils.js'
|
import { isObject } from './typeUtils.js'
|
||||||
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
||||||
|
|
||||||
const MAX_ITEM_PATHS_COLLECTION = 10000
|
const MAX_ITEM_PATHS_COLLECTION = 10000
|
||||||
const EMPTY_ARRAY = []
|
const EMPTY_ARRAY = []
|
||||||
|
@ -51,7 +51,7 @@ export function strictShallowEqual (a, b) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export function compareArrays(a, b) {
|
export function compareArrays (a, b) {
|
||||||
const minLength = Math.min(a.length, b.length)
|
const minLength = Math.min(a.length, b.length)
|
||||||
|
|
||||||
for (let i = 0; i < minLength; i++) {
|
for (let i = 0; i < minLength; i++) {
|
||||||
|
@ -75,7 +75,7 @@ export function compareArrays(a, b) {
|
||||||
*/
|
*/
|
||||||
export function getNestedPaths (array, includeObjects = false) {
|
export function getNestedPaths (array, includeObjects = false) {
|
||||||
const pathsMap = {}
|
const pathsMap = {}
|
||||||
|
|
||||||
if (!Array.isArray(array)) {
|
if (!Array.isArray(array)) {
|
||||||
throw new TypeError('Array expected')
|
throw new TypeError('Array expected')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import assert from "assert"
|
import assert from 'assert'
|
||||||
import { compareArrays, getNestedPaths } from './arrayUtils.js'
|
import { compareArrays, getNestedPaths } from './arrayUtils.js'
|
||||||
|
|
||||||
describe('arrayUtils', () => {
|
describe('arrayUtils', () => {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* @param {Element} element An HTML DOM element like a DIV
|
* @param {Element} element An HTML DOM element like a DIV
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export function getPlainText(element) {
|
export function getPlainText (element) {
|
||||||
return unescapeHTML(traverseInnerText(element))
|
return unescapeHTML(traverseInnerText(element))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export function getPlainText(element) {
|
||||||
* @param {Element} element An HTML DOM element like a DIV
|
* @param {Element} element An HTML DOM element like a DIV
|
||||||
* @param {string} text
|
* @param {string} text
|
||||||
*/
|
*/
|
||||||
export function setPlainText(element, text) {
|
export function setPlainText (element, text) {
|
||||||
element.innerHTML = escapeHTML(text)
|
element.innerHTML = escapeHTML(text)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -26,8 +26,7 @@ export function setPlainText(element, text) {
|
||||||
export function escapeHTML (text, escapeUnicode = false) {
|
export function escapeHTML (text, escapeUnicode = false) {
|
||||||
if (typeof text !== 'string') {
|
if (typeof text !== 'string') {
|
||||||
return String(text)
|
return String(text)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
let htmlEscaped = String(text)
|
let htmlEscaped = String(text)
|
||||||
if (escapeUnicode === true) {
|
if (escapeUnicode === true) {
|
||||||
// FIXME: should not unescape the just created non-breaking spaces \u00A0 ?
|
// FIXME: should not unescape the just created non-breaking spaces \u00A0 ?
|
||||||
|
@ -36,8 +35,8 @@ export function escapeHTML (text, escapeUnicode = false) {
|
||||||
|
|
||||||
htmlEscaped = htmlEscaped
|
htmlEscaped = htmlEscaped
|
||||||
.replace(/ {2}/g, ' \u00A0') // replace double space with an nbsp and space
|
.replace(/ {2}/g, ' \u00A0') // replace double space with an nbsp and space
|
||||||
.replace(/^ /, '\u00A0') // space at start
|
.replace(/^ /, '\u00A0') // space at start
|
||||||
.replace(/ $/, '\u00A0') // space at end
|
.replace(/ $/, '\u00A0') // space at end
|
||||||
|
|
||||||
const json = JSON.stringify(htmlEscaped)
|
const json = JSON.stringify(htmlEscaped)
|
||||||
return json.substring(1, json.length - 1) // remove enclosing double quotes
|
return json.substring(1, json.length - 1) // remove enclosing double quotes
|
||||||
|
@ -54,8 +53,8 @@ export function escapeUnicodeChars (text) {
|
||||||
// see https://www.wikiwand.com/en/UTF-16
|
// see https://www.wikiwand.com/en/UTF-16
|
||||||
// note: we leave surrogate pairs as two individual chars,
|
// note: we leave surrogate pairs as two individual chars,
|
||||||
// as JSON doesn't interpret them as a single unicode char.
|
// as JSON doesn't interpret them as a single unicode char.
|
||||||
return text.replace(/[\u007F-\uFFFF]/g, function(c) {
|
return text.replace(/[\u007F-\uFFFF]/g, function (c) {
|
||||||
return '\\u'+('0000' + c.charCodeAt(0).toString(16)).slice(-4)
|
return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +67,7 @@ export function unescapeHTML (escapedText) {
|
||||||
const json = '"' + escapeJSON(escapedText) + '"'
|
const json = '"' + escapeJSON(escapedText) + '"'
|
||||||
const htmlEscaped = JSON.parse(json) // TODO: replace with a JSON.parse which does do linting and give an informative error
|
const htmlEscaped = JSON.parse(json) // TODO: replace with a JSON.parse which does do linting and give an informative error
|
||||||
|
|
||||||
return htmlEscaped.replace(/\u00A0/g, ' ') // nbsp character
|
return htmlEscaped.replace(/\u00A0/g, ' ') // nbsp character
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,21 +87,18 @@ export function escapeJSON (text) {
|
||||||
let c = text.charAt(i)
|
let c = text.charAt(i)
|
||||||
if (c === '\n') {
|
if (c === '\n') {
|
||||||
escaped += '\\n'
|
escaped += '\\n'
|
||||||
}
|
} else if (c === '\\') {
|
||||||
else if (c === '\\') {
|
|
||||||
escaped += c
|
escaped += c
|
||||||
i++
|
i++
|
||||||
|
|
||||||
c = text.charAt(i)
|
c = text.charAt(i)
|
||||||
if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) {
|
if (c === '' || '"\\/bfnrtu'.indexOf(c) === -1) {
|
||||||
escaped += '\\' // no valid escape character
|
escaped += '\\' // no valid escape character
|
||||||
}
|
}
|
||||||
escaped += c
|
escaped += c
|
||||||
}
|
} else if (c === '"') {
|
||||||
else if (c === '"') {
|
|
||||||
escaped += '\\"'
|
escaped += '\\"'
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
escaped += c
|
escaped += c
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
|
@ -192,7 +188,7 @@ export function isContentEditableDiv (element) {
|
||||||
return (element.nodeName === 'DIV' && element.contentEditable === 'true')
|
return (element.nodeName === 'DIV' && element.contentEditable === 'true')
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAttribute(element, name, value) {
|
function hasAttribute (element, name, value) {
|
||||||
return typeof element.getAttribute === 'function' && element.getAttribute(name) === value
|
return typeof element.getAttribute === 'function' && element.getAttribute(name) === value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,4 +18,4 @@ describe('domUtils', () => {
|
||||||
|
|
||||||
// TODO: test unescapeHTML more thoroughly
|
// TODO: test unescapeHTML more thoroughly
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { isObjectOrArray } from './typeUtils.js'
|
import { isObjectOrArray } from './typeUtils.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immutability helpers
|
* Immutability helpers
|
||||||
|
@ -23,20 +23,22 @@ export function shallowClone (value) {
|
||||||
const copy = value.slice()
|
const copy = value.slice()
|
||||||
|
|
||||||
// copy all symbols
|
// copy all symbols
|
||||||
Object.getOwnPropertySymbols(value).forEach(symbol => copy[symbol] = value[symbol])
|
Object.getOwnPropertySymbols(value).forEach(symbol => {
|
||||||
|
copy[symbol] = value[symbol]
|
||||||
|
})
|
||||||
|
|
||||||
return copy
|
return copy
|
||||||
}
|
} else if (typeof value === 'object') {
|
||||||
else if (typeof value === 'object') {
|
|
||||||
// copy object properties
|
// copy object properties
|
||||||
const copy = { ...value }
|
const copy = { ...value }
|
||||||
|
|
||||||
// copy all symbols
|
// copy all symbols
|
||||||
Object.getOwnPropertySymbols(value).forEach(symbol => copy[symbol] = value[symbol])
|
Object.getOwnPropertySymbols(value).forEach(symbol => {
|
||||||
|
copy[symbol] = value[symbol]
|
||||||
|
})
|
||||||
|
|
||||||
return copy
|
return copy
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,8 +55,7 @@ export function applyProp (object, key, value) {
|
||||||
if (object[key] === value) {
|
if (object[key] === value) {
|
||||||
// return original object unchanged when the new value is identical to the old one
|
// return original object unchanged when the new value is identical to the old one
|
||||||
return object
|
return object
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const updatedObject = shallowClone(object)
|
const updatedObject = shallowClone(object)
|
||||||
updatedObject[key] = value
|
updatedObject[key] = value
|
||||||
return updatedObject
|
return updatedObject
|
||||||
|
@ -73,11 +74,10 @@ export function getIn (object, path) {
|
||||||
let value = object
|
let value = object
|
||||||
let i = 0
|
let i = 0
|
||||||
|
|
||||||
while(i < path.length) {
|
while (i < path.length) {
|
||||||
if (isObjectOrArray(value)) {
|
if (isObjectOrArray(value)) {
|
||||||
value = value[path[i]]
|
value = value[path[i]]
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
value = undefined
|
value = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,14 +171,12 @@ export function deleteIn (object, path) {
|
||||||
if (!(key in object)) {
|
if (!(key in object)) {
|
||||||
// key doesn't exist. return object unchanged
|
// key doesn't exist. return object unchanged
|
||||||
return object
|
return object
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
const updatedObject = shallowClone(object)
|
const updatedObject = shallowClone(object)
|
||||||
|
|
||||||
if (Array.isArray(updatedObject)) {
|
if (Array.isArray(updatedObject)) {
|
||||||
updatedObject.splice(key, 1)
|
updatedObject.splice(key, 1)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
delete updatedObject[key]
|
delete updatedObject[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +228,7 @@ export function transform (json, callback, path = []) {
|
||||||
const updated1 = callback(json, path)
|
const updated1 = callback(json, path)
|
||||||
|
|
||||||
if (Array.isArray(json)) { // array
|
if (Array.isArray(json)) { // array
|
||||||
let updated2 = undefined
|
let updated2
|
||||||
|
|
||||||
for (let i = 0; i < updated1.length; i++) {
|
for (let i = 0; i < updated1.length; i++) {
|
||||||
const before = updated1[i]
|
const before = updated1[i]
|
||||||
|
@ -246,13 +244,12 @@ export function transform (json, callback, path = []) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return updated2 ? updated2 : updated1
|
return updated2 || updated1
|
||||||
}
|
} else if (json && typeof json === 'object') { // object
|
||||||
else if (json && typeof json === 'object') { // object
|
let updated2
|
||||||
let updated2 = undefined
|
|
||||||
|
|
||||||
for (let key in updated1) {
|
for (const key in updated1) {
|
||||||
if (updated1.hasOwnProperty(key)) {
|
if (Object.hasOwnProperty.call(updated1, key)) {
|
||||||
const before = updated1[key]
|
const before = updated1[key]
|
||||||
const after = transform(before, callback, path.concat(key))
|
const after = transform(before, callback, path.concat(key))
|
||||||
if (after !== before) {
|
if (after !== before) {
|
||||||
|
@ -264,9 +261,8 @@ export function transform (json, callback, path = []) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return updated2 ? updated2 : updated1
|
return updated2 || updated1
|
||||||
}
|
} else { // number, string, boolean, null
|
||||||
else { // number, string, boolean, null
|
|
||||||
return updated1
|
return updated1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ describe('immutabilityHelpers', () => {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.deepStrictEqual(getIn(obj, ['a', 'b']), {c: 2})
|
assert.deepStrictEqual(getIn(obj, ['a', 'b']), { c: 2 })
|
||||||
assert.deepStrictEqual(getIn(obj, ['e', '1', 'f']), 5)
|
assert.deepStrictEqual(getIn(obj, ['e', '1', 'f']), 5)
|
||||||
assert.deepStrictEqual(getIn(obj, ['e', '999', 'f']), undefined)
|
assert.deepStrictEqual(getIn(obj, ['e', '999', 'f']), undefined)
|
||||||
assert.deepStrictEqual(getIn(obj, ['non', 'existing', 'path']), undefined)
|
assert.deepStrictEqual(getIn(obj, ['non', 'existing', 'path']), undefined)
|
||||||
|
@ -92,7 +92,7 @@ describe('immutabilityHelpers', () => {
|
||||||
a: [
|
a: [
|
||||||
,
|
,
|
||||||
,
|
,
|
||||||
{ c : 4 }
|
{ c: 4 }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(obj, {})
|
assert.deepStrictEqual(obj, {})
|
||||||
|
@ -104,7 +104,7 @@ describe('immutabilityHelpers', () => {
|
||||||
d: 3
|
d: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.throws(() => setIn(obj, ['a', 'b', 'c'], 4), /Path does not exist/)
|
assert.throws(() => setIn(obj, ['a', 'b', 'c'], 4), /Path does not exist/)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('setIn replace value inside nested array', () => {
|
it('setIn replace value inside nested array', () => {
|
||||||
|
@ -136,7 +136,7 @@ describe('immutabilityHelpers', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('setIn identical value should return the original object', () => {
|
it('setIn identical value should return the original object', () => {
|
||||||
const obj = {a:1, b:2}
|
const obj = { a: 1, b: 2 }
|
||||||
|
|
||||||
const updated = setIn(obj, ['b'], 2)
|
const updated = setIn(obj, ['b'], 2)
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ describe('immutabilityHelpers', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('setIn identical value should return the original object (2)', () => {
|
it('setIn identical value should return the original object (2)', () => {
|
||||||
const obj = {a:1, b: { c: 2}}
|
const obj = { a: 1, b: { c: 2 } }
|
||||||
|
|
||||||
const updated = setIn(obj, ['b', 'c'], 2)
|
const updated = setIn(obj, ['b', 'c'], 2)
|
||||||
|
|
||||||
|
@ -194,10 +194,10 @@ describe('immutabilityHelpers', () => {
|
||||||
d: 3
|
d: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = updateIn(obj, ['a', 'b' ], (obj) => [1,2,3])
|
const updated = updateIn(obj, ['a', 'b'], (obj) => [1, 2, 3])
|
||||||
assert.deepStrictEqual(updated, {
|
assert.deepStrictEqual(updated, {
|
||||||
a: {
|
a: {
|
||||||
b: [1,2,3]
|
b: [1, 2, 3]
|
||||||
},
|
},
|
||||||
d: 3
|
d: 3
|
||||||
})
|
})
|
||||||
|
@ -213,7 +213,7 @@ describe('immutabilityHelpers', () => {
|
||||||
d: 3
|
d: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = updateIn(obj, ['a', 'e' ], (value) => 'foo-' + value)
|
const updated = updateIn(obj, ['a', 'e'], (value) => 'foo-' + value)
|
||||||
assert.deepStrictEqual(updated, {
|
assert.deepStrictEqual(updated, {
|
||||||
a: {
|
a: {
|
||||||
b: {
|
b: {
|
||||||
|
@ -231,7 +231,7 @@ describe('immutabilityHelpers', () => {
|
||||||
b: 3
|
b: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = updateIn(obj, ['b' ], (value) => 3)
|
const updated = updateIn(obj, ['b'], (value) => 3)
|
||||||
assert.strictEqual(updated, obj)
|
assert.strictEqual(updated, obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ describe('immutabilityHelpers', () => {
|
||||||
it('deleteIn array', () => {
|
it('deleteIn array', () => {
|
||||||
const obj = {
|
const obj = {
|
||||||
a: {
|
a: {
|
||||||
b: [1, {c: 2, d: 3} , 4]
|
b: [1, { c: 2, d: 3 }, 4]
|
||||||
},
|
},
|
||||||
e: 5
|
e: 5
|
||||||
}
|
}
|
||||||
|
@ -281,7 +281,7 @@ describe('immutabilityHelpers', () => {
|
||||||
const updated = deleteIn(obj, ['a', 'b', '1', 'c'])
|
const updated = deleteIn(obj, ['a', 'b', '1', 'c'])
|
||||||
assert.deepStrictEqual(updated, {
|
assert.deepStrictEqual(updated, {
|
||||||
a: {
|
a: {
|
||||||
b: [1, {d: 3} , 4]
|
b: [1, { d: 3 }, 4]
|
||||||
},
|
},
|
||||||
e: 5
|
e: 5
|
||||||
})
|
})
|
||||||
|
@ -289,7 +289,7 @@ describe('immutabilityHelpers', () => {
|
||||||
// original should be unchanged
|
// original should be unchanged
|
||||||
assert.deepStrictEqual(obj, {
|
assert.deepStrictEqual(obj, {
|
||||||
a: {
|
a: {
|
||||||
b: [1, {c: 2, d: 3} , 4]
|
b: [1, { c: 2, d: 3 }, 4]
|
||||||
},
|
},
|
||||||
e: 5
|
e: 5
|
||||||
})
|
})
|
||||||
|
@ -298,54 +298,54 @@ describe('immutabilityHelpers', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('deleteIn non existing path', () => {
|
it('deleteIn non existing path', () => {
|
||||||
const obj = { a: {}}
|
const obj = { a: {} }
|
||||||
|
|
||||||
const updated = deleteIn(obj, ['a', 'b'])
|
const updated = deleteIn(obj, ['a', 'b'])
|
||||||
assert.strictEqual(updated, obj)
|
assert.strictEqual(updated, obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('insertAt', () => {
|
it('insertAt', () => {
|
||||||
const obj = { a: [1,2,3]}
|
const obj = { a: [1, 2, 3] }
|
||||||
|
|
||||||
const updated = insertAt(obj, ['a', '2'], 8)
|
const updated = insertAt(obj, ['a', '2'], 8)
|
||||||
assert.deepStrictEqual(updated, {a: [1,2,8,3]})
|
assert.deepStrictEqual(updated, { a: [1, 2, 8, 3] })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('insertAt should copy symbols', () => {
|
it('insertAt should copy symbols', () => {
|
||||||
const symbol = Symbol('test symbol')
|
const symbol = Symbol('test symbol')
|
||||||
const obj = { a: [1,2,3] }
|
const obj = { a: [1, 2, 3] }
|
||||||
obj.a[symbol] = 'test'
|
obj.a[symbol] = 'test'
|
||||||
|
|
||||||
const updated = insertAt(obj, ['a', '2'], 8)
|
const updated = insertAt(obj, ['a', '2'], 8)
|
||||||
|
|
||||||
const expected = {a: [1,2,8,3]}
|
const expected = { a: [1, 2, 8, 3] }
|
||||||
expected.a[symbol] = 'test'
|
expected.a[symbol] = 'test'
|
||||||
assert.deepStrictEqual(updated, expected)
|
assert.deepStrictEqual(updated, expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('transform (no change)', () => {
|
it('transform (no change)', () => {
|
||||||
const json = {a: [1,2,3], b: {c: 4}}
|
const json = { a: [1, 2, 3], b: { c: 4 } }
|
||||||
const updated = transform(json, (value, path) => value)
|
const updated = transform(json, (value, path) => value)
|
||||||
assert.strictEqual(updated, json)
|
assert.strictEqual(updated, json)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('transform (change based on value)', () => {
|
it('transform (change based on value)', () => {
|
||||||
const json = {a: [1,2,3], b: {c: 4}}
|
const json = { a: [1, 2, 3], b: { c: 4 } }
|
||||||
|
|
||||||
const updated = transform(json,
|
const updated = transform(json,
|
||||||
(value, path) => value === 2 ? 20 : value)
|
(value, path) => value === 2 ? 20 : value)
|
||||||
const expected = {a: [1,20,3], b: {c: 4}}
|
const expected = { a: [1, 20, 3], b: { c: 4 } }
|
||||||
|
|
||||||
assert.deepStrictEqual(updated, expected)
|
assert.deepStrictEqual(updated, expected)
|
||||||
assert.strictEqual(updated.b, json.b) // should not have replaced b
|
assert.strictEqual(updated.b, json.b) // should not have replaced b
|
||||||
})
|
})
|
||||||
|
|
||||||
it('transform (change based on path)', () => {
|
it('transform (change based on path)', () => {
|
||||||
const json = {a: [1,2,3], b: {c: 4}}
|
const json = { a: [1, 2, 3], b: { c: 4 } }
|
||||||
|
|
||||||
const updated = transform(json,
|
const updated = transform(json,
|
||||||
(value, path) => path.join('.') === 'a.1' ? 20 : value)
|
(value, path) => path.join('.') === 'a.1' ? 20 : value)
|
||||||
const expected = {a: [1,20,3], b: {c: 4}}
|
const expected = { a: [1, 20, 3], b: { c: 4 } }
|
||||||
|
|
||||||
assert.deepStrictEqual(updated, expected)
|
assert.deepStrictEqual(updated, expected)
|
||||||
assert.strictEqual(updated.b, json.b) // should not have replaced b
|
assert.strictEqual(updated.b, json.b) // should not have replaced b
|
||||||
|
@ -353,12 +353,12 @@ describe('immutabilityHelpers', () => {
|
||||||
|
|
||||||
it('existsIn', () => {
|
it('existsIn', () => {
|
||||||
const json = {
|
const json = {
|
||||||
"obj": {
|
obj: {
|
||||||
"arr": [1,2, {"first":3,"last":4}]
|
arr: [1, 2, { first: 3, last: 4 }]
|
||||||
},
|
},
|
||||||
"str": "hello world",
|
str: 'hello world',
|
||||||
"nill": null,
|
nill: null,
|
||||||
"bool": false
|
bool: false
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.deepStrictEqual(existsIn(json, ['obj', 'arr', 2, 'first']), true)
|
assert.deepStrictEqual(existsIn(json, ['obj', 'arr', 2, 'first']), true)
|
||||||
|
@ -366,17 +366,8 @@ describe('immutabilityHelpers', () => {
|
||||||
assert.deepStrictEqual(existsIn(json, ['obj', 'foo', 'bar']), false)
|
assert.deepStrictEqual(existsIn(json, ['obj', 'foo', 'bar']), false)
|
||||||
assert.deepStrictEqual(existsIn(json, []), true)
|
assert.deepStrictEqual(existsIn(json, []), true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('existsIn should find symbols', () => {
|
|
||||||
const json = {
|
|
||||||
"obj": {
|
|
||||||
"arr": [1,2, {"first":3,"last":4}]
|
|
||||||
},
|
|
||||||
"str": "hello world",
|
|
||||||
"nill": null,
|
|
||||||
"bool": false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
it('existsIn should find symbols', () => {
|
||||||
const symbol = Symbol('mySymbol')
|
const symbol = Symbol('mySymbol')
|
||||||
|
|
||||||
const arrWithSymbol = [1, 2, 3]
|
const arrWithSymbol = [1, 2, 3]
|
||||||
|
@ -387,4 +378,4 @@ describe('immutabilityHelpers', () => {
|
||||||
assert.deepStrictEqual(existsIn({ a: 1, [symbol]: 2 }, [symbol]), true)
|
assert.deepStrictEqual(existsIn({ a: 1, [symbol]: 2 }, [symbol]), true)
|
||||||
assert.deepStrictEqual(existsIn({ a: 1 }, [symbol]), false)
|
assert.deepStrictEqual(existsIn({ a: 1 }, [symbol]), false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,8 +22,8 @@ export function immutableJSONPatch (json, operations) {
|
||||||
|
|
||||||
for (let i = 0; i < operations.length; i++) {
|
for (let i = 0; i < operations.length; i++) {
|
||||||
const operation = operations[i]
|
const operation = operations[i]
|
||||||
const path = operation.path != undefined ? parseJSONPointer(operation.path) : null
|
const path = operation.path !== undefined ? parseJSONPointer(operation.path) : null
|
||||||
const from = operation.from != undefined ? parseJSONPointer(operation.from) : null
|
const from = operation.from !== undefined ? parseJSONPointer(operation.from) : null
|
||||||
|
|
||||||
switch (operation.op) {
|
switch (operation.op) {
|
||||||
case 'add': {
|
case 'add': {
|
||||||
|
@ -85,7 +85,7 @@ export function immutableJSONPatch (json, operations) {
|
||||||
// when a test fails, cancel the whole patch and return the error
|
// when a test fails, cancel the whole patch and return the error
|
||||||
const error = test(updatedJson, path, operation.value)
|
const error = test(updatedJson, path, operation.value)
|
||||||
if (error) {
|
if (error) {
|
||||||
return { json, revert: [], error}
|
return { json, revert: [], error }
|
||||||
}
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
|
@ -160,12 +160,12 @@ export function add (json, path, value) {
|
||||||
const parent = getIn(json, initial(path))
|
const parent = getIn(json, initial(path))
|
||||||
const parentIsArray = Array.isArray(parent)
|
const parentIsArray = Array.isArray(parent)
|
||||||
const oldValue = parentIsArray
|
const oldValue = parentIsArray
|
||||||
? undefined
|
? undefined
|
||||||
: getIn(json, resolvedPath)
|
: getIn(json, resolvedPath)
|
||||||
|
|
||||||
const updatedJson = parentIsArray
|
const updatedJson = parentIsArray
|
||||||
? insertAt(json, resolvedPath, value)
|
? insertAt(json, resolvedPath, value)
|
||||||
: setIn(json, resolvedPath, value)
|
: setIn(json, resolvedPath, value)
|
||||||
|
|
||||||
if (!parentIsArray && existsIn(json, resolvedPath)) {
|
if (!parentIsArray && existsIn(json, resolvedPath)) {
|
||||||
return {
|
return {
|
||||||
|
@ -176,8 +176,7 @@ export function add (json, path, value) {
|
||||||
value: oldValue
|
value: oldValue
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return {
|
return {
|
||||||
json: updatedJson,
|
json: updatedJson,
|
||||||
revert: [{
|
revert: [{
|
||||||
|
@ -219,8 +218,8 @@ export function move (json, path, from) {
|
||||||
|
|
||||||
const removedJson = remove(json, from).json
|
const removedJson = remove(json, from).json
|
||||||
const updatedJson = parentIsArray
|
const updatedJson = parentIsArray
|
||||||
? insertAt(removedJson, resolvedPath, value)
|
? insertAt(removedJson, resolvedPath, value)
|
||||||
: setIn(removedJson, resolvedPath, value)
|
: setIn(removedJson, resolvedPath, value)
|
||||||
|
|
||||||
if (oldValue !== undefined && !parentIsArray) {
|
if (oldValue !== undefined && !parentIsArray) {
|
||||||
// replaces an existing value in an object
|
// replaces an existing value in an object
|
||||||
|
@ -239,8 +238,7 @@ export function move (json, path, from) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return {
|
return {
|
||||||
json: updatedJson,
|
json: updatedJson,
|
||||||
revert: [
|
revert: [
|
||||||
|
@ -287,6 +285,6 @@ export function resolvePathIndex (json, path) {
|
||||||
const parent = getIn(json, initial(path))
|
const parent = getIn(json, initial(path))
|
||||||
|
|
||||||
return (path[path.length - 1] === '-')
|
return (path[path.length - 1] === '-')
|
||||||
? path.slice(0, path.length - 1).concat(parent.length)
|
? path.slice(0, path.length - 1).concat(parent.length)
|
||||||
: path
|
: path
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,92 +14,92 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
it('jsonpatch add', () => {
|
it('jsonpatch add', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 2}
|
obj: { a: 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'add', path: '/obj/b', value: {foo: 'bar'}}
|
{ op: 'add', path: '/obj/b', value: { foo: 'bar' } }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 2, b: {foo: 'bar'}}
|
obj: { a: 2, b: { foo: 'bar' } }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'remove', path: '/obj/b'}
|
{ op: 'remove', path: '/obj/b' }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.arr, json.arr)
|
assert.strictEqual(result.json.arr, json.arr)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch add: insert in matrix', () => {
|
it('jsonpatch add: insert in matrix', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 2}
|
obj: { a: 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'add', path: '/arr/1', value: 4}
|
{ op: 'add', path: '/arr/1', value: 4 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,4,2,3],
|
arr: [1, 4, 2, 3],
|
||||||
obj: {a : 2}
|
obj: { a: 2 }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'remove', path: '/arr/1'}
|
{ op: 'remove', path: '/arr/1' }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.obj, json.obj)
|
assert.strictEqual(result.json.obj, json.obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch add: append to matrix', () => {
|
it('jsonpatch add: append to matrix', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 2}
|
obj: { a: 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'add', path: '/arr/-', value: 4}
|
{ op: 'add', path: '/arr/-', value: 4 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,2,3,4],
|
arr: [1, 2, 3, 4],
|
||||||
obj: {a : 2}
|
obj: { a: 2 }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'remove', path: '/arr/3'}
|
{ op: 'remove', path: '/arr/3' }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.obj, json.obj)
|
assert.strictEqual(result.json.obj, json.obj)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch remove', () => {
|
it('jsonpatch remove', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4},
|
obj: { a: 4 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'remove', path: '/obj/a'},
|
{ op: 'remove', path: '/obj/a' },
|
||||||
{op: 'remove', path: '/arr/1'},
|
{ op: 'remove', path: '/arr/1' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,3],
|
arr: [1, 3],
|
||||||
obj: {},
|
obj: {},
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'add', path: '/arr/1', value: 2},
|
{ op: 'add', path: '/arr/1', value: 2 },
|
||||||
{op: 'add', path: '/obj/a', value: 4}
|
{ op: 'add', path: '/obj/a', value: 4 }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -112,26 +112,26 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
it('jsonpatch replace', () => {
|
it('jsonpatch replace', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4},
|
obj: { a: 4 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'replace', path: '/obj/a', value: 400},
|
{ op: 'replace', path: '/obj/a', value: 400 },
|
||||||
{op: 'replace', path: '/arr/1', value: 200},
|
{ op: 'replace', path: '/arr/1', value: 200 }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,200,3],
|
arr: [1, 200, 3],
|
||||||
obj: {a: 400},
|
obj: { a: 400 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'replace', path: '/arr/1', value: 2},
|
{ op: 'replace', path: '/arr/1', value: 2 },
|
||||||
{op: 'replace', path: '/obj/a', value: 4}
|
{ op: 'replace', path: '/obj/a', value: 4 }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -139,8 +139,8 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
assert.deepStrictEqual(result2.json, json)
|
assert.deepStrictEqual(result2.json, json)
|
||||||
assert.deepStrictEqual(result2.revert, [
|
assert.deepStrictEqual(result2.revert, [
|
||||||
{op: 'replace', path: '/obj/a', value: 400},
|
{ op: 'replace', path: '/obj/a', value: 400 },
|
||||||
{op: 'replace', path: '/arr/1', value: 200}
|
{ op: 'replace', path: '/arr/1', value: 200 }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.unchanged, json.unchanged)
|
assert.strictEqual(result.json.unchanged, json.unchanged)
|
||||||
})
|
})
|
||||||
|
@ -150,31 +150,31 @@ describe('immutableJSONPatch', () => {
|
||||||
a: 2
|
a: 2
|
||||||
}
|
}
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'replace', path: '', value: {b: 3}}
|
{ op: 'replace', path: '', value: { b: 3 } }
|
||||||
]
|
]
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {b: 3})
|
assert.deepStrictEqual(result.json, { b: 3 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch copy', () => {
|
it('jsonpatch copy', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'copy', from: '/obj', path: '/arr/2'},
|
{ op: 'copy', from: '/obj', path: '/arr/2' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1, 2, {a:4}, 3],
|
arr: [1, 2, { a: 4 }, 3],
|
||||||
obj: {a: 4}
|
obj: { a: 4 }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'remove', path: '/arr/2'}
|
{ op: 'remove', path: '/arr/2' }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -182,7 +182,7 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
assert.deepStrictEqual(result2.json, json)
|
assert.deepStrictEqual(result2.json, json)
|
||||||
assert.deepStrictEqual(result2.revert, [
|
assert.deepStrictEqual(result2.revert, [
|
||||||
{op: 'add', path: '/arr/2', value: {a: 4}}
|
{ op: 'add', path: '/arr/2', value: { a: 4 } }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.obj, json.obj)
|
assert.strictEqual(result.json.obj, json.obj)
|
||||||
assert.strictEqual(result.json.arr[2], json.obj)
|
assert.strictEqual(result.json.arr[2], json.obj)
|
||||||
|
@ -190,24 +190,24 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
it('jsonpatch move', () => {
|
it('jsonpatch move', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4},
|
obj: { a: 4 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'move', from: '/obj', path: '/arr/2'},
|
{ op: 'move', from: '/obj', path: '/arr/2' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.error, null)
|
assert.deepStrictEqual(result.error, null)
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1, 2, {a:4}, 3],
|
arr: [1, 2, { a: 4 }, 3],
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'move', from: '/arr/2', path: '/obj'}
|
{ op: 'move', from: '/arr/2', path: '/obj' }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -223,15 +223,15 @@ describe('immutableJSONPatch', () => {
|
||||||
const json = { a: 2, b: 3 }
|
const json = { a: 2, b: 3 }
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'move', from: '/a', path: '/b'},
|
{ op: 'move', from: '/a', path: '/b' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, { b : 2 })
|
assert.deepStrictEqual(result.json, { b: 2 })
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op:'move', from: '/b', path: '/a'},
|
{ op: 'move', from: '/b', path: '/a' },
|
||||||
{op:'add', path:'/b', value: 3}
|
{ op: 'add', path: '/b', value: 3 }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -239,31 +239,31 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
assert.deepStrictEqual(result2.json, json)
|
assert.deepStrictEqual(result2.json, json)
|
||||||
assert.deepStrictEqual(result2.revert, [
|
assert.deepStrictEqual(result2.revert, [
|
||||||
{op: 'remove', path: '/b'},
|
{ op: 'remove', path: '/b' },
|
||||||
{op: 'move', from: '/a', path: '/b'}
|
{ op: 'move', from: '/a', path: '/b' }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch move and replace (nested)', () => {
|
it('jsonpatch move and replace (nested)', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4},
|
obj: { a: 4 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'move', from: '/obj', path: '/arr'},
|
{ op: 'move', from: '/obj', path: '/arr' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: {a:4},
|
arr: { a: 4 },
|
||||||
unchanged: {}
|
unchanged: {}
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op:'move', from: '/arr', path: '/obj'},
|
{ op: 'move', from: '/arr', path: '/obj' },
|
||||||
{op:'add', path:'/arr', value: [1,2,3]}
|
{ op: 'add', path: '/arr', value: [1, 2, 3] }
|
||||||
])
|
])
|
||||||
|
|
||||||
// test revert
|
// test revert
|
||||||
|
@ -271,8 +271,8 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
assert.deepStrictEqual(result2.json, json)
|
assert.deepStrictEqual(result2.json, json)
|
||||||
assert.deepStrictEqual(result2.revert, [
|
assert.deepStrictEqual(result2.revert, [
|
||||||
{op: 'remove', path: '/arr'},
|
{ op: 'remove', path: '/arr' },
|
||||||
{op: 'move', from: '/obj', path: '/arr'}
|
{ op: 'move', from: '/obj', path: '/arr' }
|
||||||
])
|
])
|
||||||
assert.strictEqual(result.json.unchanged, json.unchanged)
|
assert.strictEqual(result.json.unchanged, json.unchanged)
|
||||||
assert.strictEqual(result2.json.unchanged, json.unchanged)
|
assert.strictEqual(result2.json.unchanged, json.unchanged)
|
||||||
|
@ -280,44 +280,44 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
it('jsonpatch test (ok)', () => {
|
it('jsonpatch test (ok)', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'test', path: '/arr', value: [1,2,3]},
|
{ op: 'test', path: '/arr', value: [1, 2, 3] },
|
||||||
{op: 'add', path: '/added', value: 'ok'}
|
{ op: 'add', path: '/added', value: 'ok' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4},
|
obj: { a: 4 },
|
||||||
added: 'ok'
|
added: 'ok'
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [
|
assert.deepStrictEqual(result.revert, [
|
||||||
{op: 'remove', path: '/added'}
|
{ op: 'remove', path: '/added' }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('jsonpatch test (fail: path not found)', () => {
|
it('jsonpatch test (fail: path not found)', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'test', path: '/arr/5', value: [1,2,3]},
|
{ op: 'test', path: '/arr/5', value: [1, 2, 3] },
|
||||||
{op: 'add', path: '/added', value: 'ok'}
|
{ op: 'add', path: '/added', value: 'ok' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
// patch shouldn't be applied
|
// patch shouldn't be applied
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [])
|
assert.deepStrictEqual(result.revert, [])
|
||||||
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, path not found')
|
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, path not found')
|
||||||
|
@ -325,23 +325,23 @@ describe('immutableJSONPatch', () => {
|
||||||
|
|
||||||
it('jsonpatch test (fail: value not equal)', () => {
|
it('jsonpatch test (fail: value not equal)', () => {
|
||||||
const json = {
|
const json = {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
}
|
}
|
||||||
|
|
||||||
const patch = [
|
const patch = [
|
||||||
{op: 'test', path: '/obj', value: {a:4, b: 6}},
|
{ op: 'test', path: '/obj', value: { a: 4, b: 6 } },
|
||||||
{op: 'add', path: '/added', value: 'ok'}
|
{ op: 'add', path: '/added', value: 'ok' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const result = immutableJSONPatch(json, patch)
|
const result = immutableJSONPatch(json, patch)
|
||||||
|
|
||||||
// patch shouldn't be applied
|
// patch shouldn't be applied
|
||||||
assert.deepStrictEqual(result.json, {
|
assert.deepStrictEqual(result.json, {
|
||||||
arr: [1,2,3],
|
arr: [1, 2, 3],
|
||||||
obj: {a : 4}
|
obj: { a: 4 }
|
||||||
})
|
})
|
||||||
assert.deepStrictEqual(result.revert, [])
|
assert.deepStrictEqual(result.revert, [])
|
||||||
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, value differs')
|
assert.deepStrictEqual(result.error.toString(), 'Error: Test failed, value differs')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -19,6 +19,6 @@ export function parseJSONPointer (pointer) {
|
||||||
*/
|
*/
|
||||||
export function compileJSONPointer (path) {
|
export function compileJSONPointer (path) {
|
||||||
return path
|
return path
|
||||||
.map(p => '/' + String(p).replace(/~/g, '~0').replace(/\//g, '~1'))
|
.map(p => '/' + String(p).replace(/~/g, '~0').replace(/\//g, '~1'))
|
||||||
.join('')
|
.join('')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import {compileJSONPointer, parseJSONPointer} from './jsonPointer.js'
|
import { compileJSONPointer, parseJSONPointer } from './jsonPointer.js'
|
||||||
|
|
||||||
describe('jsonPointer', () => {
|
describe('jsonPointer', () => {
|
||||||
it('parseJSONPointer', () => {
|
it('parseJSONPointer', () => {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* @param {string} code
|
* @param {string} code
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export function nameFromKeyCode(code) {
|
export function nameFromKeyCode (code) {
|
||||||
return codes[code] || ''
|
return codes[code] || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ export function nameFromKeyCode(code) {
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export function keyComboFromEvent (event) {
|
export function keyComboFromEvent (event) {
|
||||||
let combi = []
|
const combi = []
|
||||||
|
|
||||||
if (event.ctrlKey) { combi.push('Ctrl') }
|
if (event.ctrlKey) { combi.push('Ctrl') }
|
||||||
if (event.metaKey) { combi.push('Command') }
|
if (event.metaKey) { combi.push('Command') }
|
||||||
|
@ -31,7 +31,7 @@ export function keyComboFromEvent (event) {
|
||||||
if (event.shiftKey) { combi.push('Shift') }
|
if (event.shiftKey) { combi.push('Shift') }
|
||||||
|
|
||||||
const keyName = nameFromKeyCode(event.which)
|
const keyName = nameFromKeyCode(event.which)
|
||||||
if (!metaCodes[keyName]) { // prevent output like 'Ctrl+Ctrl'
|
if (!metaCodes[keyName]) { // prevent output like 'Ctrl+Ctrl'
|
||||||
combi.push(keyName)
|
combi.push(keyName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +47,10 @@ export function keyComboFromEvent (event) {
|
||||||
export function createFindKeyBinding (keyBindings) {
|
export function createFindKeyBinding (keyBindings) {
|
||||||
// turn the map with key bindings by name (multiple per binding) into a map by key combo
|
// turn the map with key bindings by name (multiple per binding) into a map by key combo
|
||||||
const keyCombos = {}
|
const keyCombos = {}
|
||||||
Object.keys(keyBindings).forEach ((name) => {
|
Object.keys(keyBindings).forEach((name) => {
|
||||||
keyBindings[name].forEach(combo => keyCombos[normalizeKeyCombo(combo)] = name)
|
keyBindings[name].forEach(combo => {
|
||||||
|
keyCombos[normalizeKeyCombo(combo)] = name
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return function findKeyBinding (event) {
|
return function findKeyBinding (event) {
|
||||||
|
@ -81,112 +83,112 @@ function normalizeKeyCombo (combo) {
|
||||||
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
||||||
|
|
||||||
const metaCodes = {
|
const metaCodes = {
|
||||||
'Ctrl': true,
|
Ctrl: true,
|
||||||
'Command': true,
|
Command: true,
|
||||||
'Alt': true,
|
Alt: true,
|
||||||
'Option': true,
|
Option: true,
|
||||||
'Shift': true
|
Shift: true
|
||||||
}
|
}
|
||||||
|
|
||||||
const codes = {
|
const codes = {
|
||||||
'8': 'Backspace',
|
8: 'Backspace',
|
||||||
'9': 'Tab',
|
9: 'Tab',
|
||||||
'13': 'Enter',
|
13: 'Enter',
|
||||||
'16': 'Shift',
|
16: 'Shift',
|
||||||
'17': 'Ctrl',
|
17: 'Ctrl',
|
||||||
'18': 'Alt',
|
18: 'Alt',
|
||||||
'19': 'Pause_Break',
|
19: 'Pause_Break',
|
||||||
'20': 'Caps_Lock',
|
20: 'Caps_Lock',
|
||||||
'27': 'Escape',
|
27: 'Escape',
|
||||||
'33': 'Page_Up',
|
33: 'Page_Up',
|
||||||
'34': 'Page_Down',
|
34: 'Page_Down',
|
||||||
'35': 'End',
|
35: 'End',
|
||||||
'36': 'Home',
|
36: 'Home',
|
||||||
'37': 'Left',
|
37: 'Left',
|
||||||
'38': 'Up',
|
38: 'Up',
|
||||||
'39': 'Right',
|
39: 'Right',
|
||||||
'40': 'Down',
|
40: 'Down',
|
||||||
'45': 'Insert',
|
45: 'Insert',
|
||||||
'46': 'Delete',
|
46: 'Delete',
|
||||||
'48': '0',
|
48: '0',
|
||||||
'49': '1',
|
49: '1',
|
||||||
'50': '2',
|
50: '2',
|
||||||
'51': '3',
|
51: '3',
|
||||||
'52': '4',
|
52: '4',
|
||||||
'53': '5',
|
53: '5',
|
||||||
'54': '6',
|
54: '6',
|
||||||
'55': '7',
|
55: '7',
|
||||||
'56': '8',
|
56: '8',
|
||||||
'57': '9',
|
57: '9',
|
||||||
'65': 'A',
|
65: 'A',
|
||||||
'66': 'B',
|
66: 'B',
|
||||||
'67': 'C',
|
67: 'C',
|
||||||
'68': 'D',
|
68: 'D',
|
||||||
'69': 'E',
|
69: 'E',
|
||||||
'70': 'F',
|
70: 'F',
|
||||||
'71': 'G',
|
71: 'G',
|
||||||
'72': 'H',
|
72: 'H',
|
||||||
'73': 'I',
|
73: 'I',
|
||||||
'74': 'J',
|
74: 'J',
|
||||||
'75': 'K',
|
75: 'K',
|
||||||
'76': 'L',
|
76: 'L',
|
||||||
'77': 'M',
|
77: 'M',
|
||||||
'78': 'N',
|
78: 'N',
|
||||||
'79': 'O',
|
79: 'O',
|
||||||
'80': 'P',
|
80: 'P',
|
||||||
'81': 'Q',
|
81: 'Q',
|
||||||
'82': 'R',
|
82: 'R',
|
||||||
'83': 'S',
|
83: 'S',
|
||||||
'84': 'T',
|
84: 'T',
|
||||||
'85': 'U',
|
85: 'U',
|
||||||
'86': 'V',
|
86: 'V',
|
||||||
'87': 'W',
|
87: 'W',
|
||||||
'88': 'X',
|
88: 'X',
|
||||||
'89': 'Y',
|
89: 'Y',
|
||||||
'90': 'Z',
|
90: 'Z',
|
||||||
'91': 'Left_Window_Key',
|
91: 'Left_Window_Key',
|
||||||
'92': 'Right_Window_Key',
|
92: 'Right_Window_Key',
|
||||||
'93': 'Select_Key',
|
93: 'Select_Key',
|
||||||
'96': 'Numpad_0',
|
96: 'Numpad_0',
|
||||||
'97': 'Numpad_1',
|
97: 'Numpad_1',
|
||||||
'98': 'Numpad_2',
|
98: 'Numpad_2',
|
||||||
'99': 'Numpad_3',
|
99: 'Numpad_3',
|
||||||
'100': 'Numpad_4',
|
100: 'Numpad_4',
|
||||||
'101': 'Numpad_5',
|
101: 'Numpad_5',
|
||||||
'102': 'Numpad_6',
|
102: 'Numpad_6',
|
||||||
'103': 'Numpad_7',
|
103: 'Numpad_7',
|
||||||
'104': 'Numpad_8',
|
104: 'Numpad_8',
|
||||||
'105': 'Numpad_9',
|
105: 'Numpad_9',
|
||||||
'106': 'Numpad_*',
|
106: 'Numpad_*',
|
||||||
'107': 'Numpad_+',
|
107: 'Numpad_+',
|
||||||
'109': 'Numpad_-',
|
109: 'Numpad_-',
|
||||||
'110': 'Numpad_.',
|
110: 'Numpad_.',
|
||||||
'111': 'Numpad_/',
|
111: 'Numpad_/',
|
||||||
'112': 'F1',
|
112: 'F1',
|
||||||
'113': 'F2',
|
113: 'F2',
|
||||||
'114': 'F3',
|
114: 'F3',
|
||||||
'115': 'F4',
|
115: 'F4',
|
||||||
'116': 'F5',
|
116: 'F5',
|
||||||
'117': 'F6',
|
117: 'F6',
|
||||||
'118': 'F7',
|
118: 'F7',
|
||||||
'119': 'F8',
|
119: 'F8',
|
||||||
'120': 'F9',
|
120: 'F9',
|
||||||
'121': 'F10',
|
121: 'F10',
|
||||||
'122': 'F11',
|
122: 'F11',
|
||||||
'123': 'F12',
|
123: 'F12',
|
||||||
'144': 'Num_Lock',
|
144: 'Num_Lock',
|
||||||
'145': 'Scroll_Lock',
|
145: 'Scroll_Lock',
|
||||||
'186': ';',
|
186: ';',
|
||||||
'187': '=',
|
187: '=',
|
||||||
'188': ',',
|
188: ',',
|
||||||
'189': '-',
|
189: '-',
|
||||||
'190': '.',
|
190: '.',
|
||||||
'191': '/',
|
191: '/',
|
||||||
'192': '`',
|
192: '`',
|
||||||
'219': '[',
|
219: '[',
|
||||||
'220': '\\',
|
220: '\\',
|
||||||
'221': ']',
|
221: ']',
|
||||||
'222': '\''
|
222: '\''
|
||||||
}
|
}
|
||||||
|
|
||||||
// all secondary characters of the keyboard buttons (used via Shift)
|
// all secondary characters of the keyboard buttons (used via Shift)
|
||||||
|
@ -195,14 +197,14 @@ const aliases = {
|
||||||
'!': '1',
|
'!': '1',
|
||||||
'@': '2',
|
'@': '2',
|
||||||
'#': '3',
|
'#': '3',
|
||||||
'$': '4',
|
$: '4',
|
||||||
'%': '5',
|
'%': '5',
|
||||||
'^': '6',
|
'^': '6',
|
||||||
'&': '7',
|
'&': '7',
|
||||||
'*': '8',
|
'*': '8',
|
||||||
'(': '9',
|
'(': '9',
|
||||||
')': '0',
|
')': '0',
|
||||||
'_': '-',
|
_: '-',
|
||||||
'+': '=',
|
'+': '=',
|
||||||
'{': '[',
|
'{': '[',
|
||||||
'}': ']',
|
'}': ']',
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stringify a path like
|
* Stringify a path like
|
||||||
*
|
*
|
||||||
* ["data", 2, "nested", "property"]
|
* ["data", 2, "nested", "property"]
|
||||||
*
|
*
|
||||||
* into a string:
|
* into a string:
|
||||||
*
|
*
|
||||||
* ".data[2].nested.property"
|
* ".data[2].nested.property"
|
||||||
*
|
*
|
||||||
* @param {Path}
|
* @param {Path}
|
||||||
* @return {string}
|
* @return {string}
|
||||||
*/
|
*/
|
||||||
export function stringifyPath (path) {
|
export function stringifyPath (path) {
|
||||||
return path.map(prop => {
|
return path.map(prop => {
|
||||||
return typeof prop === 'number'
|
return typeof prop === 'number'
|
||||||
? `[${prop}]`
|
? `[${prop}]`
|
||||||
: `.${prop}`
|
: `.${prop}`
|
||||||
}).join('')
|
}).join('')
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { stringifyPath } from './pathUtils.js'
|
||||||
|
|
||||||
describe('pathUtils', () => {
|
describe('pathUtils', () => {
|
||||||
it('stringifyPath', () => {
|
it('stringifyPath', () => {
|
||||||
assert.strictEqual(stringifyPath(["data", 2, "nested", "property"]), '.data[2].nested.property')
|
assert.strictEqual(stringifyPath(['data', 2, 'nested', 'property']), '.data[2].nested.property')
|
||||||
assert.strictEqual(stringifyPath(['']), '.')
|
assert.strictEqual(stringifyPath(['']), '.')
|
||||||
assert.strictEqual(stringifyPath([]), '')
|
assert.strictEqual(stringifyPath([]), '')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -9,13 +9,13 @@ import {
|
||||||
|
|
||||||
describe('stringUtils', () => {
|
describe('stringUtils', () => {
|
||||||
it('findUniqueName', () => {
|
it('findUniqueName', () => {
|
||||||
assert.deepStrictEqual(findUniqueName('other', {'a': true, 'b': true, 'c': true}), 'other')
|
assert.deepStrictEqual(findUniqueName('other', { a: true, b: true, c: true }), 'other')
|
||||||
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true}), 'b (copy)')
|
assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true }), 'b (copy)')
|
||||||
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true, 'b (copy)': true}), 'b (copy 2)')
|
assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true, 'b (copy)': true }), 'b (copy 2)')
|
||||||
assert.deepStrictEqual(findUniqueName('b (copy)', {'a': true, 'b': true, 'c': true, 'b (copy)': true}), 'b (copy 2)')
|
assert.deepStrictEqual(findUniqueName('b (copy)', { a: true, b: true, c: true, 'b (copy)': true }), 'b (copy 2)')
|
||||||
assert.deepStrictEqual(findUniqueName('b', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)')
|
assert.deepStrictEqual(findUniqueName('b', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
|
||||||
assert.deepStrictEqual(findUniqueName('b (copy)', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)')
|
assert.deepStrictEqual(findUniqueName('b (copy)', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
|
||||||
assert.deepStrictEqual(findUniqueName('b (copy 2)', {'a': true, 'b': true, 'c': true, 'b (copy)': true, 'b (copy 2)': true}), 'b (copy 3)')
|
assert.deepStrictEqual(findUniqueName('b (copy 2)', { a: true, b: true, c: true, 'b (copy)': true, 'b (copy 2)': true }), 'b (copy 3)')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toCapital', () => {
|
it('toCapital', () => {
|
||||||
|
|
|
@ -41,7 +41,7 @@ export function isObjectOrArray (value) {
|
||||||
* @param {*} value
|
* @param {*} value
|
||||||
* @return {String} type
|
* @return {String} type
|
||||||
*/
|
*/
|
||||||
export function valueType(value) {
|
export function valueType (value) {
|
||||||
if (value === null) {
|
if (value === null) {
|
||||||
return 'null'
|
return 'null'
|
||||||
}
|
}
|
||||||
|
@ -85,25 +85,20 @@ export function isUrl (text) {
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
export function stringConvert (str) {
|
export function stringConvert (str) {
|
||||||
const num = Number(str) // will nicely fail with '123ab'
|
const num = Number(str) // will nicely fail with '123ab'
|
||||||
const numFloat = parseFloat(str) // will nicely fail with ' '
|
const numFloat = parseFloat(str) // will nicely fail with ' '
|
||||||
|
|
||||||
if (str === '') {
|
if (str === '') {
|
||||||
return ''
|
return ''
|
||||||
}
|
} else if (str === 'null') {
|
||||||
else if (str === 'null') {
|
|
||||||
return null
|
return null
|
||||||
}
|
} else if (str === 'true') {
|
||||||
else if (str === 'true') {
|
|
||||||
return true
|
return true
|
||||||
}
|
} else if (str === 'false') {
|
||||||
else if (str === 'false') {
|
|
||||||
return false
|
return false
|
||||||
}
|
} else if (!isNaN(num) && !isNaN(numFloat)) {
|
||||||
else if (!isNaN(num) && !isNaN(numFloat)) {
|
|
||||||
return num
|
return num
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
// TODO: write unit tests
|
// TODO: write unit tests
|
||||||
|
|
Loading…
Reference in New Issue