UNPKG

substance

Version:

Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing systems.

1,937 lines (1,740 loc) 3.04 MB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : (factory()); }(this, (function () { 'use strict'; /* Implements Substance ChangeStore API. This is just a dumb store. No integrity checks are made, as this is the task of DocumentEngine */ function forEach(iteratee, func) { if (!iteratee) return if (iteratee.constructor.prototype.forEach) { iteratee.forEach(func); } else { Object.keys(iteratee).forEach(function(key) { return func(iteratee[key], key) }); } } function isObject(val) { const type = typeof val; return Boolean(val) && (type === 'object' || type === 'function') } /** Event support. */ class EventEmitter { /** Emit an event. @param {String} event @param ...arguments @return true if a listener was notified, false otherwise. */ emit(event) { if (event in this.__events__) { // console.log("Emitting event %s (%d listeners) on", event, this.__events__[event].length, this) // Clone the list of bindings so that handlers can remove or add handlers during the call. var bindings = this.__events__[event].slice(); var args = Array.prototype.slice.call(arguments, 1); for (var i = 0, len = bindings.length; i < len; i++) { var binding = bindings[i]; // console.log("- triggering %s on %s", event, binding.context.constructor.name) binding.method.apply(binding.context, args); } return true } return false } /** Subscribe a listener to an event. Optionally, a `priority` can be provided to control the order of all bindings. The default priority is 0. All listeners with the same priority remain in order of registration. A lower priority will make the listener be called later, a higher priority earlier. @param {String} event @param {Function} method @param {Object} context @param {Object} options */ on(event, method, context, options) { var priority = 0; if (arguments.length === 4) { priority = options.priority || priority; } _on.call(this, event, method, context, priority); this.__events__[event].sort(byPriorityDescending); } /** Unsubscrive a listener from an event. @param {String} event @param {Function} method @param {Object} context @param {Object} options */ off(event, method, context) { // eslint-disable-line no-unused-vars if (arguments.length === 1 && isObject(arguments[0])) { _disconnect.call(this, arguments[0]); } else { _off.apply(this, arguments); } } _debugEvents() { /* eslint-disable no-console */ console.log('### EventEmitter: ', this); forEach(this.__events__, function(handlers, name) { console.log("- %s listeners for %s: ", handlers.length, name, handlers); }); /* eslint-enable no-console */ } get __events__() { if (!this.___events___) { this.___events___ = {}; } return this.___events___ } } // sort descending as a listener with higher priority should be // called earlier function byPriorityDescending(a, b) { return b.priority - a.priority } /* Internal implementation for registering a listener. @param {String} event @param {Function} method @param {Object} context */ function _on(event, method, context, priority) { /* eslint-disable no-invalid-this */ var bindings; validateMethod( method, context ); if (this.__events__.hasOwnProperty(event)) { bindings = this.__events__[event]; } else { // Auto-initialize bindings list bindings = this.__events__[event] = []; } // Add binding bindings.push({ method: method, context: context || null, priority: priority }); return this /*eslint-enable no-invalid-this */ } /* Remove a listener. @param {String} event @param {Function} method @param {Object} context */ function _off(event, method, context) { /* eslint-disable no-invalid-this */ var i, bindings; if ( arguments.length === 1 ) { // Remove all bindings for event delete this.__events__[event]; return this } validateMethod( method, context ); if ( !( event in this.__events__ ) || !this.__events__[event].length ) { // No matching bindings return this } // Default to null context if ( arguments.length < 3 ) { context = null; } // Remove matching handlers bindings = this.__events__[event]; i = bindings.length; while ( i-- ) { if ( bindings[i].method === method && bindings[i].context === context ) { bindings.splice( i, 1 ); } } // Cleanup if now empty if ( bindings.length === 0 ) { delete this.__events__[event]; } return this /* eslint-enable no-invalid-this */ } /** Internal implementation of disconnect. */ function _disconnect(context) { /* eslint-disable no-invalid-this */ // Remove all connections to the context forEach(this.__events__, function(bindings, event) { for (var i = bindings.length-1; i>=0; i--) { // bindings[i] may have been removed by the previous steps // so check it still exists if (bindings[i] && bindings[i].context === context) { _off.call(this, event, bindings[i].method, context); } } }.bind(this)); return this /* eslint-enable no-invalid-this */ } function validateMethod(method, context) { // Validate method and context if (typeof method === 'string') { // Validate method if (context === undefined || context === null) { throw new Error( 'Method name "' + method + '" has no context.' ) } if (!(method in context)) { // Technically the method does not need to exist yet: it could be // added before call time. But this probably signals a typo. throw new Error( 'Method not found: "' + method + '"' ) } if (typeof context[method] !== 'function') { // Technically the property could be replaced by a function before // call time. But this probably signals a typo. throw new Error( 'Property "' + method + '" is not a function' ) } } else if (typeof method !== 'function') { throw new Error( 'Invalid callback. Function or method name expected.' ) } } /* Custom error object for all Substance related errors @example ```js import Err from 'substance/util/SubstanceError' throw new Err('Document.SelectionUpdateError', {message: 'Could not update selection.'}) ``` For better inspection allows you to pass a cause (the error that caused the error). That way we can attach context information on each level and we can also ensure security, by not passing the cause-chain to the client. Resources: http://www.bennadel.com/blog/2828-creating-custom-error-objects-in-node-js-with-error-capturestacktrace.htm https://gist.github.com/justmoon/15511f92e5216fa2624b https://github.com/davepacheco/node-verror/blob/master/lib/verror.js */ function extend(...args) { return Object.assign(...args) } function map(iteratee, func) { if (!iteratee) return [] if (!func) func = function(item) { return item }; if (Array.isArray(iteratee)) { return iteratee.map(func) } else if (typeof iteratee.length !== 'undefined') { let l = iteratee.length; let result = []; for (var i = 0; i < l; i++) { result.push(func(iteratee[i], i)); } return result } else { return Object.keys(iteratee).map(function(key) { return func(iteratee[key], key) }) } } function isPlainObject(o) { return Boolean(o) && o.constructor === {}.constructor } function isArray(a) { return Array.isArray(a) } function isEqual(a, b) { if (a === b) return true if (isArray(a) && isArray(b)) { if (a.length !== b.length) return false for (let i = 0; i < a.length; i++) { if (!isEqual(a[i], b[i])) return false } return true } if (isPlainObject(a) && isPlainObject(b)) { let akeys = Object.keys(a).sort(); let bkeys = Object.keys(b).sort(); if (!isEqual(akeys, bkeys)) return false for (let i = 0; i < akeys.length; i++) { let key = akeys[i]; if (!isEqual(a[key], b[key])) return false } return true } return false } function clone(val) { if (isArray(val)) { return val.slice(0) } if (isObject(val)) { return Object.assign({}, val) } // we do not clone primitives // TODO: is that ok? return val } function cloneDeep(val) { if (isArray(val)) { return _cloneArrayDeep(val); } if (isObject(val)) { return _cloneObjectDeep(val) } // primitives don't need to be cloned // TODO: is that ok? return val } function _cloneObjectDeep(obj) { let res = {}; forEach(obj, (val, key) => { res[key] = cloneDeep(val); }); return res } function _cloneArrayDeep(arr) { return arr.map(cloneDeep) } /*! Math.uuid.js (v1.4) http://www.broofa.com mailto:robert@broofa.com Copyright (c) 2010 Robert Kieffer Dual licensed under the MIT and GPL licenses. */ /** * Generates a unique id. * * @param {String} [prefix] if provided the UUID will be prefixed. * @param {Number} [len] if provided a UUID with given length will be created. * @return A generated uuid. */ function uuid(prefix, len) { if (prefix && prefix[prefix.length-1] !== "-") { prefix = prefix.concat("-"); } var chars = '0123456789abcdefghijklmnopqrstuvwxyz'.split(''); var uuid = []; var radix = 16; var idx; len = len || 32; if (len) { // Compact form for (idx = 0; idx < len; idx++) uuid[idx] = chars[0 | Math.random()*radix]; } else { // rfc4122, version 4 form var r; // rfc4122 requires these characters uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4'; // Fill in random data. At i==19 set the high bits of clock sequence as // per rfc4122, sec. 4.1.5 for (idx = 0; idx < 36; idx++) { if (!uuid[idx]) { r = 0 | Math.random()*16; uuid[idx] = chars[(idx === 19) ? (r & 0x3) | 0x8 : r]; } } } return (prefix ? prefix : "") + uuid.join(''); } function isNumber(n) { return typeof n === 'number' } function isNil(o) { return o === null || o === undefined } function isString(s) { return typeof s === 'string' } /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @type {Function} * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, * else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray$1 = Array.isArray; /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/6.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject$1(value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); } /** `Object#toString` result references. */ var funcTag = '[object Function]'; var genTag = '[object GeneratorFunction]'; /** Used for built-in method references. */ var objectProto$1 = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto$1.toString; /** * Checks if `value` is classified as a `Function` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, * else `false`. * @example * * _.isFunction(_); * // => true * * _.isFunction(/abc/); * // => false */ function isFunction(value) { // The use of `Object#toString` avoids issues with the `typeof` operator // in Safari 8 which returns 'object' for typed array and weak map constructors, // and PhantomJS 1.9 which returns 'function' for `NodeList` instances. var tag = isObject$1(value) ? objectToString.call(value) : ''; return tag == funcTag || tag == genTag; } /** * Checks if `value` is a host object in IE < 9. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a host object, else `false`. */ function isHostObject(value) { // Many host objects are `Object` objects that can coerce to strings // despite having improperly defined `toString` methods. var result = false; if (value != null && typeof value.toString != 'function') { try { result = !!(value + ''); } catch (e) {} } return result; } /** * Checks if `value` is a global object. * * @private * @param {*} value The value to check. * @returns {null|Object} Returns `value` if it's a global object, else `null`. */ function checkGlobal(value) { return (value && value.Object === Object) ? value : null; } /** Detect free variable `global` from Node.js. */ var freeGlobal = checkGlobal(typeof global == 'object' && global); /** Detect free variable `self`. */ var freeSelf = checkGlobal(typeof self == 'object' && self); /** Detect `this` as the global object. */ var thisGlobal = checkGlobal(typeof undefined == 'object' && undefined); /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || thisGlobal || Function('return this')(); /** Used to detect overreaching core-js shims. */ var coreJsData = root['__core-js_shared__']; /** Used to detect methods masquerading as native. */ var maskSrcKey = (function() { var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || ''); return uid ? ('Symbol(src)_1.' + uid) : ''; }()); /** * Checks if `func` has its source masked. * * @private * @param {Function} func The function to check. * @returns {boolean} Returns `true` if `func` is masked, else `false`. */ function isMasked(func) { return !!maskSrcKey && (maskSrcKey in func); } /** Used to resolve the decompiled source of functions. */ var funcToString$1 = Function.prototype.toString; /** * Converts `func` to its source code. * * @private * @param {Function} func The function to process. * @returns {string} Returns the source code. */ function toSource(func) { if (func != null) { try { return funcToString$1.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/6.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used for built-in method references. */ var objectProto = Object.prototype; /** Used to resolve the decompiled source of functions. */ var funcToString = Function.prototype.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** * The base implementation of `_.isNative` without bad shim checks. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a native function, * else `false`. */ function baseIsNative(value) { if (!isObject$1(value) || isMasked(value)) { return false; } var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * Gets the value at `key` of `object`. * * @private * @param {Object} [object] The object to query. * @param {string} key The key of the property to get. * @returns {*} Returns the property value. */ function getValue(object, key) { return object == null ? undefined : object[key]; } /** * Gets the native function at `key` of `object`. * * @private * @param {Object} object The object to query. * @param {string} key The key of the method to get. * @returns {*} Returns the function if it's native, else `undefined`. */ function getNative(object, key) { var value = getValue(object, key); return baseIsNative(value) ? value : undefined; } /* Built-in method references that are verified to be native. */ var nativeCreate = getNative(Object, 'create'); /** * Removes all key-value entries from the hash. * * @private * @name clear * @memberOf Hash */ function hashClear() { this.__data__ = nativeCreate ? nativeCreate(null) : {}; } /** * Removes `key` and its value from the hash. * * @private * @name delete * @memberOf Hash * @param {Object} hash The hash to modify. * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function hashDelete(key) { return this.has(key) && delete this.__data__[key]; } /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used for built-in method references. */ var objectProto$2 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$1 = objectProto$2.hasOwnProperty; /** * Gets the hash value for `key`. * * @private * @name get * @memberOf Hash * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function hashGet(key) { var data = this.__data__; if (nativeCreate) { var result = data[key]; return result === HASH_UNDEFINED ? undefined : result; } return hasOwnProperty$1.call(data, key) ? data[key] : undefined; } /** Used for built-in method references. */ var objectProto$3 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$2 = objectProto$3.hasOwnProperty; /** * Checks if a hash value for `key` exists. * * @private * @name has * @memberOf Hash * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function hashHas(key) { var data = this.__data__; return nativeCreate ? data[key] !== undefined : hasOwnProperty$2.call(data, key); } /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED$1 = '__lodash_hash_undefined__'; /** * Sets the hash `key` to `value`. * * @private * @name set * @memberOf Hash * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the hash instance. */ function hashSet(key, value) { var data = this.__data__; data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED$1 : value; return this; } /** * Creates a hash object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Hash(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } // Add methods to `Hash`. Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ function listCacheClear() { this.__data__ = []; } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) * comparison between two values to determine if they are equivalent. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to compare. * @param {*} other The other value to compare. * @returns {boolean} Returns `true` if the values are equivalent, else `false`. * @example * * var object = { 'user': 'fred' }; * var other = { 'user': 'fred' }; * * _.eq(object, object); * // => true * * _.eq(object, other); * // => false * * _.eq('a', 'a'); * // => true * * _.eq('a', Object('a')); * // => false * * _.eq(NaN, NaN); * // => true */ function eq(value, other) { return value === other || (value !== value && other !== other); } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to search. * @param {*} key The key to search for. * @returns {number} Returns the index of the matched value, else `-1`. */ function assocIndexOf(array, key) { var length = array.length; while (length--) { if (eq(array[length][0], key)) { return length; } } return -1; } /** Used for built-in method references. */ var arrayProto = Array.prototype; /** Built-in value references. */ var splice = arrayProto.splice; /** * Removes `key` and its value from the list cache. * * @private * @name delete * @memberOf ListCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function listCacheDelete(key) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { return false; } var lastIndex = data.length - 1; if (index == lastIndex) { data.pop(); } else { splice.call(data, index, 1); } return true; } /** * Gets the list cache value for `key`. * * @private * @name get * @memberOf ListCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function listCacheGet(key) { var data = this.__data__, index = assocIndexOf(data, key); return index < 0 ? undefined : data[index][1]; } /** * Checks if a list cache value for `key` exists. * * @private * @name has * @memberOf ListCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function listCacheHas(key) { return assocIndexOf(this.__data__, key) > -1; } /** * Sets the list cache `key` to `value`. * * @private * @name set * @memberOf ListCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the list cache instance. */ function listCacheSet(key, value) { var data = this.__data__, index = assocIndexOf(data, key); if (index < 0) { data.push([key, value]); } else { data[index][1] = value; } return this; } /** * Creates an list cache object. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function ListCache(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /* Built-in method references that are verified to be native. */ var Map$1 = getNative(root, 'Map'); /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ function mapCacheClear() { this.__data__ = { 'hash': new Hash, 'map': new (Map$1 || ListCache), 'string': new Hash }; } /** * Checks if `value` is suitable for use as unique object key. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is suitable, else `false`. */ function isKeyable(value) { var type = typeof value; return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean') ? (value !== '__proto__') : (value === null); } /** * Gets the data for `map`. * * @private * @param {Object} map The map to query. * @param {string} key The reference key. * @returns {*} Returns the map data. */ function getMapData(map, key) { var data = map.__data__; return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map; } /** * Removes `key` and its value from the map. * * @private * @name delete * @memberOf MapCache * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function mapCacheDelete(key) { return getMapData(this, key)['delete'](key); } /** * Gets the map value for `key`. * * @private * @name get * @memberOf MapCache * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function mapCacheGet(key) { return getMapData(this, key).get(key); } /** * Checks if a map value for `key` exists. * * @private * @name has * @memberOf MapCache * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function mapCacheHas(key) { return getMapData(this, key).has(key); } /** * Sets the map `key` to `value`. * * @private * @name set * @memberOf MapCache * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the map cache instance. */ function mapCacheSet(key, value) { getMapData(this, key).set(key, value); return this; } /** * Creates a map cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function MapCache(entries) { var index = -1, length = entries ? entries.length : 0; this.clear(); while (++index < length) { var entry = entries[index]; this.set(entry[0], entry[1]); } } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /** Used as the `TypeError` message for "Functions" methods. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** * Creates a function that memoizes the result of `func`. If `resolver` is * provided, it determines the cache key for storing the result based on the * arguments provided to the memoized function. By default, the first argument * provided to the memoized function is used as the map cache key. The `func` * is invoked with the `this` binding of the memoized function. * * **Note:** The cache is exposed as the `cache` property on the memoized * function. Its creation may be customized by replacing the `_.memoize.Cache` * constructor with one whose instances implement the * [`Map`](http://ecma-international.org/ecma-262/6.0/#sec-properties-of-the-map-prototype-object) * method interface of `delete`, `get`, `has`, and `set`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to have its output memoized. * @param {Function} [resolver] The function to resolve the cache key. * @returns {Function} Returns the new memoized function. * @example * * var object = { 'a': 1, 'b': 2 }; * var other = { 'c': 3, 'd': 4 }; * * var values = _.memoize(_.values); * values(object); * // => [1, 2] * * values(other); * // => [3, 4] * * object.a = 2; * values(object); * // => [1, 2] * * // Modify the result cache. * values.cache.set(object, ['a', 'b']); * values(object); * // => ['a', 'b'] * * // Replace `_.memoize.Cache`. * _.memoize.Cache = WeakMap; */ function memoize(func, resolver) { if (typeof func != 'function' || (resolver && typeof resolver != 'function')) { throw new TypeError(FUNC_ERROR_TEXT); } var memoized = function() { var args = arguments, key = resolver ? resolver.apply(this, args) : args[0], cache = memoized.cache; if (cache.has(key)) { return cache.get(key); } var result = func.apply(this, args); memoized.cache = cache.set(key, result); return result; }; memoized.cache = new (memoize.Cache || MapCache); return memoized; } // Assign cache to `_.memoize`. memoize.Cache = MapCache; /** Built-in value references. */ var Symbol$1 = root.Symbol; /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return !!value && typeof value == 'object'; } /** `Object#toString` result references. */ var symbolTag = '[object Symbol]'; /** Used for built-in method references. */ var objectProto$4 = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring) * of values. */ var objectToString$1 = objectProto$4.toString; /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is correctly classified, * else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && objectToString$1.call(value) == symbolTag); } /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0; /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined; var symbolToString = symbolProto ? symbolProto.toString : undefined; /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') { return value; } if (isSymbol(value)) { return symbolToString ? symbolToString.call(value) : ''; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {string} Returns the string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ function toString(value) { return value == null ? '' : baseToString(value); } /** Used to match property names within property paths. */ var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(\.|\[\])(?:\4|$))/g; /** Used to match backslashes in property paths. */ var reEscapeChar = /\\(\\)?/g; /** * Converts `string` to a property path array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the property path array. */ var stringToPath = memoize(function(string) { var result = []; toString(string).replace(rePropName, function(match, number, quote, string) { result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); }); return result; }); /** * Casts `value` to a path array if it's not one. * * @private * @param {*} value The value to inspect. * @returns {Array} Returns the cast property path array. */ function castPath(value) { return isArray$1(value) ? value : stringToPath(value); } /** Used to match property names within property paths. */ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/; var reIsPlainProp = /^\w*$/; /** * Checks if `value` is a property name and not a property path. * * @private * @param {*} value The value to check. * @param {Object} [object] The object to query keys on. * @returns {boolean} Returns `true` if `value` is a property name, else `false`. */ function isKey(value, object) { if (isArray$1(value)) { return false; } var type = typeof value; if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol(value)) { return true; } return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || (object != null && value in Object(object)); } /** Used as references for various `Number` constants. */ var INFINITY$1 = 1 / 0; /** * Converts `value` to a string key if it's not a string or symbol. * * @private * @param {*} value The value to inspect. * @returns {string|symbol} Returns the key. */ function toKey(value) { if (typeof value == 'string' || isSymbol(value)) { return value; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result; } /** * The base implementation of `_.get` without support for default values. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @returns {*} Returns the resolved value. */ function baseGet(object, path) { path = isKey(path, object) ? [path] : castPath(path); var index = 0, length = path.length; while (object != null && index < length) { object = object[toKey(path[index++])]; } return (index && index == length) ? object : undefined; } /** * Gets the value at `path` of `object`. If the resolved value is * `undefined`, the `defaultValue` is used in its place. * * @static * @memberOf _ * @since 3.7.0 * @category Object * @param {Object} object The object to query. * @param {Array|string} path The path of the property to get. * @param {*} [defaultValue] The value returned for `undefined` resolved values. * @returns {*} Returns the resolved value. * @example * * var object = { 'a': [{ 'b': { 'c': 3 } }] }; * * _.get(object, 'a[0].b.c'); * // => 3 * * _.get(object, ['a', '0', 'b', 'c']); * // => 3 * * _.get(object, 'a.b.c', 'default'); * // => 'default' */ function get(object, path, defaultValue) { var result = object == null ? undefined : baseGet(object, path); return result === undefined ? defaultValue : result; } /** Used for built-in method references. */ var objectProto$5 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$3 = objectProto$5.hasOwnProperty; /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) * for equality comparisons. * * @private * @param {Object} object The object to modify. * @param {string} key The key of the property to assign. * @param {*} value The value to assign. */ function assignValue(object, key, value) { var objValue = object[key]; if (!(hasOwnProperty$3.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { object[key] = value; } } /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER = 9007199254740991; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** * Checks if `value` is a valid array-like index. * * @private * @param {*} value The value to check. * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. */ function isIndex(value, length) { length = length == null ? MAX_SAFE_INTEGER : length; return !!length && (typeof value == 'number' || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); } /** * The base implementation of `_.set`. * * @private * @param {Object} object The object to query. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize path creation. * @returns {Object} Returns `object`. */ function baseSet(object, path, value, customizer) { path = isKey(path, object) ? [path] : castPath(path); var index = -1, length = path.length, lastIndex = length - 1, nested = object; while (nested != null && ++index < length) { var key = toKey(path[index]); if (isObject$1(nested)) { var newValue = value; if (index != lastIndex) { var objValue = nested[key]; newValue = customizer ? customizer(objValue, key, nested) : undefined; if (newValue === undefined) { newValue = objValue == null ? (isIndex(path[index + 1]) ? [] : {}) : objValue; } } assignValue(nested, key, newValue); } nested = nested[key]; } return object; } /** * This method is like `_.set` except that it accepts `customizer` which is * invoked to produce the objects of `path`. If `customizer` returns `undefined` * path creation is handled by the method instead. The `customizer` is invoked * with three arguments: (nsValue, key, nsObject). * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to set. * @param {*} value The value to set. * @param {Function} [customizer] The function to customize assigned values. * @returns {Object} Returns `object`. * @example * * var object = {}; * * _.setWith(object, '[0][1]', 'a', Object); * // => { '0': { '1': 'a' } } */ function setWith(object, path, value, customizer) { customizer = typeof customizer == 'function' ? customizer : undefined; return object == null ? object : baseSet(object, path, value, customizer); } /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeGetPrototype = Object.getPrototypeOf; /** * Gets the `[[Prototype]]` of `value`. * * @private * @param {*} value The value to query. * @returns {null|Object} Returns the `[[Prototype]]`. */ function getPrototype(value) { return nativeGetPrototype(Object(value)); } /** Used for built-in method references. */ var objectProto$6 = Object.prototype; /** Used to check objects for own properties. */ var hasOwnProperty$4 = objectProto$6.hasOwnProperty; /** * The base implementation of `_.has` without support for deep paths. * * @private * @param {Object} [object] The object to query. * @param {Array|string} key The key to check. * @returns {boolean} Returns `true` if `key` exists, else `false`. */ function baseHas(object, key) { // Avoid a bug in IE 10-11 where objects with a [[Prototype]] of `null`, // that are composed entirely of index properties, return `false` for // `hasOwnProperty` checks of them. return object != null && (hasOwnProperty$4.call(object, key) || (typeof object == 'object' && key in object && getPrototype(object) === null)); } /** * Gets the last element of `array`. * * @static * @memberOf _ * @since 0.1.0 * @category Array * @param {Array} array The array to query. * @returns {*} Returns the last element of `array`. * @example * * _.last([1, 2, 3]); * // => 3 */ function last(array) { var length = array ? array.length : 0; return length ? array[length - 1] : undefined; } /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; if (start < 0) { start = -start > length ? 0 : (length + start); } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : ((end - start) >>> 0); start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * Gets the parent value at `path` of `object`. * * @private * @param {Object} object The object to query. * @param {Array} path The path to get the parent value of. * @returns {*} Returns the parent value. */ function parent(object, path) { return path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); } /** * The base implementation of `_.unset`. * * @private * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. */ function baseUnset(object, path) { path = isKey(path, object) ? [path] : castPath(path); object = parent(object, path); var key = toKey(last(path)); return !(object != null && baseHas(object, key)) || delete object[key]; } /** * Removes the property at `path` of `object`. * * **Note:** This method mutates `object`. * * @static * @memberOf _ * @since 4.0.0 * @category Object * @param {Object} object The object to modify. * @param {Array|string} path The path of the property to unset. * @returns {boolean} Returns `true` if the property is deleted, else `false`. * @example * * var object = { 'a': [{ 'b': { 'c': 7 } }] }; * _.unset(object, 'a[0].b.c'); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; * * _.unset(object, ['a', '0', 'b', 'c']); * // => true * * console.log(object); * // => { 'a': [{ 'b': {} }] }; */ function unset(object, path) { return object == null ? true : baseUnset(object, path); } /* An object that can be access via path API. @example var obj = new PathObject({a: "aVal", b: {b1: 'b1Val', b2: 'b2Val'}}) */ class PathObject { /* @param {object} [root] An object to operate on */ constructor(root) { if (root) { this.__root__ = root; } } contains(id) { return Boolean(this.getRoot()[id]) } getRoot() { if (this.__root__) { return this.__root__ } else { return this } } /** Get value at path @return {object} The value stored for a given path @example obj.get(['b', 'b1']) => b1Val */ get(path) { if (!path) { return undefined } if (isString(path)) { return this.getRoot()[path] } if (arguments.length > 1) { path = Array.prototype.slice(arguments, 0); } if (!isArray(path)) { throw new Error('Illegal argument for PathObject.get()') } return get(this.getRoot(), path) } set(path, value) { if (!path) { throw new Error('Illegal argument: PathObject.set(>path<, value) - path is mandatory.') } if (isString(path)) { this.getRoot()[path] = value; } else { setWith(this.getRoot(), path, value); } } delete(path) { if (isString(path)) { delete this.getRoot()[path]; } else if (path.length === 1) { delete this.getRoot()[path[0]]; } else { var success = unset(this.getRoot(), path); if (!success) { throw new Error('Could not delete property at path' + path) } } } clear() { var root = this.getRoot(); for (var key in root) { if (root.hasOwnProperty(key)) { delete root[key]; } } } } PathObject.prototype._isPathObject = true; class Conflict extends Error { constructor(a, b) { super("Conflict: " + JSON.stringify(a) +" vs " + JSON.stringify(b)); this.a = a; this.b = b; } } const INSERT = "insert"; const DELETE$1 = "delete"; class TextOperation { constructor(data) { if (!data || data.type === undefined || data.pos === undefined || data.str === undefined) { throw new Error("Illegal argument: insufficient data.") } // 'insert' or 'delete' this.type = data.type; // the position where to apply the operation this.pos = data.pos; // the string to delete or insert this.str = data.str; // sanity checks if(!this.isInsert() && !this.isDelete()) { throw new Error("Illegal type.") } if (!isString(this.str)) { throw new Error("Illegal argument: expecting string.") } if (!isNumber(this.pos) || this.pos < 0) { throw new Error("Illegal argument: expecting positive number as pos.") } } apply(str) { if (this.isEmpty()) return str if (this.type === INSERT) { if (str.length < this.pos) { throw new Error("Provided string is too short.") } if (str.splice) { return str.splice(this.pos, 0, this.str) } else { return str.slice(0, this.pos).concat(this.str).concat(str.slice(this.pos)) } } else /* if (this.type === DELETE) */ { if (str.length < this.pos + this.str.length) { throw new Error("Provided string is too short.") } if (str.splice) { return str.splice(this.pos, this.str.length) } else { return str.slice(0, this.pos).concat(str.slice(this.pos + this.str.length)) } } } clone() { return new TextOperation(this) } isNOP() { return this.type === "NOP" || this.str.length === 0 } isInsert() { return this.type === INSERT } isDelete() { return this.type === DELETE$1 } getLength() { return this.str.length } invert() { var data = { type: this.isInsert() ? DELETE$1 : INSERT, pos: this.pos, str: this.str }; return new TextOperation(data) } hasConflict(other) { return hasConflict$1(this, other) } isEmpty() { return this.str.length === 0 } toJSON() { return { type: this.type, pos: this.pos, str: this.str } } toString() { return ["(", (this.isInsert() ? INSERT : DELETE$1), ",", this.pos, ",'", this.str, "')"].join('') } } TextOperation.prototype._isOperation = true; TextOperation.prototype._isTextOperation = true; function hasConflict$1(a, b) { // Insert vs Insert: // // Insertions are conflicting iff their insert position is the same. if (a.type === INSERT && b.type === INSERT) return (a.pos === b.pos) // Delete vs Delete: // // Deletions are conflicting if their ranges overlap. if (a.type === DELETE$1 && b.type === DELETE$1) { // to have no conflict, either `a` should be after `b` or `b` after `a`, otherwise. return !(a.pos >= b.pos + b.str.length || b.pos >= a.pos + a.str.length) } // Delete vs Insert: // // A deletion and an insertion are conflicting if the insert position is within the deleted range. var del, ins; if (a.type === DELETE$1) { del = a; ins = b; } else { del = b; ins = a; } return (ins.pos >= del.pos && ins.pos < del.pos + del.str.length) } // Transforms two Insertions // -------- function transform_insert_insert(a, b) { if (a.pos === b.pos) { b.pos += a.str.length; } else if (a.pos < b.pos) { b.pos += a.str.length; } else { a.pos += b.str.length; } } // Transform two Deletions // -------- // function transform_delete_delete$1(a, b, first) { // reduce to a normalized case if (a.pos > b.pos) { return transform_delete_delete$1(b, a, !first) } if (a.pos === b.pos && a.str.length > b.str.length) { return transform_delete_delete$1(b, a, !first) } // take out overlapping parts if (b.pos < a.pos + a.str.length) { var s = b.pos - a.pos; var s1 = a.str.length - s; var s2 = s + b.str.length; a.str = a.str.slice(0, s) + a.str.slice(s2); b.str = b.str.slice(s1); b.pos -= s; } else { b.pos -= a.str.length; } } // Transform Insert and Deletion // -------- // function transform_insert_delete(a, b) { if (a.type === DELETE$1) { return transform_insert_delete(b, a) } // we can assume, that a is an insertion and b is a deletion // a is before b if (a.pos <= b.pos) { b.pos += a.str.length; } // a is after b else if (a.pos >= b.pos + b.str.length) { a.pos -= b.str.length; } // Note: this is a conflict case the user should be noticed about // If applied still, the deletion takes precedence // a.pos > b.pos && <= b.pos + b.length else { var s = a.pos - b.pos; b.str = b.str.slice(0, s) + a.str + b.str.slice(s); a.str = ""; } } function transform$1(a, b, options) { options = options || {}; if (options["no-conflict"] && hasConflict$1(a, b)) { throw new Conflict(a, b) } if (!options.inplace) { a = a.clone(); b = b.clone(); } if (a.type === INSERT && b.type === INSERT) { transform_insert_insert(a, b); } els