/** * @name string-left-right * @fileoverview Looks up the first non-whitespace character to the left/right of a given index * @version 4.1.0 * @author Roy Revelt, Codsen Ltd * @license MIT * {@link https://codsen.com/os/string-left-right/} */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.stringLeftRight = {})); }(this, (function (exports) { 'use strict'; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; /** * lodash (Custom Build) * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors * Released under MIT license * Based on Underscore.js 1.8.3 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** `Object#toString` result references. */ var objectTag = '[object Object]'; /** * 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; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** Used for built-in method references. */ var funcProto = Function.prototype, objectProto = Object.prototype; /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** Used to infer the `Object` constructor. */ var objectCtorString = funcToString.call(Object); /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /** Built-in value references. */ var getPrototype = overArg(Object.getPrototypeOf, Object); /** * 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'; } /** * Checks if `value` is a plain object, that is, an object created by the * `Object` constructor or one with a `[[Prototype]]` of `null`. * * @static * @memberOf _ * @since 0.8.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. * @example * * function Foo() { * this.a = 1; * } * * _.isPlainObject(new Foo); * // => false * * _.isPlainObject([1, 2, 3]); * // => false * * _.isPlainObject({ 'x': 0, 'y': 0 }); * // => true * * _.isPlainObject(Object.create(null)); * // => true */ function isPlainObject(value) { if (!isObjectLike(value) || objectToString.call(value) != objectTag || isHostObject(value)) { return false; } var proto = getPrototype(value); if (proto === null) { return true; } var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor; return (typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString); } var lodash_isplainobject = isPlainObject; var lodash_clonedeep = {exports: {}}; /** * lodash (Custom Build) * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors * Released under MIT license * Based on Underscore.js 1.8.3 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ (function (module, exports) { /** Used as the size to enable large array optimizations. */ var LARGE_ARRAY_SIZE = 200; /** Used to stand-in for `undefined` hash values. */ var HASH_UNDEFINED = '__lodash_hash_undefined__'; /** Used as references for various `Number` constants. */ var MAX_SAFE_INTEGER = 9007199254740991; /** `Object#toString` result references. */ var argsTag = '[object Arguments]', arrayTag = '[object Array]', boolTag = '[object Boolean]', dateTag = '[object Date]', errorTag = '[object Error]', funcTag = '[object Function]', genTag = '[object GeneratorFunction]', mapTag = '[object Map]', numberTag = '[object Number]', objectTag = '[object Object]', promiseTag = '[object Promise]', regexpTag = '[object RegExp]', setTag = '[object Set]', stringTag = '[object String]', symbolTag = '[object Symbol]', weakMapTag = '[object WeakMap]'; var arrayBufferTag = '[object ArrayBuffer]', dataViewTag = '[object DataView]', float32Tag = '[object Float32Array]', float64Tag = '[object Float64Array]', int8Tag = '[object Int8Array]', int16Tag = '[object Int16Array]', int32Tag = '[object Int32Array]', uint8Tag = '[object Uint8Array]', uint8ClampedTag = '[object Uint8ClampedArray]', uint16Tag = '[object Uint16Array]', uint32Tag = '[object Uint32Array]'; /** * Used to match `RegExp` * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). */ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g; /** Used to match `RegExp` flags from their coerced string values. */ var reFlags = /\w*$/; /** Used to detect host constructors (Safari). */ var reIsHostCtor = /^\[object .+?Constructor\]$/; /** Used to detect unsigned integer values. */ var reIsUint = /^(?:0|[1-9]\d*)$/; /** Used to identify `toStringTag` values supported by `_.clone`. */ var cloneableTags = {}; cloneableTags[argsTag] = cloneableTags[arrayTag] = cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = cloneableTags[boolTag] = cloneableTags[dateTag] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag] = cloneableTags[numberTag] = cloneableTags[objectTag] = cloneableTags[regexpTag] = cloneableTags[setTag] = cloneableTags[stringTag] = cloneableTags[symbolTag] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; cloneableTags[errorTag] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Detect free variable `exports`. */ var freeExports = exports && !exports.nodeType && exports; /** Detect free variable `module`. */ var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module; /** Detect the popular CommonJS extension `module.exports`. */ var moduleExports = freeModule && freeModule.exports === freeExports; /** * Adds the key-value `pair` to `map`. * * @private * @param {Object} map The map to modify. * @param {Array} pair The key-value pair to add. * @returns {Object} Returns `map`. */ function addMapEntry(map, pair) { // Don't return `map.set` because it's not chainable in IE 11. map.set(pair[0], pair[1]); return map; } /** * Adds `value` to `set`. * * @private * @param {Object} set The set to modify. * @param {*} value The value to add. * @returns {Object} Returns `set`. */ function addSetEntry(set, value) { // Don't return `set.add` because it's not chainable in IE 11. set.add(value); return set; } /** * A specialized version of `_.forEach` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns `array`. */ function arrayEach(array, iteratee) { var index = -1, length = array ? array.length : 0; while (++index < length) { if (iteratee(array[index], index, array) === false) { break; } } return array; } /** * Appends the elements of `values` to `array`. * * @private * @param {Array} array The array to modify. * @param {Array} values The values to append. * @returns {Array} Returns `array`. */ function arrayPush(array, values) { var index = -1, length = values.length, offset = array.length; while (++index < length) { array[offset + index] = values[index]; } return array; } /** * A specialized version of `_.reduce` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the first element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initAccum) { var index = -1, length = array ? array.length : 0; if (initAccum && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * The base implementation of `_.times` without support for iteratee shorthands * or max array length checks. * * @private * @param {number} n The number of times to invoke `iteratee`. * @param {Function} iteratee The function invoked per iteration. * @returns {Array} Returns the array of results. */ function baseTimes(n, iteratee) { var index = -1, result = Array(n); while (++index < n) { result[index] = iteratee(index); } return result; } /** * 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]; } /** * 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; } /** * Converts `map` to its key-value pairs. * * @private * @param {Object} map The map to convert. * @returns {Array} Returns the key-value pairs. */ function mapToArray(map) { var index = -1, result = Array(map.size); map.forEach(function(value, key) { result[++index] = [key, value]; }); return result; } /** * Creates a unary function that invokes `func` with its argument transformed. * * @private * @param {Function} func The function to wrap. * @param {Function} transform The argument transform. * @returns {Function} Returns the new function. */ function overArg(func, transform) { return function(arg) { return func(transform(arg)); }; } /** * Converts `set` to an array of its values. * * @private * @param {Object} set The set to convert. * @returns {Array} Returns the values. */ function setToArray(set) { var index = -1, result = Array(set.size); set.forEach(function(value) { result[++index] = value; }); return result; } /** Used for built-in method references. */ var arrayProto = Array.prototype, funcProto = Function.prototype, objectProto = Object.prototype; /** 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) : ''; }()); /** Used to resolve the decompiled source of functions. */ var funcToString = funcProto.toString; /** Used to check objects for own properties. */ var hasOwnProperty = objectProto.hasOwnProperty; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /** Used to detect if a method is native. */ var reIsNative = RegExp('^' + funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&') .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); /** Built-in value references. */ var Buffer = moduleExports ? root.Buffer : undefined, Symbol = root.Symbol, Uint8Array = root.Uint8Array, getPrototype = overArg(Object.getPrototypeOf, Object), objectCreate = Object.create, propertyIsEnumerable = objectProto.propertyIsEnumerable, splice = arrayProto.splice; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeGetSymbols = Object.getOwnPropertySymbols, nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined, nativeKeys = overArg(Object.keys, Object); /* Built-in method references that are verified to be native. */ var DataView = getNative(root, 'DataView'), Map = getNative(root, 'Map'), Promise = getNative(root, 'Promise'), Set = getNative(root, 'Set'), WeakMap = getNative(root, 'WeakMap'), nativeCreate = getNative(Object, 'create'); /** Used to detect maps, sets, and weakmaps. */ var dataViewCtorString = toSource(DataView), mapCtorString = toSource(Map), promiseCtorString = toSource(Promise), setCtorString = toSource(Set), weakMapCtorString = toSource(WeakMap); /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol ? Symbol.prototype : undefined, symbolValueOf = symbolProto ? symbolProto.valueOf : undefined; /** * 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]); } } /** * 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]; } /** * 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.call(data, key) ? data[key] : undefined; } /** * 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.call(data, key); } /** * 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 : value; return this; } // Add methods to `Hash`. Hash.prototype.clear = hashClear; Hash.prototype['delete'] = hashDelete; Hash.prototype.get = hashGet; Hash.prototype.has = hashHas; Hash.prototype.set = hashSet; /** * 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]); } } /** * Removes all key-value entries from the list cache. * * @private * @name clear * @memberOf ListCache */ function listCacheClear() { this.__data__ = []; } /** * 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; } // Add methods to `ListCache`. ListCache.prototype.clear = listCacheClear; ListCache.prototype['delete'] = listCacheDelete; ListCache.prototype.get = listCacheGet; ListCache.prototype.has = listCacheHas; ListCache.prototype.set = listCacheSet; /** * 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]); } } /** * Removes all key-value entries from the map. * * @private * @name clear * @memberOf MapCache */ function mapCacheClear() { this.__data__ = { 'hash': new Hash, 'map': new (Map || ListCache), 'string': new Hash }; } /** * 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; } // Add methods to `MapCache`. MapCache.prototype.clear = mapCacheClear; MapCache.prototype['delete'] = mapCacheDelete; MapCache.prototype.get = mapCacheGet; MapCache.prototype.has = mapCacheHas; MapCache.prototype.set = mapCacheSet; /** * Creates a stack cache object to store key-value pairs. * * @private * @constructor * @param {Array} [entries] The key-value pairs to cache. */ function Stack(entries) { this.__data__ = new ListCache(entries); } /** * Removes all key-value entries from the stack. * * @private * @name clear * @memberOf Stack */ function stackClear() { this.__data__ = new ListCache; } /** * Removes `key` and its value from the stack. * * @private * @name delete * @memberOf Stack * @param {string} key The key of the value to remove. * @returns {boolean} Returns `true` if the entry was removed, else `false`. */ function stackDelete(key) { return this.__data__['delete'](key); } /** * Gets the stack value for `key`. * * @private * @name get * @memberOf Stack * @param {string} key The key of the value to get. * @returns {*} Returns the entry value. */ function stackGet(key) { return this.__data__.get(key); } /** * Checks if a stack value for `key` exists. * * @private * @name has * @memberOf Stack * @param {string} key The key of the entry to check. * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. */ function stackHas(key) { return this.__data__.has(key); } /** * Sets the stack `key` to `value`. * * @private * @name set * @memberOf Stack * @param {string} key The key of the value to set. * @param {*} value The value to set. * @returns {Object} Returns the stack cache instance. */ function stackSet(key, value) { var cache = this.__data__; if (cache instanceof ListCache) { var pairs = cache.__data__; if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) { pairs.push([key, value]); return this; } cache = this.__data__ = new MapCache(pairs); } cache.set(key, value); return this; } // Add methods to `Stack`. Stack.prototype.clear = stackClear; Stack.prototype['delete'] = stackDelete; Stack.prototype.get = stackGet; Stack.prototype.has = stackHas; Stack.prototype.set = stackSet; /** * Creates an array of the enumerable property names of the array-like `value`. * * @private * @param {*} value The value to query. * @param {boolean} inherited Specify returning inherited property names. * @returns {Array} Returns the array of property names. */ function arrayLikeKeys(value, inherited) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. // Safari 9 makes `arguments.length` enumerable in strict mode. var result = (isArray(value) || isArguments(value)) ? baseTimes(value.length, String) : []; var length = result.length, skipIndexes = !!length; for (var key in value) { if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == 'length' || isIndex(key, length)))) { result.push(key); } } return result; } /** * Assigns `value` to `key` of `object` if the existing value is not equivalent * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.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.call(object, key) && eq(objValue, value)) || (value === undefined && !(key in object))) { object[key] = value; } } /** * Gets the index at which the `key` is found in `array` of key-value pairs. * * @private * @param {Array} array The array to inspect. * @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; } /** * The base implementation of `_.assign` without support for multiple sources * or `customizer` functions. * * @private * @param {Object} object The destination object. * @param {Object} source The source object. * @returns {Object} Returns `object`. */ function baseAssign(object, source) { return object && copyObject(source, keys(source), object); } /** * The base implementation of `_.clone` and `_.cloneDeep` which tracks * traversed objects. * * @private * @param {*} value The value to clone. * @param {boolean} [isDeep] Specify a deep clone. * @param {boolean} [isFull] Specify a clone including symbols. * @param {Function} [customizer] The function to customize cloning. * @param {string} [key] The key of `value`. * @param {Object} [object] The parent object of `value`. * @param {Object} [stack] Tracks traversed objects and their clone counterparts. * @returns {*} Returns the cloned value. */ function baseClone(value, isDeep, isFull, customizer, key, object, stack) { var result; if (customizer) { result = object ? customizer(value, key, object, stack) : customizer(value); } if (result !== undefined) { return result; } if (!isObject(value)) { return value; } var isArr = isArray(value); if (isArr) { result = initCloneArray(value); if (!isDeep) { return copyArray(value, result); } } else { var tag = getTag(value), isFunc = tag == funcTag || tag == genTag; if (isBuffer(value)) { return cloneBuffer(value, isDeep); } if (tag == objectTag || tag == argsTag || (isFunc && !object)) { if (isHostObject(value)) { return object ? value : {}; } result = initCloneObject(isFunc ? {} : value); if (!isDeep) { return copySymbols(value, baseAssign(result, value)); } } else { if (!cloneableTags[tag]) { return object ? value : {}; } result = initCloneByTag(value, tag, baseClone, isDeep); } } // Check for circular references and return its corresponding clone. stack || (stack = new Stack); var stacked = stack.get(value); if (stacked) { return stacked; } stack.set(value, result); if (!isArr) { var props = isFull ? getAllKeys(value) : keys(value); } arrayEach(props || value, function(subValue, key) { if (props) { key = subValue; subValue = value[key]; } // Recursively populate clone (susceptible to call stack limits). assignValue(result, key, baseClone(subValue, isDeep, isFull, customizer, key, value, stack)); }); return result; } /** * The base implementation of `_.create` without support for assigning * properties to the created object. * * @private * @param {Object} prototype The object to inherit from. * @returns {Object} Returns the new object. */ function baseCreate(proto) { return isObject(proto) ? objectCreate(proto) : {}; } /** * The base implementation of `getAllKeys` and `getAllKeysIn` which uses * `keysFunc` and `symbolsFunc` to get the enumerable property names and * symbols of `object`. * * @private * @param {Object} object The object to query. * @param {Function} keysFunc The function to get the keys of `object`. * @param {Function} symbolsFunc The function to get the symbols of `object`. * @returns {Array} Returns the array of property names and symbols. */ function baseGetAllKeys(object, keysFunc, symbolsFunc) { var result = keysFunc(object); return isArray(object) ? result : arrayPush(result, symbolsFunc(object)); } /** * The base implementation of `getTag`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ function baseGetTag(value) { return objectToString.call(value); } /** * 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(value) || isMasked(value)) { return false; } var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor; return pattern.test(toSource(value)); } /** * The base implementation of `_.keys` which doesn't treat sparse arrays as dense. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. */ function baseKeys(object) { if (!isPrototype(object)) { return nativeKeys(object); } var result = []; for (var key in Object(object)) { if (hasOwnProperty.call(object, key) && key != 'constructor') { result.push(key); } } return result; } /** * Creates a clone of `buffer`. * * @private * @param {Buffer} buffer The buffer to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Buffer} Returns the cloned buffer. */ function cloneBuffer(buffer, isDeep) { if (isDeep) { return buffer.slice(); } var result = new buffer.constructor(buffer.length); buffer.copy(result); return result; } /** * Creates a clone of `arrayBuffer`. * * @private * @param {ArrayBuffer} arrayBuffer The array buffer to clone. * @returns {ArrayBuffer} Returns the cloned array buffer. */ function cloneArrayBuffer(arrayBuffer) { var result = new arrayBuffer.constructor(arrayBuffer.byteLength); new Uint8Array(result).set(new Uint8Array(arrayBuffer)); return result; } /** * Creates a clone of `dataView`. * * @private * @param {Object} dataView The data view to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned data view. */ function cloneDataView(dataView, isDeep) { var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer; return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength); } /** * Creates a clone of `map`. * * @private * @param {Object} map The map to clone. * @param {Function} cloneFunc The function to clone values. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned map. */ function cloneMap(map, isDeep, cloneFunc) { var array = isDeep ? cloneFunc(mapToArray(map), true) : mapToArray(map); return arrayReduce(array, addMapEntry, new map.constructor); } /** * Creates a clone of `regexp`. * * @private * @param {Object} regexp The regexp to clone. * @returns {Object} Returns the cloned regexp. */ function cloneRegExp(regexp) { var result = new regexp.constructor(regexp.source, reFlags.exec(regexp)); result.lastIndex = regexp.lastIndex; return result; } /** * Creates a clone of `set`. * * @private * @param {Object} set The set to clone. * @param {Function} cloneFunc The function to clone values. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned set. */ function cloneSet(set, isDeep, cloneFunc) { var array = isDeep ? cloneFunc(setToArray(set), true) : setToArray(set); return arrayReduce(array, addSetEntry, new set.constructor); } /** * Creates a clone of the `symbol` object. * * @private * @param {Object} symbol The symbol object to clone. * @returns {Object} Returns the cloned symbol object. */ function cloneSymbol(symbol) { return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {}; } /** * Creates a clone of `typedArray`. * * @private * @param {Object} typedArray The typed array to clone. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the cloned typed array. */ function cloneTypedArray(typedArray, isDeep) { var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer; return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } /** * Copies the values of `source` to `array`. * * @private * @param {Array} source The array to copy values from. * @param {Array} [array=[]] The array to copy values to. * @returns {Array} Returns `array`. */ function copyArray(source, array) { var index = -1, length = source.length; array || (array = Array(length)); while (++index < length) { array[index] = source[index]; } return array; } /** * Copies properties of `source` to `object`. * * @private * @param {Object} source The object to copy properties from. * @param {Array} props The property identifiers to copy. * @param {Object} [object={}] The object to copy properties to. * @param {Function} [customizer] The function to customize copied values. * @returns {Object} Returns `object`. */ function copyObject(source, props, object, customizer) { object || (object = {}); var index = -1, length = props.length; while (++index < length) { var key = props[index]; var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined; assignValue(object, key, newValue === undefined ? source[key] : newValue); } return object; } /** * Copies own symbol properties of `source` to `object`. * * @private * @param {Object} source The object to copy symbols from. * @param {Object} [object={}] The object to copy symbols to. * @returns {Object} Returns `object`. */ function copySymbols(source, object) { return copyObject(source, getSymbols(source), object); } /** * Creates an array of own enumerable property names and symbols of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of property names and symbols. */ function getAllKeys(object) { return baseGetAllKeys(object, keys, getSymbols); } /** * 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; } /** * 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; } /** * Creates an array of the own enumerable symbol properties of `object`. * * @private * @param {Object} object The object to query. * @returns {Array} Returns the array of symbols. */ var getSymbols = nativeGetSymbols ? overArg(nativeGetSymbols, Object) : stubArray; /** * Gets the `toStringTag` of `value`. * * @private * @param {*} value The value to query. * @returns {string} Returns the `toStringTag`. */ var getTag = baseGetTag; // Fallback for data views, maps, sets, and weak maps in IE 11, // for data views in Edge < 14, and promises in Node.js. if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) || (Map && getTag(new Map) != mapTag) || (Promise && getTag(Promise.resolve()) != promiseTag) || (Set && getTag(new Set) != setTag) || (WeakMap && getTag(new WeakMap) != weakMapTag)) { getTag = function(value) { var result = objectToString.call(value), Ctor = result == objectTag ? value.constructor : undefined, ctorString = Ctor ? toSource(Ctor) : undefined; if (ctorString) { switch (ctorString) { case dataViewCtorString: return dataViewTag; case mapCtorString: return mapTag; case promiseCtorString: return promiseTag; case setCtorString: return setTag; case weakMapCtorString: return weakMapTag; } } return result; }; } /** * Initializes an array clone. * * @private * @param {Array} array The array to clone. * @returns {Array} Returns the initialized clone. */ function initCloneArray(array) { var length = array.length, result = array.constructor(length); // Add properties assigned by `RegExp#exec`. if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) { result.index = array.index; result.input = array.input; } return result; } /** * Initializes an object clone. * * @private * @param {Object} object The object to clone. * @returns {Object} Returns the initialized clone. */ function initCloneObject(object) { return (typeof object.constructor == 'function' && !isPrototype(object)) ? baseCreate(getPrototype(object)) : {}; } /** * Initializes an object clone based on its `toStringTag`. * * **Note:** This function only supports cloning values with tags of * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. * * @private * @param {Object} object The object to clone. * @param {string} tag The `toStringTag` of the object to clone. * @param {Function} cloneFunc The function to clone values. * @param {boolean} [isDeep] Specify a deep clone. * @returns {Object} Returns the initialized clone. */ function initCloneByTag(object, tag, cloneFunc, isDeep) { var Ctor = object.constructor; switch (tag) { case arrayBufferTag: return cloneArrayBuffer(object); case boolTag: case dateTag: return new Ctor(+object); case dataViewTag: return cloneDataView(object, isDeep); case float32Tag: case float64Tag: case int8Tag: case int16Tag: case int32Tag: case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: return cloneTypedArray(object, isDeep); case mapTag: return cloneMap(object, isDeep, cloneFunc); case numberTag: case stringTag: return new Ctor(object); case regexpTag: return cloneRegExp(object); case setTag: return cloneSet(object, isDeep, cloneFunc); case symbolTag: return cloneSymbol(object); } } /** * 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); } /** * 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); } /** * 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); } /** * Checks if `value` is likely a prototype object. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a prototype, else `false`. */ function isPrototype(value) { var Ctor = value && value.constructor, proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto; return value === proto; } /** * 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.call(func); } catch (e) {} try { return (func + ''); } catch (e) {} } return ''; } /** * This method is like `_.clone` except that it recursively clones `value`. * * @static * @memberOf _ * @since 1.0.0 * @category Lang * @param {*} value The value to recursively clone. * @returns {*} Returns the deep cloned value. * @see _.clone * @example * * var objects = [{ 'a': 1 }, { 'b': 2 }]; * * var deep = _.cloneDeep(objects); * console.log(deep[0] === objects[0]); * // => false */ function cloneDeep(value) { return baseClone(value, true, true); } /** * Performs a * [`SameValueZero`](http://ecma-international.org/ecma-262/7.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 = { 'a': 1 }; * var other = { 'a': 1 }; * * _.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); } /** * Checks if `value` is likely an `arguments` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, * else `false`. * @example * * _.isArguments(function() { return arguments; }()); * // => true * * _.isArguments([1, 2, 3]); * // => false */ function isArguments(value) { // Safari 8.1 makes `arguments.callee` enumerable in strict mode. return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') && (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag); } /** * Checks if `value` is classified as an `Array` object. * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array, else `false`. * @example * * _.isArray([1, 2, 3]); * // => true * * _.isArray(document.body.children); * // => false * * _.isArray('abc'); * // => false * * _.isArray(_.noop); * // => false */ var isArray = Array.isArray; /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * _.isArrayLike([1, 2, 3]); * // => true * * _.isArrayLike(document.body.children); * // => true * * _.isArrayLike('abc'); * // => true * * _.isArrayLike(_.noop); * // => false */ function isArrayLike(value) { return value != null && isLength(value.length) && !isFunction(value); } /** * This method is like `_.isArrayLike` except that it also checks if `value` * is an object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * _.isArrayLikeObject([1, 2, 3]); * // => true * * _.isArrayLikeObject(document.body.children); * // => true * * _.isArrayLikeObject('abc'); * // => false * * _.isArrayLikeObject(_.noop); * // => false */ function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value); } /** * Checks if `value` is a buffer. * * @static * @memberOf _ * @since 4.3.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. * @example * * _.isBuffer(new Buffer(2)); * // => true * * _.isBuffer(new Uint8Array(2)); * // => false */ var isBuffer = nativeIsBuffer || stubFalse; /** * 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 a function, 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-9 which returns 'object' for typed array and other constructors. var tag = isObject(value) ? objectToString.call(value) : ''; return tag == funcTag || tag == genTag; } /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * _.isLength(3); * // => true * * _.isLength(Number.MIN_VALUE); * // => false * * _.isLength(Infinity); * // => false * * _.isLength('3'); * // => false */ function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.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(value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); } /** * 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'; } /** * Creates an array of the own enumerable property names of `object`. * * **Note:** Non-object values are coerced to objects. See the * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) * for more details. * * @static * @since 0.1.0 * @memberOf _ * @category Object * @param {Object} object The object to query. * @returns {Array} Returns the array of property names. * @example * * function Foo() { * this.a = 1; * this.b = 2; * } * * Foo.prototype.c = 3; * * _.keys(new Foo); * // => ['a', 'b'] (iteration order is not guaranteed) * * _.keys('hi'); * // => ['0', '1'] */ function keys(object) { return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); } /** * This method returns a new empty array. * * @static * @memberOf _ * @since 4.13.0 * @category Util * @returns {Array} Returns the new empty array. * @example * * var arrays = _.times(2, _.stubArray); * * console.log(arrays); * // => [[], []] * * console.log(arrays[0] === arrays[1]); * // => false */ function stubArray() { return []; } /** * This method returns `false`. * * @static * @memberOf _ * @since 4.13.0 * @category Util * @returns {boolean} Returns `false`. * @example * * _.times(2, _.stubFalse); * // => [false, false] */ function stubFalse() { return false; } module.exports = cloneDeep; }(lodash_clonedeep, lodash_clonedeep.exports)); var clone = lodash_clonedeep.exports; var version$1 = "4.1.0"; const version = version$1; const RAWNBSP = "\u00A0"; // separates the value from flags function x(something) { // console.log( // `007 ${`\u001b[${35}m${`x() incoming "${something}"`}\u001b[${39}m`}` // ); const res = { value: something, hungry: false, optional: false, }; if ((res.value.endsWith("?*") || res.value.endsWith("*?")) && res.value.length > 2) { res.value = res.value.slice(0, res.value.length - 2); res.optional = true; res.hungry = true; } else if (res.value.endsWith("?") && res.value.length > 1) { res.value = res.value.slice(0, ~-res.value.length); res.optional = true; } else if (res.value.endsWith("*") && res.value.length > 1) { res.value = res.value.slice(0, ~-res.value.length); res.hungry = true; } // console.log( // `036 ${`\u001b[${35}m${`x() returning ${JSON.stringify( // res, // null, // 0 // )}`}\u001b[${39}m`}` // ); return res; } function isNum(something) { return typeof something === "number"; } function isStr(something) { return typeof something === "string"; } function rightMain({ str, idx = 0, stopAtNewlines = false, stopAtRawNbsp = false, }) { if (typeof str !== "string" || !str.length) { return null; } if (!idx || typeof idx !== "number") { idx = 0; } if (!str[idx + 1]) { return null; } if ( // next character exists str[idx + 1] && // and... // it's solid (str[idx + 1].trim() || // or it's a whitespace character, but... // stop at newlines is on (stopAtNewlines && // and it's a newline "\n\r".includes(str[idx + 1])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[idx + 1] === RAWNBSP))) { // best case scenario - next character is non-whitespace: return idx + 1; } if ( // second next character exists str[idx + 2] && // and... // it's solid (str[idx + 2].trim() || // it's a whitespace character and... // stop at newlines is on (stopAtNewlines && // and it's a newline "\n\r".includes(str[idx + 2])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[idx + 2] === RAWNBSP))) { // second best case scenario - second next character is non-whitespace: return idx + 2; } // worst case scenario - traverse forwards for (let i = idx + 1, len = str.length; i < len; i++) { if ( // it's solid str[i].trim() || // it's a whitespace character and... // stop at newlines is on (stopAtNewlines && // and it's a newline "\n\r".includes(str[i])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[i] === RAWNBSP)) { return i; } } return null; } function right(str, idx = 0) { return rightMain({ str, idx, stopAtNewlines: false, stopAtRawNbsp: false }); } function rightStopAtNewLines(str, idx) { return rightMain({ str, idx, stopAtNewlines: true, stopAtRawNbsp: false }); } function rightStopAtRawNbsp(str, idx) { return rightMain({ str, idx, stopAtNewlines: false, stopAtRawNbsp: true }); } // // // lllllll ffffffffffffffff tttt (((((( )))))) // l:::::l f::::::::::::::::f ttt:::t ((::::::( )::::::)) // l:::::l f::::::::::::::::::f t:::::t ((:::::::( ):::::::)) // l:::::l f::::::fffffff:::::f t:::::t (:::::::(( )):::::::) // l::::l eeeeeeeeeeee f:::::f ffffffttttttt:::::ttttttt (::::::( )::::::) // l::::l ee::::::::::::ee f:::::f t:::::::::::::::::t (:::::( ):::::) // l::::l e::::::eeeee:::::eef:::::::ffffff t:::::::::::::::::t (:::::( ):::::) // l::::l e::::::e e:::::ef::::::::::::f tttttt:::::::tttttt (:::::( ):::::) // l::::l e:::::::eeeee::::::ef::::::::::::f t:::::t (:::::( ):::::) // l::::l e:::::::::::::::::e f:::::::ffffff t:::::t (:::::( ):::::) // l::::l e::::::eeeeeeeeeee f:::::f t:::::t (:::::( ):::::) // l::::l e:::::::e f:::::f t:::::t tttttt (::::::( )::::::) // l::::::le::::::::e f:::::::f t::::::tttt:::::t (:::::::(( )):::::::) // l::::::l e::::::::eeeeeeee f:::::::f tt::::::::::::::t ((:::::::( ):::::::)) // l::::::l ee:::::::::::::e f:::::::f tt:::::::::::tt ((::::::( )::::::) // llllllll eeeeeeeeeeeeee fffffffff ttttttttttt (((((( )))))) // // // Finds the index of the first non-whitespace character on the left function leftMain({ str, idx, stopAtNewlines, stopAtRawNbsp }) { if (typeof str !== "string" || !str.length) { return null; } if (!idx || typeof idx !== "number") { idx = 0; } if (idx < 1) { return null; } if ( // ~- means minus one, in bitwise str[~-idx] && // either it's not a whitespace (str[~-idx].trim() || // or it is whitespace, but... // stop at newlines is on (stopAtNewlines && // and it's a newline "\n\r".includes(str[~-idx])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[~-idx] === RAWNBSP))) { // best case scenario - next character is non-whitespace: return ~-idx; } // if we reached this point, this means character on the left is whitespace - // fine - check the next character on the left, str[idx - 2] if ( // second character exists str[idx - 2] && // either it's not whitespace so Bob's your uncle here's non-whitespace character (str[idx - 2].trim() || // it is whitespace, but... // stop at newlines is on (stopAtNewlines && // it's some sort of a newline "\n\r".includes(str[idx - 2])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[idx - 2] === RAWNBSP))) { // second best case scenario - second next character is non-whitespace: return idx - 2; } // worst case scenario - traverse backwards for (let i = idx; i--;) { if (str[i] && // it's non-whitespace character (str[i].trim() || // or it is whitespace character, but... // stop at newlines is on (stopAtNewlines && // it's some sort of a newline "\n\r".includes(str[i])) || // stop at raw nbsp is on (stopAtRawNbsp && // and it's a raw nbsp str[i] === RAWNBSP))) { return i; } } return null; } function left(str, idx = 0) { return leftMain({ str, idx, stopAtNewlines: false, stopAtRawNbsp: false }); } function leftStopAtNewLines(str, idx) { return leftMain({ str, idx, stopAtNewlines: true, stopAtRawNbsp: false }); } function leftStopAtRawNbsp(str, idx) { return leftMain({ str, idx, stopAtNewlines: false, stopAtRawNbsp: true }); } function seq(direction, str, idx, opts, args) { if (typeof str !== "string" || !str.length) { return null; } if (typeof idx !== "number") { idx = 0; } if ((direction === "right" && !str[idx + 1]) || (direction === "left" && !str[~-idx])) { // if next character on the particular side doesn't even exist, that's a quick end return null; } // we start to look on the particular side from index "idx". // From there on, each finding sets its index to "lastFinding" so that we // know where to start looking on from next. Any failed finding // in a sequence is instant return "null". let lastFinding = idx; const gaps = []; let leftmostChar; let rightmostChar; let satiated; // used to prevent mismatching action kicking in when that // mismatching is after multiple hungry findings. // go through all arguments let i = 0; // we use while loop because for loop would not do in hungry matching cases, // where we need to repeat same step (hungrily matched character) few times. while (i < args.length) { if (!isStr(args[i]) || !args[i].length) { i += 1; continue; } const { value, optional, hungry } = x(args[i]); const whattsOnTheSide = direction === "right" ? right(str, lastFinding) : left(str, lastFinding); if ((opts.i && str[whattsOnTheSide].toLowerCase() === value.toLowerCase()) || (!opts.i && str[whattsOnTheSide] === value)) { // OK, one was matched, we're in the right clauses (otherwise we'd skip // if it was optional or break the matching) // Now, it depends, is it a hungry match, because if so, we need to look // for more of these. const temp = direction === "right" ? right(str, whattsOnTheSide) : left(str, whattsOnTheSide); if (hungry && ((opts.i && str[temp].toLowerCase() === value.toLowerCase()) || (!opts.i && str[temp] === value))) { // satiated means next iteration is allowed not to match anything satiated = true; } else { // move on i += 1; } // 1. first, tackle gaps // if there was a gap, push it to gaps array: if (typeof whattsOnTheSide === "number" && direction === "right" && whattsOnTheSide > lastFinding + 1) { gaps.push([lastFinding + 1, whattsOnTheSide]); } else if (direction === "left" && typeof whattsOnTheSide === "number" && whattsOnTheSide < ~-lastFinding) { gaps.unshift([whattsOnTheSide + 1, lastFinding]); } // 2. second, tackle the matching lastFinding = whattsOnTheSide; if (direction === "right") { if (leftmostChar === undefined) { leftmostChar = whattsOnTheSide; } rightmostChar = whattsOnTheSide; } else { if (rightmostChar === undefined) { rightmostChar = whattsOnTheSide; } leftmostChar = whattsOnTheSide; } } else if (optional) { i += 1; continue; } else if (satiated) { i += 1; satiated = undefined; continue; } else { return null; } } // if all arguments in sequence were empty strings, we return falsey null: if (leftmostChar === undefined || rightmostChar === undefined) { return null; } return { gaps, leftmostChar, rightmostChar }; } // // // lllllll // l:::::l // l:::::l // l:::::l // l::::l rrrrr rrrrrrrrr ssssssssss eeeeeeeeeeee qqqqqqqqq qqqqq // l::::l r::::rrr:::::::::r ss::::::::::s ee::::::::::::ee q:::::::::qqq::::q // l::::l r:::::::::::::::::r ss:::::::::::::s e::::::eeeee:::::ee q:::::::::::::::::q // l::::l --------------- rr::::::rrrrr::::::r s::::::ssss:::::se::::::e e:::::eq::::::qqqqq::::::qq // l::::l -:::::::::::::- r:::::r r:::::r s:::::s ssssss e:::::::eeeee::::::eq:::::q q:::::q // l::::l --------------- r:::::r rrrrrrr s::::::s e:::::::::::::::::e q:::::q q:::::q // l::::l r:::::r s::::::s e::::::eeeeeeeeeee q:::::q q:::::q // l::::l r:::::r ssssss s:::::s e:::::::e q::::::q q:::::q // l::::::l r:::::r s:::::ssss::::::se::::::::e q:::::::qqqqq:::::q // l::::::l r:::::r s::::::::::::::s e::::::::eeeeeeee q::::::::::::::::q // l::::::l r:::::r s:::::::::::ss ee:::::::::::::e qq::::::::::::::q // llllllll rrrrrrr sssssssssss eeeeeeeeeeeeee qqqqqqqq::::::q // q:::::q // q:::::q // q:::::::q // q:::::::q // q:::::::q // qqqqqqqqq const seqDefaults = { i: false, }; function leftSeq(str, idx, ...args) { // if there are no arguments, it becomes left() if (!args || !args.length) { // console.log(`493 leftSeq() calling left()`); // return left(str, idx); throw new Error(`string-left-right/leftSeq(): only two input arguments were passed! Did you intend to use left() method instead?`); } let opts; if (lodash_isplainobject(args[0])) { opts = { ...seqDefaults, ...args.shift() }; } else { opts = seqDefaults; } return seq("left", str, idx, opts, Array.from(args).reverse()); } function rightSeq(str, idx, ...args) { // if there are no arguments, it becomes right() if (!args || !args.length) { // console.log(`520 rightSeq() calling right()`); // return right(str, idx); throw new Error(`string-left-right/rightSeq(): only two input arguments were passed! Did you intend to use right() method instead?`); } let opts; if (lodash_isplainobject(args[0])) { opts = { ...seqDefaults, ...args.shift() }; } else { opts = seqDefaults; } return seq("right", str, idx, opts, args); } // chomp() lets you match sequences of characters with zero or more whitespace characters in between each, // on left or right of a given string index, with optional granular control over surrounding // whitespace-munching. Yes, that's a technical term. function chomp(direction, str, idx, opts, args = []) { // // INSURANCE. // if (typeof str !== "string" || !str.length) { return null; } if (!idx || typeof idx !== "number") { idx = 0; } if ((direction === "right" && !str[idx + 1]) || (direction === "left" && +idx === 0)) { return null; } // // ACTION. // let lastRes = null; let lastIdx = null; do { lastRes = direction === "right" ? rightSeq(str, typeof lastIdx === "number" ? lastIdx : idx, ...args) : leftSeq(str, typeof lastIdx === "number" ? lastIdx : idx, ...args); if (lastRes !== null) { lastIdx = direction === "right" ? lastRes.rightmostChar : lastRes.leftmostChar; } } while (lastRes); if (lastIdx != null && direction === "right") { lastIdx += 1; } if (lastIdx === null) { // if nothing was matched return null; } // the last thing what's left to do is tackle the whitespace on the right. // Depending on opts.mode, there can be different ways. if (direction === "right") { // // // // R I G H T // // // // quick ending - no whitespace on the right at all: if (str[lastIdx] && str[lastIdx].trim()) { // if the character follows tightly right after, return lastIdx; } // Default, 0 is leave single space if possible or chomp up to nearest line // break character or chomp up to EOL const whatsOnTheRight = right(str, lastIdx); if (!opts || opts.mode === 0) { if (whatsOnTheRight === lastIdx + 1) { // if there's one whitespace character, Bob's your uncle here's // the final result return lastIdx; } if (str.slice(lastIdx, whatsOnTheRight || str.length).trim() || str.slice(lastIdx, whatsOnTheRight || str.length).includes("\n") || str.slice(lastIdx, whatsOnTheRight || str.length).includes("\r")) { // if there are line break characters between current "lastIdx" we're on // and the first non-whitespace character on the right for (let y = lastIdx, len = str.length; y < len; y++) { if (`\n\r`.includes(str[y])) { return y; } } } else { return whatsOnTheRight ? ~-whatsOnTheRight : str.length; } } else if (opts.mode === 1) { // mode 1 doesn't touch the whitespace, so it's quick: return lastIdx; } else if (opts.mode === 2) { // mode 2 hungrily chomps all whitespace except newlines const remainderString = str.slice(lastIdx); if (remainderString.trim() || remainderString.includes("\n") || remainderString.includes("\r")) { // if there are line breaks, we need to loop to chomp up to them but not further for (let y = lastIdx, len = str.length; y < len; y++) { if (str[y].trim() || `\n\r`.includes(str[y])) { return y; } } } // ELSE, last but not least, chomp to the end: return str.length; } // ELSE - mode 3 // mode 3 is an aggro chomp - will chump all whitespace return whatsOnTheRight || str.length; // // // // R I G H T E N D S // // // } // // // // L E F T // // // // quick ending - no whitespace on the left at all: if (str[lastIdx] && str[~-lastIdx] && str[~-lastIdx].trim()) { // if the non-whitespace character is on the left return lastIdx; } // Default, 0 is leave single space if possible or chomp up to nearest line // break character or chomp up to index zero, start of the string const whatsOnTheLeft = left(str, lastIdx); if (!opts || opts.mode === 0) { if (whatsOnTheLeft === lastIdx - 2) { // if there's one whitespace character between here and next real character, Bob's your uncle here's // the final result return lastIdx; } if (str.slice(0, lastIdx).trim() || str.slice(0, lastIdx).includes("\n") || str.slice(0, lastIdx).includes("\r")) { // if there are line break characters between current "lastIdx" we're on // and the first non-whitespace character on the right for (let y = lastIdx; y--;) { if (`\n\r`.includes(str[y]) || str[y].trim()) { return y + 1 + (str[y].trim() ? 1 : 0); } } } // ELSE return 0; } if (opts.mode === 1) { // mode 1 doesn't touch the whitespace, so it's quick: return lastIdx; } if (opts.mode === 2) { // mode 2 hungrily chomps all whitespace except newlines const remainderString = str.slice(0, lastIdx); if (remainderString.trim() || remainderString.includes("\n") || remainderString.includes("\r")) { // if there are line breaks, we need to loop to chomp up to them but not further for (let y = lastIdx; y--;) { if (str[y].trim() || `\n\r`.includes(str[y])) { return y + 1; } } } // ELSE, last but not least, chomp to the end: return 0; } // ELSE - mode 3 // mode 3 is an aggro chomp - will chump all whitespace return whatsOnTheLeft !== null ? whatsOnTheLeft + 1 : 0; // // // // L E F T E N D S // // // } // // // hhhhhhh LLLLLLLLLLL // h:::::h L:::::::::L // h:::::h L:::::::::L // h:::::h LL:::::::LL // cccccccccccccccch::::h hhhhh mmmmmmm mmmmmmm ppppp ppppppppp L:::::L // cc:::::::::::::::ch::::hh:::::hhh mm:::::::m m:::::::mm p::::ppp:::::::::p L:::::L // c:::::::::::::::::ch::::::::::::::hh m::::::::::mm::::::::::mp:::::::::::::::::p L:::::L // c:::::::cccccc:::::ch:::::::hhh::::::h m::::::::::::::::::::::mpp::::::ppppp::::::p L:::::L // c::::::c ccccccch::::::h h::::::hm:::::mmm::::::mmm:::::m p:::::p p:::::p L:::::L // c:::::c h:::::h h:::::hm::::m m::::m m::::m p:::::p p:::::p L:::::L // c:::::c h:::::h h:::::hm::::m m::::m m::::m p:::::p p:::::p L:::::L // c::::::c ccccccch:::::h h:::::hm::::m m::::m m::::m p:::::p p::::::p L:::::L LLLLLL // c:::::::cccccc:::::ch:::::h h:::::hm::::m m::::m m::::m p:::::ppppp:::::::pLL:::::::LLLLLLLLL:::::L // c:::::::::::::::::ch:::::h h:::::hm::::m m::::m m::::m p::::::::::::::::p L::::::::::::::::::::::L // cc:::::::::::::::ch:::::h h:::::hm::::m m::::m m::::m p::::::::::::::pp L::::::::::::::::::::::L // cccccccccccccccchhhhhhh hhhhhhhmmmmmm mmmmmm mmmmmm p::::::pppppppp LLLLLLLLLLLLLLLLLLLLLLLL // p:::::p // p:::::p // p:::::::p // p:::::::p // p:::::::p // ppppppppp // function chompLeft(str, idx, ...args) { // if there are no arguments, null if (!args.length || (args.length === 1 && lodash_isplainobject(args[0]))) { return null; } // // OPTS. // // modes: // 0 - leave single space if possible // 1 - stop at first space, leave whitespace alone // 2 - aggressively chomp all whitespace except newlines // 3 - aggressively chomp all whitespace including newlines const defaults = { mode: 0, }; // now, the first element within args can be opts. // It's a plain object so it's easy to distinguish if (lodash_isplainobject(args[0])) { const opts = { ...defaults, ...clone(args[0]) }; if (!opts.mode) { opts.mode = 0; } else if (isStr(opts.mode) && `0123`.includes(opts.mode)) { opts.mode = Number.parseInt(opts.mode, 10); } else if (!isNum(opts.mode)) { throw new Error(`string-left-right/chompLeft(): [THROW_ID_01] the opts.mode is wrong! It should be 0, 1, 2 or 3. It was given as ${opts.mode} (type ${typeof opts.mode})`); } return chomp("left", str, idx, opts, clone(args).slice(1)); } if (!isStr(args[0])) { return chomp("left", str, idx, defaults, clone(args).slice(1)); } // ELSE // all arguments are values to match, first element is not options object return chomp("left", str, idx, defaults, clone(args)); } // // // hhhhhhh RRRRRRRRRRRRRRRRR // h:::::h R::::::::::::::::R // h:::::h R::::::RRRRRR:::::R // h:::::h RR:::::R R:::::R // cccccccccccccccch::::h hhhhh mmmmmmm mmmmmmm ppppp ppppppppp R::::R R:::::R // cc:::::::::::::::ch::::hh:::::hhh mm:::::::m m:::::::mm p::::ppp:::::::::p R::::R R:::::R // c:::::::::::::::::ch::::::::::::::hh m::::::::::mm::::::::::mp:::::::::::::::::p R::::RRRRRR:::::R // c:::::::cccccc:::::ch:::::::hhh::::::h m::::::::::::::::::::::mpp::::::ppppp::::::p R:::::::::::::RR // c::::::c ccccccch::::::h h::::::hm:::::mmm::::::mmm:::::m p:::::p p:::::p R::::RRRRRR:::::R // c:::::c h:::::h h:::::hm::::m m::::m m::::m p:::::p p:::::p R::::R R:::::R // c:::::c h:::::h h:::::hm::::m m::::m m::::m p:::::p p:::::p R::::R R:::::R // c::::::c ccccccch:::::h h:::::hm::::m m::::m m::::m p:::::p p::::::p R::::R R:::::R // c:::::::cccccc:::::ch:::::h h:::::hm::::m m::::m m::::m p:::::ppppp:::::::pRR:::::R R:::::R // c:::::::::::::::::ch:::::h h:::::hm::::m m::::m m::::m p::::::::::::::::p R::::::R R:::::R // cc:::::::::::::::ch:::::h h:::::hm::::m m::::m m::::m p::::::::::::::pp R::::::R R:::::R // cccccccccccccccchhhhhhh hhhhhhhmmmmmm mmmmmm mmmmmm p::::::pppppppp RRRRRRRR RRRRRRR // p:::::p // p:::::p // p:::::::p // p:::::::p // p:::::::p // ppppppppp // function chompRight(str, idx, ...args) { // if there are no arguments, null if (!args.length || (args.length === 1 && lodash_isplainobject(args[0]))) { return null; } // // OPTS. // // modes: // 0 - leave single space if possible // 1 - stop at first space, leave whitespace alone // 2 - aggressively chomp all whitespace except newlines // 3 - aggressively chomp all whitespace including newlines const defaults = { mode: 0, }; // now, the first element within args can be opts. // It's a plain object so it's easy to distinguish if (lodash_isplainobject(args[0])) { const opts = { ...defaults, ...clone(args[0]) }; if (!opts.mode) { opts.mode = 0; } else if (isStr(opts.mode) && `0123`.includes(opts.mode)) { opts.mode = Number.parseInt(opts.mode, 10); } else if (!isNum(opts.mode)) { throw new Error(`string-left-right/chompRight(): [THROW_ID_02] the opts.mode is wrong! It should be 0, 1, 2 or 3. It was given as ${opts.mode} (type ${typeof opts.mode})`); } return chomp("right", str, idx, opts, clone(args).slice(1)); } if (!isStr(args[0])) { return chomp("right", str, idx, defaults, clone(args).slice(1)); } // ELSE // all arguments are values to match, first element is not options object return chomp("right", str, idx, defaults, clone(args)); } exports.chompLeft = chompLeft; exports.chompRight = chompRight; exports.left = left; exports.leftSeq = leftSeq; exports.leftStopAtNewLines = leftStopAtNewLines; exports.leftStopAtRawNbsp = leftStopAtRawNbsp; exports.right = right; exports.rightSeq = rightSeq; exports.rightStopAtNewLines = rightStopAtNewLines; exports.rightStopAtRawNbsp = rightStopAtRawNbsp; exports.version = version; Object.defineProperty(exports, '__esModule', { value: true }); })));