Created
June 19, 2015 23:49
-
-
Save Joeao/0c33f63c6aba38e8e80f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! Quill Editor v0.19.14 | |
* https://quilljs.com/ | |
* Copyright (c) 2014, Jason Chen | |
* Copyright (c) 2013, salesforce.com | |
*/ | |
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Quill = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | |
(function (global){ | |
/** | |
* @license | |
* lodash 3.9.3 (Custom Build) <https://lodash.com/> | |
* Build: `lodash modern include="difference,intersection,last,all,each,find,invoke,map,reduce,bind,defer,partial,clone,extend,defaults,omit,values,isElement,isEqual,isFunction,isNumber,isObject,isString,uniqueId" --development --output .build/lodash.js` | |
* Copyright 2012-2015 The Dojo Foundation <http://dojofoundation.org/> | |
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> | |
* Copyright 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors | |
* Available under MIT license <https://lodash.com/license> | |
*/ | |
;(function() { | |
/** Used as a safe reference for `undefined` in pre-ES5 environments. */ | |
var undefined; | |
/** Used as the semantic version number. */ | |
var VERSION = '3.9.3'; | |
/** Used to compose bitmasks for wrapper metadata. */ | |
var BIND_FLAG = 1, | |
BIND_KEY_FLAG = 2, | |
CURRY_BOUND_FLAG = 4, | |
CURRY_FLAG = 8, | |
CURRY_RIGHT_FLAG = 16, | |
PARTIAL_FLAG = 32, | |
PARTIAL_RIGHT_FLAG = 64, | |
ARY_FLAG = 128, | |
REARG_FLAG = 256; | |
/** Used to detect when a function becomes hot. */ | |
var HOT_COUNT = 150, | |
HOT_SPAN = 16; | |
/** Used as the `TypeError` message for "Functions" methods. */ | |
var FUNC_ERROR_TEXT = 'Expected a function'; | |
/** Used as the internal argument placeholder. */ | |
var PLACEHOLDER = '__lodash_placeholder__'; | |
/** `Object#toString` result references. */ | |
var argsTag = '[object Arguments]', | |
arrayTag = '[object Array]', | |
boolTag = '[object Boolean]', | |
dateTag = '[object Date]', | |
errorTag = '[object Error]', | |
funcTag = '[object Function]', | |
mapTag = '[object Map]', | |
numberTag = '[object Number]', | |
objectTag = '[object Object]', | |
regexpTag = '[object RegExp]', | |
setTag = '[object Set]', | |
stringTag = '[object String]', | |
weakMapTag = '[object WeakMap]'; | |
var arrayBufferTag = '[object ArrayBuffer]', | |
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 property names within property paths. */ | |
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\n\\]|\\.)*?\1)\]/, | |
reIsPlainProp = /^\w*$/, | |
rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\n\\]|\\.)*?)\2)\]/g; | |
/** | |
* Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special). | |
* In addition to special characters the forward slash is escaped to allow for | |
* easier `eval` use and `Function` compilation. | |
*/ | |
var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g, | |
reHasRegExpChars = RegExp(reRegExpChars.source); | |
/** Used to match backslashes in property paths. */ | |
var reEscapeChar = /\\(\\)?/g; | |
/** Used to match `RegExp` flags from their coerced string values. */ | |
var reFlags = /\w*$/; | |
/** Used to detect host constructors (Safari > 5). */ | |
var reIsHostCtor = /^\[object .+?Constructor\]$/; | |
/** Used to detect unsigned integer values. */ | |
var reIsUint = /^\d+$/; | |
/** Used to identify `toStringTag` values of typed arrays. */ | |
var typedArrayTags = {}; | |
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = | |
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = | |
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = | |
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = | |
typedArrayTags[uint32Tag] = true; | |
typedArrayTags[argsTag] = typedArrayTags[arrayTag] = | |
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = | |
typedArrayTags[dateTag] = typedArrayTags[errorTag] = | |
typedArrayTags[funcTag] = typedArrayTags[mapTag] = | |
typedArrayTags[numberTag] = typedArrayTags[objectTag] = | |
typedArrayTags[regexpTag] = typedArrayTags[setTag] = | |
typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false; | |
/** Used to identify `toStringTag` values supported by `_.clone`. */ | |
var cloneableTags = {}; | |
cloneableTags[argsTag] = cloneableTags[arrayTag] = | |
cloneableTags[arrayBufferTag] = cloneableTags[boolTag] = | |
cloneableTags[dateTag] = cloneableTags[float32Tag] = | |
cloneableTags[float64Tag] = cloneableTags[int8Tag] = | |
cloneableTags[int16Tag] = cloneableTags[int32Tag] = | |
cloneableTags[numberTag] = cloneableTags[objectTag] = | |
cloneableTags[regexpTag] = cloneableTags[stringTag] = | |
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = | |
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true; | |
cloneableTags[errorTag] = cloneableTags[funcTag] = | |
cloneableTags[mapTag] = cloneableTags[setTag] = | |
cloneableTags[weakMapTag] = false; | |
/** Used to determine if values are of the language type `Object`. */ | |
var objectTypes = { | |
'function': true, | |
'object': true | |
}; | |
/** Detect free variable `exports`. */ | |
var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports; | |
/** Detect free variable `module`. */ | |
var freeModule = objectTypes[typeof module] && module && !module.nodeType && module; | |
/** Detect free variable `global` from Node.js. */ | |
var freeGlobal = freeExports && freeModule && typeof global == 'object' && global && global.Object && global; | |
/** Detect free variable `self`. */ | |
var freeSelf = objectTypes[typeof self] && self && self.Object && self; | |
/** Detect free variable `window`. */ | |
var freeWindow = objectTypes[typeof window] && window && window.Object && window; | |
/** Detect the popular CommonJS extension `module.exports`. */ | |
var moduleExports = freeModule && freeModule.exports === freeExports && freeExports; | |
/** | |
* Used as a reference to the global object. | |
* | |
* The `this` value is used if it's the global object to avoid Greasemonkey's | |
* restricted `window` object, otherwise the `window` object is used. | |
*/ | |
var root = freeGlobal || ((freeWindow !== (this && this.window)) && freeWindow) || freeSelf || this; | |
/*--------------------------------------------------------------------------*/ | |
/** | |
* The base implementation of `_.findIndex` and `_.findLastIndex` without | |
* support for callback shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array} array The array to search. | |
* @param {Function} predicate The function invoked per iteration. | |
* @param {boolean} [fromRight] Specify iterating from right to left. | |
* @returns {number} Returns the index of the matched value, else `-1`. | |
*/ | |
function baseFindIndex(array, predicate, fromRight) { | |
var length = array.length, | |
index = fromRight ? length : -1; | |
while ((fromRight ? index-- : ++index < length)) { | |
if (predicate(array[index], index, array)) { | |
return index; | |
} | |
} | |
return -1; | |
} | |
/** | |
* The base implementation of `_.indexOf` without support for binary searches. | |
* | |
* @private | |
* @param {Array} array The array to search. | |
* @param {*} value The value to search for. | |
* @param {number} fromIndex The index to search from. | |
* @returns {number} Returns the index of the matched value, else `-1`. | |
*/ | |
function baseIndexOf(array, value, fromIndex) { | |
if (value !== value) { | |
return indexOfNaN(array, fromIndex); | |
} | |
var index = fromIndex - 1, | |
length = array.length; | |
while (++index < length) { | |
if (array[index] === value) { | |
return index; | |
} | |
} | |
return -1; | |
} | |
/** | |
* The base implementation of `_.isFunction` without support for environments | |
* with incorrect `typeof` results. | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. | |
*/ | |
function baseIsFunction(value) { | |
// Avoid a Chakra JIT bug in compatibility modes of IE 11. | |
// See https://github.com/jashkenas/underscore/issues/1621 for more details. | |
return typeof value == 'function' || false; | |
} | |
/** | |
* Converts `value` to a string if it's not one. An empty string is returned | |
* for `null` or `undefined` values. | |
* | |
* @private | |
* @param {*} value The value to process. | |
* @returns {string} Returns the string. | |
*/ | |
function baseToString(value) { | |
if (typeof value == 'string') { | |
return value; | |
} | |
return value == null ? '' : (value + ''); | |
} | |
/** | |
* Gets the index at which the first occurrence of `NaN` is found in `array`. | |
* | |
* @private | |
* @param {Array} array The array to search. | |
* @param {number} fromIndex The index to search from. | |
* @param {boolean} [fromRight] Specify iterating from right to left. | |
* @returns {number} Returns the index of the matched `NaN`, else `-1`. | |
*/ | |
function indexOfNaN(array, fromIndex, fromRight) { | |
var length = array.length, | |
index = fromIndex + (fromRight ? 0 : -1); | |
while ((fromRight ? index-- : ++index < length)) { | |
var other = array[index]; | |
if (other !== other) { | |
return index; | |
} | |
} | |
return -1; | |
} | |
/** | |
* Checks if `value` is object-like. | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is object-like, else `false`. | |
*/ | |
function isObjectLike(value) { | |
return !!value && typeof value == 'object'; | |
} | |
/** | |
* Replaces all `placeholder` elements in `array` with an internal placeholder | |
* and returns an array of their indexes. | |
* | |
* @private | |
* @param {Array} array The array to modify. | |
* @param {*} placeholder The placeholder to replace. | |
* @returns {Array} Returns the new array of placeholder indexes. | |
*/ | |
function replaceHolders(array, placeholder) { | |
var index = -1, | |
length = array.length, | |
resIndex = -1, | |
result = []; | |
while (++index < length) { | |
if (array[index] === placeholder) { | |
array[index] = PLACEHOLDER; | |
result[++resIndex] = index; | |
} | |
} | |
return result; | |
} | |
/*--------------------------------------------------------------------------*/ | |
/** Used for native method references. */ | |
var objectProto = Object.prototype; | |
/** Used to detect DOM support. */ | |
var document = (document = root.window) ? document.document : null; | |
/** Used to resolve the decompiled source of functions. */ | |
var fnToString = Function.prototype.toString; | |
/** Used to check objects for own properties. */ | |
var hasOwnProperty = objectProto.hasOwnProperty; | |
/** Used to generate unique IDs. */ | |
var idCounter = 0; | |
/** | |
* Used to resolve the [`toStringTag`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.tostring) | |
* of values. | |
*/ | |
var objToString = objectProto.toString; | |
/** Used to detect if a method is native. */ | |
var reIsNative = RegExp('^' + | |
escapeRegExp(fnToString.call(hasOwnProperty)) | |
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' | |
); | |
/** Native method references. */ | |
var ArrayBuffer = getNative(root, 'ArrayBuffer'), | |
bufferSlice = getNative(ArrayBuffer && new ArrayBuffer(0), 'slice'), | |
floor = Math.floor, | |
getPrototypeOf = getNative(Object, 'getPrototypeOf'), | |
Set = getNative(root, 'Set'), | |
Uint8Array = getNative(root, 'Uint8Array'), | |
WeakMap = getNative(root, 'WeakMap'); | |
/** Used to clone array buffers. */ | |
var Float64Array = (function() { | |
// Safari 5 errors when using an array buffer to initialize a typed array | |
// where the array buffer's `byteLength` is not a multiple of the typed | |
// array's `BYTES_PER_ELEMENT`. | |
try { | |
var func = getNative(root, 'Float64Array'), | |
result = new func(new ArrayBuffer(10), 0, 1) && func; | |
} catch(e) {} | |
return result || null; | |
}()); | |
/* Native method references for those with the same name as other `lodash` methods. */ | |
var nativeCreate = getNative(Object, 'create'), | |
nativeIsArray = getNative(Array, 'isArray'), | |
nativeKeys = getNative(Object, 'keys'), | |
nativeMax = Math.max, | |
nativeMin = Math.min, | |
nativeNow = getNative(Date, 'now'); | |
/** Used as references for `-Infinity` and `Infinity`. */ | |
var POSITIVE_INFINITY = Number.POSITIVE_INFINITY; | |
/** Used as references for the maximum length and index of an array. */ | |
var MAX_ARRAY_LENGTH = 4294967295, | |
MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1, | |
HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1; | |
/** Used as the size, in bytes, of each `Float64Array` element. */ | |
var FLOAT64_BYTES_PER_ELEMENT = Float64Array ? Float64Array.BYTES_PER_ELEMENT : 0; | |
/** | |
* Used as the [maximum length](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.max_safe_integer) | |
* of an array-like value. | |
*/ | |
var MAX_SAFE_INTEGER = 9007199254740991; | |
/** Used to store function metadata. */ | |
var metaMap = WeakMap && new WeakMap; | |
/** Used to lookup unminified function names. */ | |
var realNames = {}; | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates a `lodash` object which wraps `value` to enable implicit chaining. | |
* Methods that operate on and return arrays, collections, and functions can | |
* be chained together. Methods that return a boolean or single value will | |
* automatically end the chain returning the unwrapped value. Explicit chaining | |
* may be enabled using `_.chain`. The execution of chained methods is lazy, | |
* that is, execution is deferred until `_#value` is implicitly or explicitly | |
* called. | |
* | |
* Lazy evaluation allows several methods to support shortcut fusion. Shortcut | |
* fusion is an optimization that merges iteratees to avoid creating intermediate | |
* arrays and reduce the number of iteratee executions. | |
* | |
* Chaining is supported in custom builds as long as the `_#value` method is | |
* directly or indirectly included in the build. | |
* | |
* In addition to lodash methods, wrappers have `Array` and `String` methods. | |
* | |
* The wrapper `Array` methods are: | |
* `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, | |
* `splice`, and `unshift` | |
* | |
* The wrapper `String` methods are: | |
* `replace` and `split` | |
* | |
* The wrapper methods that support shortcut fusion are: | |
* `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, | |
* `first`, `initial`, `last`, `map`, `pluck`, `reject`, `rest`, `reverse`, | |
* `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `toArray`, | |
* and `where` | |
* | |
* The chainable wrapper methods are: | |
* `after`, `ary`, `assign`, `at`, `before`, `bind`, `bindAll`, `bindKey`, | |
* `callback`, `chain`, `chunk`, `commit`, `compact`, `concat`, `constant`, | |
* `countBy`, `create`, `curry`, `debounce`, `defaults`, `defer`, `delay`, | |
* `difference`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `fill`, | |
* `filter`, `flatten`, `flattenDeep`, `flow`, `flowRight`, `forEach`, | |
* `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`, `functions`, | |
* `groupBy`, `indexBy`, `initial`, `intersection`, `invert`, `invoke`, `keys`, | |
* `keysIn`, `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, | |
* `memoize`, `merge`, `method`, `methodOf`, `mixin`, `negate`, `omit`, `once`, | |
* `pairs`, `partial`, `partialRight`, `partition`, `pick`, `plant`, `pluck`, | |
* `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`, `rearg`, | |
* `reject`, `remove`, `rest`, `restParam`, `reverse`, `set`, `shuffle`, | |
* `slice`, `sort`, `sortBy`, `sortByAll`, `sortByOrder`, `splice`, `spread`, | |
* `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`, | |
* `thru`, `times`, `toArray`, `toPlainObject`, `transform`, `union`, `uniq`, | |
* `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, `where`, `without`, | |
* `wrap`, `xor`, `zip`, `zipObject`, `zipWith` | |
* | |
* The wrapper methods that are **not** chainable by default are: | |
* `add`, `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `deburr`, | |
* `endsWith`, `escape`, `escapeRegExp`, `every`, `find`, `findIndex`, `findKey`, | |
* `findLast`, `findLastIndex`, `findLastKey`, `findWhere`, `first`, `get`, | |
* `gt`, `gte`, `has`, `identity`, `includes`, `indexOf`, `inRange`, `isArguments`, | |
* `isArray`, `isBoolean`, `isDate`, `isElement`, `isEmpty`, `isEqual`, `isError`, | |
* `isFinite` `isFunction`, `isMatch`, `isNative`, `isNaN`, `isNull`, `isNumber`, | |
* `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, | |
* `isTypedArray`, `join`, `kebabCase`, `last`, `lastIndexOf`, `lt`, `lte`, | |
* `max`, `min`, `noConflict`, `noop`, `now`, `pad`, `padLeft`, `padRight`, | |
* `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, | |
* `runInContext`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, | |
* `sortedLastIndex`, `startCase`, `startsWith`, `sum`, `template`, `trim`, | |
* `trimLeft`, `trimRight`, `trunc`, `unescape`, `uniqueId`, `value`, and `words` | |
* | |
* The wrapper method `sample` will return a wrapped value when `n` is provided, | |
* otherwise an unwrapped value is returned. | |
* | |
* @name _ | |
* @constructor | |
* @category Chain | |
* @param {*} value The value to wrap in a `lodash` instance. | |
* @returns {Object} Returns the new `lodash` wrapper instance. | |
* @example | |
* | |
* var wrapped = _([1, 2, 3]); | |
* | |
* // returns an unwrapped value | |
* wrapped.reduce(function(total, n) { | |
* return total + n; | |
* }); | |
* // => 6 | |
* | |
* // returns a wrapped value | |
* var squares = wrapped.map(function(n) { | |
* return n * n; | |
* }); | |
* | |
* _.isArray(squares); | |
* // => false | |
* | |
* _.isArray(squares.value()); | |
* // => true | |
*/ | |
function lodash() { | |
// No operation performed. | |
} | |
/** | |
* The function whose prototype all chaining wrappers inherit from. | |
* | |
* @private | |
*/ | |
function baseLodash() { | |
// No operation performed. | |
} | |
/** | |
* An object environment feature flags. | |
* | |
* @static | |
* @memberOf _ | |
* @type Object | |
*/ | |
var support = lodash.support = {}; | |
(function(x) { | |
var Ctor = function() { this.x = x; }, | |
object = { '0': x, 'length': x }, | |
props = []; | |
Ctor.prototype = { 'valueOf': x, 'y': x }; | |
for (var key in new Ctor) { props.push(key); } | |
/** | |
* Detect if the DOM is supported. | |
* | |
* @memberOf _.support | |
* @type boolean | |
*/ | |
try { | |
support.dom = document.createDocumentFragment().nodeType === 11; | |
} catch(e) { | |
support.dom = false; | |
} | |
}(1, 0)); | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates a lazy wrapper object which wraps `value` to enable lazy evaluation. | |
* | |
* @private | |
* @param {*} value The value to wrap. | |
*/ | |
function LazyWrapper(value) { | |
this.__wrapped__ = value; | |
this.__actions__ = null; | |
this.__dir__ = 1; | |
this.__dropCount__ = 0; | |
this.__filtered__ = false; | |
this.__iteratees__ = null; | |
this.__takeCount__ = POSITIVE_INFINITY; | |
this.__views__ = null; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* | |
* Creates a cache object to store unique values. | |
* | |
* @private | |
* @param {Array} [values] The values to cache. | |
*/ | |
function SetCache(values) { | |
var length = values ? values.length : 0; | |
this.data = { 'hash': nativeCreate(null), 'set': new Set }; | |
while (length--) { | |
this.push(values[length]); | |
} | |
} | |
/** | |
* Checks if `value` is in `cache` mimicking the return signature of | |
* `_.indexOf` by returning `0` if the value is found, else `-1`. | |
* | |
* @private | |
* @param {Object} cache The cache to search. | |
* @param {*} value The value to search for. | |
* @returns {number} Returns `0` if `value` is found, else `-1`. | |
*/ | |
function cacheIndexOf(cache, value) { | |
var data = cache.data, | |
result = (typeof value == 'string' || isObject(value)) ? data.set.has(value) : data.hash[value]; | |
return result ? 0 : -1; | |
} | |
/** | |
* Adds `value` to the cache. | |
* | |
* @private | |
* @name push | |
* @memberOf SetCache | |
* @param {*} value The value to cache. | |
*/ | |
function cachePush(value) { | |
var data = this.data; | |
if (typeof value == 'string' || isObject(value)) { | |
data.set.add(value); | |
} else { | |
data.hash[value] = true; | |
} | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* 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 arrayCopy(source, array) { | |
var index = -1, | |
length = source.length; | |
array || (array = Array(length)); | |
while (++index < length) { | |
array[index] = source[index]; | |
} | |
return array; | |
} | |
/** | |
* A specialized version of `_.forEach` for arrays without support for callback | |
* shorthands and `this` binding. | |
* | |
* @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.length; | |
while (++index < length) { | |
if (iteratee(array[index], index, array) === false) { | |
break; | |
} | |
} | |
return array; | |
} | |
/** | |
* A specialized version of `_.every` for arrays without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array} array The array to iterate over. | |
* @param {Function} predicate The function invoked per iteration. | |
* @returns {boolean} Returns `true` if all elements pass the predicate check, | |
* else `false`. | |
*/ | |
function arrayEvery(array, predicate) { | |
var index = -1, | |
length = array.length; | |
while (++index < length) { | |
if (!predicate(array[index], index, array)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* A specialized version of `_.map` for arrays without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array} array The array to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @returns {Array} Returns the new mapped array. | |
*/ | |
function arrayMap(array, iteratee) { | |
var index = -1, | |
length = array.length, | |
result = Array(length); | |
while (++index < length) { | |
result[index] = iteratee(array[index], index, array); | |
} | |
return result; | |
} | |
/** | |
* A specialized version of `_.reduce` for arrays without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array} array The array to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @param {*} [accumulator] The initial value. | |
* @param {boolean} [initFromArray] Specify using the first element of `array` | |
* as the initial value. | |
* @returns {*} Returns the accumulated value. | |
*/ | |
function arrayReduce(array, iteratee, accumulator, initFromArray) { | |
var index = -1, | |
length = array.length; | |
if (initFromArray && length) { | |
accumulator = array[++index]; | |
} | |
while (++index < length) { | |
accumulator = iteratee(accumulator, array[index], index, array); | |
} | |
return accumulator; | |
} | |
/** | |
* A specialized version of `_.some` for arrays without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array} array The array to iterate over. | |
* @param {Function} predicate The function invoked per iteration. | |
* @returns {boolean} Returns `true` if any element passes the predicate check, | |
* else `false`. | |
*/ | |
function arraySome(array, predicate) { | |
var index = -1, | |
length = array.length; | |
while (++index < length) { | |
if (predicate(array[index], index, array)) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* Used by `_.defaults` to customize its `_.assign` use. | |
* | |
* @private | |
* @param {*} objectValue The destination object property value. | |
* @param {*} sourceValue The source object property value. | |
* @returns {*} Returns the value to assign to the destination object. | |
*/ | |
function assignDefaults(objectValue, sourceValue) { | |
return objectValue === undefined ? sourceValue : objectValue; | |
} | |
/** | |
* A specialized version of `_.assign` for customizing assigned values without | |
* support for argument juggling, multiple sources, and `this` binding `customizer` | |
* functions. | |
* | |
* @private | |
* @param {Object} object The destination object. | |
* @param {Object} source The source object. | |
* @param {Function} customizer The function to customize assigned values. | |
* @returns {Object} Returns `object`. | |
*/ | |
function assignWith(object, source, customizer) { | |
var index = -1, | |
props = keys(source), | |
length = props.length; | |
while (++index < length) { | |
var key = props[index], | |
value = object[key], | |
result = customizer(value, source[key], key, object, source); | |
if ((result === result ? (result !== value) : (value === value)) || | |
(value === undefined && !(key in object))) { | |
object[key] = result; | |
} | |
} | |
return object; | |
} | |
/** | |
* The base implementation of `_.assign` without support for argument juggling, | |
* multiple sources, and `customizer` functions. | |
* | |
* @private | |
* @param {Object} object The destination object. | |
* @param {Object} source The source object. | |
* @returns {Object} Returns `object`. | |
*/ | |
function baseAssign(object, source) { | |
return source == null | |
? object | |
: baseCopy(source, keys(source), object); | |
} | |
/** | |
* Copies properties of `source` to `object`. | |
* | |
* @private | |
* @param {Object} source The object to copy properties from. | |
* @param {Array} props The property names to copy. | |
* @param {Object} [object={}] The object to copy properties to. | |
* @returns {Object} Returns `object`. | |
*/ | |
function baseCopy(source, props, object) { | |
object || (object = {}); | |
var index = -1, | |
length = props.length; | |
while (++index < length) { | |
var key = props[index]; | |
object[key] = source[key]; | |
} | |
return object; | |
} | |
/** | |
* The base implementation of `_.callback` which supports specifying the | |
* number of arguments to provide to `func`. | |
* | |
* @private | |
* @param {*} [func=_.identity] The value to convert to a callback. | |
* @param {*} [thisArg] The `this` binding of `func`. | |
* @param {number} [argCount] The number of arguments to provide to `func`. | |
* @returns {Function} Returns the callback. | |
*/ | |
function baseCallback(func, thisArg, argCount) { | |
var type = typeof func; | |
if (type == 'function') { | |
return thisArg === undefined | |
? func | |
: bindCallback(func, thisArg, argCount); | |
} | |
if (func == null) { | |
return identity; | |
} | |
if (type == 'object') { | |
return baseMatches(func); | |
} | |
return thisArg === undefined | |
? property(func) | |
: baseMatchesProperty(func, thisArg); | |
} | |
/** | |
* The base implementation of `_.clone` without support for argument juggling | |
* and `this` binding `customizer` functions. | |
* | |
* @private | |
* @param {*} value The value to clone. | |
* @param {boolean} [isDeep] Specify a deep clone. | |
* @param {Function} [customizer] The function to customize cloning values. | |
* @param {string} [key] The key of `value`. | |
* @param {Object} [object] The object `value` belongs to. | |
* @param {Array} [stackA=[]] Tracks traversed source objects. | |
* @param {Array} [stackB=[]] Associates clones with source counterparts. | |
* @returns {*} Returns the cloned value. | |
*/ | |
function baseClone(value, isDeep, customizer, key, object, stackA, stackB) { | |
var result; | |
if (customizer) { | |
result = object ? customizer(value, key, object) : customizer(value); | |
} | |
if (result !== undefined) { | |
return result; | |
} | |
if (!isObject(value)) { | |
return value; | |
} | |
var isArr = isArray(value); | |
if (isArr) { | |
result = initCloneArray(value); | |
if (!isDeep) { | |
return arrayCopy(value, result); | |
} | |
} else { | |
var tag = objToString.call(value), | |
isFunc = tag == funcTag; | |
if (tag == objectTag || tag == argsTag || (isFunc && !object)) { | |
result = initCloneObject(isFunc ? {} : value); | |
if (!isDeep) { | |
return baseAssign(result, value); | |
} | |
} else { | |
return cloneableTags[tag] | |
? initCloneByTag(value, tag, isDeep) | |
: (object ? value : {}); | |
} | |
} | |
// Check for circular references and return corresponding clone. | |
stackA || (stackA = []); | |
stackB || (stackB = []); | |
var length = stackA.length; | |
while (length--) { | |
if (stackA[length] == value) { | |
return stackB[length]; | |
} | |
} | |
// Add the source value to the stack of traversed objects and associate it with its clone. | |
stackA.push(value); | |
stackB.push(result); | |
// Recursively populate clone (susceptible to call stack limits). | |
(isArr ? arrayEach : baseForOwn)(value, function(subValue, key) { | |
result[key] = baseClone(subValue, isDeep, customizer, key, value, stackA, stackB); | |
}); | |
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. | |
*/ | |
var baseCreate = (function() { | |
function object() {} | |
return function(prototype) { | |
if (isObject(prototype)) { | |
object.prototype = prototype; | |
var result = new object; | |
object.prototype = null; | |
} | |
return result || {}; | |
}; | |
}()); | |
/** | |
* The base implementation of `_.delay` and `_.defer` which accepts an index | |
* of where to slice the arguments to provide to `func`. | |
* | |
* @private | |
* @param {Function} func The function to delay. | |
* @param {number} wait The number of milliseconds to delay invocation. | |
* @param {Object} args The arguments provide to `func`. | |
* @returns {number} Returns the timer id. | |
*/ | |
function baseDelay(func, wait, args) { | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
return setTimeout(function() { func.apply(undefined, args); }, wait); | |
} | |
/** | |
* The base implementation of `_.difference` which accepts a single array | |
* of values to exclude. | |
* | |
* @private | |
* @param {Array} array The array to inspect. | |
* @param {Array} values The values to exclude. | |
* @returns {Array} Returns the new array of filtered values. | |
*/ | |
function baseDifference(array, values) { | |
var length = array ? array.length : 0, | |
result = []; | |
if (!length) { | |
return result; | |
} | |
var index = -1, | |
indexOf = getIndexOf(), | |
isCommon = indexOf == baseIndexOf, | |
cache = (isCommon && values.length >= 200) ? createCache(values) : null, | |
valuesLength = values.length; | |
if (cache) { | |
indexOf = cacheIndexOf; | |
isCommon = false; | |
values = cache; | |
} | |
outer: | |
while (++index < length) { | |
var value = array[index]; | |
if (isCommon && value === value) { | |
var valuesIndex = valuesLength; | |
while (valuesIndex--) { | |
if (values[valuesIndex] === value) { | |
continue outer; | |
} | |
} | |
result.push(value); | |
} | |
else if (indexOf(values, value, 0) < 0) { | |
result.push(value); | |
} | |
} | |
return result; | |
} | |
/** | |
* The base implementation of `_.forEach` without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @returns {Array|Object|string} Returns `collection`. | |
*/ | |
var baseEach = createBaseEach(baseForOwn); | |
/** | |
* The base implementation of `_.every` without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} predicate The function invoked per iteration. | |
* @returns {boolean} Returns `true` if all elements pass the predicate check, | |
* else `false` | |
*/ | |
function baseEvery(collection, predicate) { | |
var result = true; | |
baseEach(collection, function(value, index, collection) { | |
result = !!predicate(value, index, collection); | |
return result; | |
}); | |
return result; | |
} | |
/** | |
* The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`, | |
* without support for callback shorthands and `this` binding, which iterates | |
* over `collection` using the provided `eachFunc`. | |
* | |
* @private | |
* @param {Array|Object|string} collection The collection to search. | |
* @param {Function} predicate The function invoked per iteration. | |
* @param {Function} eachFunc The function to iterate over `collection`. | |
* @param {boolean} [retKey] Specify returning the key of the found element | |
* instead of the element itself. | |
* @returns {*} Returns the found element or its key, else `undefined`. | |
*/ | |
function baseFind(collection, predicate, eachFunc, retKey) { | |
var result; | |
eachFunc(collection, function(value, key, collection) { | |
if (predicate(value, key, collection)) { | |
result = retKey ? key : value; | |
return false; | |
} | |
}); | |
return result; | |
} | |
/** | |
* The base implementation of `_.flatten` with added support for restricting | |
* flattening and specifying the start index. | |
* | |
* @private | |
* @param {Array} array The array to flatten. | |
* @param {boolean} [isDeep] Specify a deep flatten. | |
* @param {boolean} [isStrict] Restrict flattening to arrays-like objects. | |
* @returns {Array} Returns the new flattened array. | |
*/ | |
function baseFlatten(array, isDeep, isStrict) { | |
var index = -1, | |
length = array.length, | |
resIndex = -1, | |
result = []; | |
while (++index < length) { | |
var value = array[index]; | |
if (isObjectLike(value) && isArrayLike(value) && | |
(isStrict || isArray(value) || isArguments(value))) { | |
if (isDeep) { | |
// Recursively flatten arrays (susceptible to call stack limits). | |
value = baseFlatten(value, isDeep, isStrict); | |
} | |
var valIndex = -1, | |
valLength = value.length; | |
while (++valIndex < valLength) { | |
result[++resIndex] = value[valIndex]; | |
} | |
} else if (!isStrict) { | |
result[++resIndex] = value; | |
} | |
} | |
return result; | |
} | |
/** | |
* The base implementation of `baseForIn` and `baseForOwn` which iterates | |
* over `object` properties returned by `keysFunc` invoking `iteratee` for | |
* each property. Iteratee functions may exit iteration early by explicitly | |
* returning `false`. | |
* | |
* @private | |
* @param {Object} object The object to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @param {Function} keysFunc The function to get the keys of `object`. | |
* @returns {Object} Returns `object`. | |
*/ | |
var baseFor = createBaseFor(); | |
/** | |
* The base implementation of `_.forIn` without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Object} object The object to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @returns {Object} Returns `object`. | |
*/ | |
function baseForIn(object, iteratee) { | |
return baseFor(object, iteratee, keysIn); | |
} | |
/** | |
* The base implementation of `_.forOwn` without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Object} object The object to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @returns {Object} Returns `object`. | |
*/ | |
function baseForOwn(object, iteratee) { | |
return baseFor(object, iteratee, keys); | |
} | |
/** | |
* The base implementation of `get` without support for string paths | |
* and default values. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @param {Array} path The path of the property to get. | |
* @param {string} [pathKey] The key representation of path. | |
* @returns {*} Returns the resolved value. | |
*/ | |
function baseGet(object, path, pathKey) { | |
if (object == null) { | |
return; | |
} | |
if (pathKey !== undefined && pathKey in toObject(object)) { | |
path = [pathKey]; | |
} | |
var index = 0, | |
length = path.length; | |
while (object != null && index < length) { | |
object = object[path[index++]]; | |
} | |
return (index && index == length) ? object : undefined; | |
} | |
/** | |
* The base implementation of `_.isEqual` without support for `this` binding | |
* `customizer` functions. | |
* | |
* @private | |
* @param {*} value The value to compare. | |
* @param {*} other The other value to compare. | |
* @param {Function} [customizer] The function to customize comparing values. | |
* @param {boolean} [isLoose] Specify performing partial comparisons. | |
* @param {Array} [stackA] Tracks traversed `value` objects. | |
* @param {Array} [stackB] Tracks traversed `other` objects. | |
* @returns {boolean} Returns `true` if the values are equivalent, else `false`. | |
*/ | |
function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) { | |
if (value === other) { | |
return true; | |
} | |
if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) { | |
return value !== value && other !== other; | |
} | |
return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB); | |
} | |
/** | |
* A specialized version of `baseIsEqual` for arrays and objects which performs | |
* deep comparisons and tracks traversed objects enabling objects with circular | |
* references to be compared. | |
* | |
* @private | |
* @param {Object} object The object to compare. | |
* @param {Object} other The other object to compare. | |
* @param {Function} equalFunc The function to determine equivalents of values. | |
* @param {Function} [customizer] The function to customize comparing objects. | |
* @param {boolean} [isLoose] Specify performing partial comparisons. | |
* @param {Array} [stackA=[]] Tracks traversed `value` objects. | |
* @param {Array} [stackB=[]] Tracks traversed `other` objects. | |
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`. | |
*/ | |
function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) { | |
var objIsArr = isArray(object), | |
othIsArr = isArray(other), | |
objTag = arrayTag, | |
othTag = arrayTag; | |
if (!objIsArr) { | |
objTag = objToString.call(object); | |
if (objTag == argsTag) { | |
objTag = objectTag; | |
} else if (objTag != objectTag) { | |
objIsArr = isTypedArray(object); | |
} | |
} | |
if (!othIsArr) { | |
othTag = objToString.call(other); | |
if (othTag == argsTag) { | |
othTag = objectTag; | |
} else if (othTag != objectTag) { | |
othIsArr = isTypedArray(other); | |
} | |
} | |
var objIsObj = objTag == objectTag, | |
othIsObj = othTag == objectTag, | |
isSameTag = objTag == othTag; | |
if (isSameTag && !(objIsArr || objIsObj)) { | |
return equalByTag(object, other, objTag); | |
} | |
if (!isLoose) { | |
var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'), | |
othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__'); | |
if (objIsWrapped || othIsWrapped) { | |
return equalFunc(objIsWrapped ? object.value() : object, othIsWrapped ? other.value() : other, customizer, isLoose, stackA, stackB); | |
} | |
} | |
if (!isSameTag) { | |
return false; | |
} | |
// Assume cyclic values are equal. | |
// For more information on detecting circular references see https://es5.github.io/#JO. | |
stackA || (stackA = []); | |
stackB || (stackB = []); | |
var length = stackA.length; | |
while (length--) { | |
if (stackA[length] == object) { | |
return stackB[length] == other; | |
} | |
} | |
// Add `object` and `other` to the stack of traversed objects. | |
stackA.push(object); | |
stackB.push(other); | |
var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB); | |
stackA.pop(); | |
stackB.pop(); | |
return result; | |
} | |
/** | |
* The base implementation of `_.isMatch` without support for callback | |
* shorthands and `this` binding. | |
* | |
* @private | |
* @param {Object} object The object to inspect. | |
* @param {Array} matchData The propery names, values, and compare flags to match. | |
* @param {Function} [customizer] The function to customize comparing objects. | |
* @returns {boolean} Returns `true` if `object` is a match, else `false`. | |
*/ | |
function baseIsMatch(object, matchData, customizer) { | |
var index = matchData.length, | |
length = index, | |
noCustomizer = !customizer; | |
if (object == null) { | |
return !length; | |
} | |
object = toObject(object); | |
while (index--) { | |
var data = matchData[index]; | |
if ((noCustomizer && data[2]) | |
? data[1] !== object[data[0]] | |
: !(data[0] in object) | |
) { | |
return false; | |
} | |
} | |
while (++index < length) { | |
data = matchData[index]; | |
var key = data[0], | |
objValue = object[key], | |
srcValue = data[1]; | |
if (noCustomizer && data[2]) { | |
if (objValue === undefined && !(key in object)) { | |
return false; | |
} | |
} else { | |
var result = customizer ? customizer(objValue, srcValue, key) : undefined; | |
if (!(result === undefined ? baseIsEqual(srcValue, objValue, customizer, true) : result)) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
/** | |
* The base implementation of `_.map` without support for callback shorthands | |
* and `this` binding. | |
* | |
* @private | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @returns {Array} Returns the new mapped array. | |
*/ | |
function baseMap(collection, iteratee) { | |
var index = -1, | |
result = isArrayLike(collection) ? Array(collection.length) : []; | |
baseEach(collection, function(value, key, collection) { | |
result[++index] = iteratee(value, key, collection); | |
}); | |
return result; | |
} | |
/** | |
* The base implementation of `_.matches` which does not clone `source`. | |
* | |
* @private | |
* @param {Object} source The object of property values to match. | |
* @returns {Function} Returns the new function. | |
*/ | |
function baseMatches(source) { | |
var matchData = getMatchData(source); | |
if (matchData.length == 1 && matchData[0][2]) { | |
var key = matchData[0][0], | |
value = matchData[0][1]; | |
return function(object) { | |
if (object == null) { | |
return false; | |
} | |
return object[key] === value && (value !== undefined || (key in toObject(object))); | |
}; | |
} | |
return function(object) { | |
return baseIsMatch(object, matchData); | |
}; | |
} | |
/** | |
* The base implementation of `_.matchesProperty` which does not clone `srcValue`. | |
* | |
* @private | |
* @param {string} path The path of the property to get. | |
* @param {*} srcValue The value to compare. | |
* @returns {Function} Returns the new function. | |
*/ | |
function baseMatchesProperty(path, srcValue) { | |
var isArr = isArray(path), | |
isCommon = isKey(path) && isStrictComparable(srcValue), | |
pathKey = (path + ''); | |
path = toPath(path); | |
return function(object) { | |
if (object == null) { | |
return false; | |
} | |
var key = pathKey; | |
object = toObject(object); | |
if ((isArr || !isCommon) && !(key in object)) { | |
object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); | |
if (object == null) { | |
return false; | |
} | |
key = last(path); | |
object = toObject(object); | |
} | |
return object[key] === srcValue | |
? (srcValue !== undefined || (key in object)) | |
: baseIsEqual(srcValue, object[key], undefined, true); | |
}; | |
} | |
/** | |
* The base implementation of `_.property` without support for deep paths. | |
* | |
* @private | |
* @param {string} key The key of the property to get. | |
* @returns {Function} Returns the new function. | |
*/ | |
function baseProperty(key) { | |
return function(object) { | |
return object == null ? undefined : object[key]; | |
}; | |
} | |
/** | |
* A specialized version of `baseProperty` which supports deep paths. | |
* | |
* @private | |
* @param {Array|string} path The path of the property to get. | |
* @returns {Function} Returns the new function. | |
*/ | |
function basePropertyDeep(path) { | |
var pathKey = (path + ''); | |
path = toPath(path); | |
return function(object) { | |
return baseGet(object, path, pathKey); | |
}; | |
} | |
/** | |
* The base implementation of `_.reduce` and `_.reduceRight` without support | |
* for callback shorthands and `this` binding, which iterates over `collection` | |
* using the provided `eachFunc`. | |
* | |
* @private | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @param {*} accumulator The initial value. | |
* @param {boolean} initFromCollection Specify using the first or last element | |
* of `collection` as the initial value. | |
* @param {Function} eachFunc The function to iterate over `collection`. | |
* @returns {*} Returns the accumulated value. | |
*/ | |
function baseReduce(collection, iteratee, accumulator, initFromCollection, eachFunc) { | |
eachFunc(collection, function(value, index, collection) { | |
accumulator = initFromCollection | |
? (initFromCollection = false, value) | |
: iteratee(accumulator, value, index, collection); | |
}); | |
return accumulator; | |
} | |
/** | |
* The base implementation of `setData` without support for hot loop detection. | |
* | |
* @private | |
* @param {Function} func The function to associate metadata with. | |
* @param {*} data The metadata. | |
* @returns {Function} Returns `func`. | |
*/ | |
var baseSetData = !metaMap ? identity : function(func, data) { | |
metaMap.set(func, data); | |
return func; | |
}; | |
/** | |
* 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; | |
start = start == null ? 0 : (+start || 0); | |
if (start < 0) { | |
start = -start > length ? 0 : (length + start); | |
} | |
end = (end === undefined || end > length) ? length : (+end || 0); | |
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; | |
} | |
/** | |
* The base implementation of `_.values` and `_.valuesIn` which creates an | |
* array of `object` property values corresponding to the property names | |
* of `props`. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @param {Array} props The property names to get values for. | |
* @returns {Object} Returns the array of property values. | |
*/ | |
function baseValues(object, props) { | |
var index = -1, | |
length = props.length, | |
result = Array(length); | |
while (++index < length) { | |
result[index] = object[props[index]]; | |
} | |
return result; | |
} | |
/** | |
* Performs a binary search of `array` to determine the index at which `value` | |
* should be inserted into `array` in order to maintain its sort order. | |
* | |
* @private | |
* @param {Array} array The sorted array to inspect. | |
* @param {*} value The value to evaluate. | |
* @param {boolean} [retHighest] Specify returning the highest qualified index. | |
* @returns {number} Returns the index at which `value` should be inserted | |
* into `array`. | |
*/ | |
function binaryIndex(array, value, retHighest) { | |
var low = 0, | |
high = array ? array.length : low; | |
if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) { | |
while (low < high) { | |
var mid = (low + high) >>> 1, | |
computed = array[mid]; | |
if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { | |
low = mid + 1; | |
} else { | |
high = mid; | |
} | |
} | |
return high; | |
} | |
return binaryIndexBy(array, value, identity, retHighest); | |
} | |
/** | |
* This function is like `binaryIndex` except that it invokes `iteratee` for | |
* `value` and each element of `array` to compute their sort ranking. The | |
* iteratee is invoked with one argument; (value). | |
* | |
* @private | |
* @param {Array} array The sorted array to inspect. | |
* @param {*} value The value to evaluate. | |
* @param {Function} iteratee The function invoked per iteration. | |
* @param {boolean} [retHighest] Specify returning the highest qualified index. | |
* @returns {number} Returns the index at which `value` should be inserted | |
* into `array`. | |
*/ | |
function binaryIndexBy(array, value, iteratee, retHighest) { | |
value = iteratee(value); | |
var low = 0, | |
high = array ? array.length : 0, | |
valIsNaN = value !== value, | |
valIsNull = value === null, | |
valIsUndef = value === undefined; | |
while (low < high) { | |
var mid = floor((low + high) / 2), | |
computed = iteratee(array[mid]), | |
isDef = computed !== undefined, | |
isReflexive = computed === computed; | |
if (valIsNaN) { | |
var setLow = isReflexive || retHighest; | |
} else if (valIsNull) { | |
setLow = isReflexive && isDef && (retHighest || computed != null); | |
} else if (valIsUndef) { | |
setLow = isReflexive && (retHighest || isDef); | |
} else if (computed == null) { | |
setLow = false; | |
} else { | |
setLow = retHighest ? (computed <= value) : (computed < value); | |
} | |
if (setLow) { | |
low = mid + 1; | |
} else { | |
high = mid; | |
} | |
} | |
return nativeMin(high, MAX_ARRAY_INDEX); | |
} | |
/** | |
* A specialized version of `baseCallback` which only supports `this` binding | |
* and specifying the number of arguments to provide to `func`. | |
* | |
* @private | |
* @param {Function} func The function to bind. | |
* @param {*} thisArg The `this` binding of `func`. | |
* @param {number} [argCount] The number of arguments to provide to `func`. | |
* @returns {Function} Returns the callback. | |
*/ | |
function bindCallback(func, thisArg, argCount) { | |
if (typeof func != 'function') { | |
return identity; | |
} | |
if (thisArg === undefined) { | |
return func; | |
} | |
switch (argCount) { | |
case 1: return function(value) { | |
return func.call(thisArg, value); | |
}; | |
case 3: return function(value, index, collection) { | |
return func.call(thisArg, value, index, collection); | |
}; | |
case 4: return function(accumulator, value, index, collection) { | |
return func.call(thisArg, accumulator, value, index, collection); | |
}; | |
case 5: return function(value, other, key, object, source) { | |
return func.call(thisArg, value, other, key, object, source); | |
}; | |
} | |
return function() { | |
return func.apply(thisArg, arguments); | |
}; | |
} | |
/** | |
* Creates a clone of the given array buffer. | |
* | |
* @private | |
* @param {ArrayBuffer} buffer The array buffer to clone. | |
* @returns {ArrayBuffer} Returns the cloned array buffer. | |
*/ | |
function bufferClone(buffer) { | |
return bufferSlice.call(buffer, 0); | |
} | |
if (!bufferSlice) { | |
// PhantomJS has `ArrayBuffer` and `Uint8Array` but not `Float64Array`. | |
bufferClone = !(ArrayBuffer && Uint8Array) ? constant(null) : function(buffer) { | |
var byteLength = buffer.byteLength, | |
floatLength = Float64Array ? floor(byteLength / FLOAT64_BYTES_PER_ELEMENT) : 0, | |
offset = floatLength * FLOAT64_BYTES_PER_ELEMENT, | |
result = new ArrayBuffer(byteLength); | |
if (floatLength) { | |
var view = new Float64Array(result, 0, floatLength); | |
view.set(new Float64Array(buffer, 0, floatLength)); | |
} | |
if (byteLength != offset) { | |
view = new Uint8Array(result, offset); | |
view.set(new Uint8Array(buffer, offset)); | |
} | |
return result; | |
}; | |
} | |
/** | |
* Creates an array that is the composition of partially applied arguments, | |
* placeholders, and provided arguments into a single array of arguments. | |
* | |
* @private | |
* @param {Array|Object} args The provided arguments. | |
* @param {Array} partials The arguments to prepend to those provided. | |
* @param {Array} holders The `partials` placeholder indexes. | |
* @returns {Array} Returns the new array of composed arguments. | |
*/ | |
function composeArgs(args, partials, holders) { | |
var holdersLength = holders.length, | |
argsIndex = -1, | |
argsLength = nativeMax(args.length - holdersLength, 0), | |
leftIndex = -1, | |
leftLength = partials.length, | |
result = Array(argsLength + leftLength); | |
while (++leftIndex < leftLength) { | |
result[leftIndex] = partials[leftIndex]; | |
} | |
while (++argsIndex < holdersLength) { | |
result[holders[argsIndex]] = args[argsIndex]; | |
} | |
while (argsLength--) { | |
result[leftIndex++] = args[argsIndex++]; | |
} | |
return result; | |
} | |
/** | |
* This function is like `composeArgs` except that the arguments composition | |
* is tailored for `_.partialRight`. | |
* | |
* @private | |
* @param {Array|Object} args The provided arguments. | |
* @param {Array} partials The arguments to append to those provided. | |
* @param {Array} holders The `partials` placeholder indexes. | |
* @returns {Array} Returns the new array of composed arguments. | |
*/ | |
function composeArgsRight(args, partials, holders) { | |
var holdersIndex = -1, | |
holdersLength = holders.length, | |
argsIndex = -1, | |
argsLength = nativeMax(args.length - holdersLength, 0), | |
rightIndex = -1, | |
rightLength = partials.length, | |
result = Array(argsLength + rightLength); | |
while (++argsIndex < argsLength) { | |
result[argsIndex] = args[argsIndex]; | |
} | |
var offset = argsIndex; | |
while (++rightIndex < rightLength) { | |
result[offset + rightIndex] = partials[rightIndex]; | |
} | |
while (++holdersIndex < holdersLength) { | |
result[offset + holders[holdersIndex]] = args[argsIndex++]; | |
} | |
return result; | |
} | |
/** | |
* Creates a function that assigns properties of source object(s) to a given | |
* destination object. | |
* | |
* **Note:** This function is used to create `_.assign`, `_.defaults`, and `_.merge`. | |
* | |
* @private | |
* @param {Function} assigner The function to assign values. | |
* @returns {Function} Returns the new assigner function. | |
*/ | |
function createAssigner(assigner) { | |
return restParam(function(object, sources) { | |
var index = -1, | |
length = object == null ? 0 : sources.length, | |
customizer = length > 2 ? sources[length - 2] : undefined, | |
guard = length > 2 ? sources[2] : undefined, | |
thisArg = length > 1 ? sources[length - 1] : undefined; | |
if (typeof customizer == 'function') { | |
customizer = bindCallback(customizer, thisArg, 5); | |
length -= 2; | |
} else { | |
customizer = typeof thisArg == 'function' ? thisArg : undefined; | |
length -= (customizer ? 1 : 0); | |
} | |
if (guard && isIterateeCall(sources[0], sources[1], guard)) { | |
customizer = length < 3 ? undefined : customizer; | |
length = 1; | |
} | |
while (++index < length) { | |
var source = sources[index]; | |
if (source) { | |
assigner(object, source, customizer); | |
} | |
} | |
return object; | |
}); | |
} | |
/** | |
* Creates a `baseEach` or `baseEachRight` function. | |
* | |
* @private | |
* @param {Function} eachFunc The function to iterate over a collection. | |
* @param {boolean} [fromRight] Specify iterating from right to left. | |
* @returns {Function} Returns the new base function. | |
*/ | |
function createBaseEach(eachFunc, fromRight) { | |
return function(collection, iteratee) { | |
var length = collection ? getLength(collection) : 0; | |
if (!isLength(length)) { | |
return eachFunc(collection, iteratee); | |
} | |
var index = fromRight ? length : -1, | |
iterable = toObject(collection); | |
while ((fromRight ? index-- : ++index < length)) { | |
if (iteratee(iterable[index], index, iterable) === false) { | |
break; | |
} | |
} | |
return collection; | |
}; | |
} | |
/** | |
* Creates a base function for `_.forIn` or `_.forInRight`. | |
* | |
* @private | |
* @param {boolean} [fromRight] Specify iterating from right to left. | |
* @returns {Function} Returns the new base function. | |
*/ | |
function createBaseFor(fromRight) { | |
return function(object, iteratee, keysFunc) { | |
var iterable = toObject(object), | |
props = keysFunc(object), | |
length = props.length, | |
index = fromRight ? length : -1; | |
while ((fromRight ? index-- : ++index < length)) { | |
var key = props[index]; | |
if (iteratee(iterable[key], key, iterable) === false) { | |
break; | |
} | |
} | |
return object; | |
}; | |
} | |
/** | |
* Creates a function that wraps `func` and invokes it with the `this` | |
* binding of `thisArg`. | |
* | |
* @private | |
* @param {Function} func The function to bind. | |
* @param {*} [thisArg] The `this` binding of `func`. | |
* @returns {Function} Returns the new bound function. | |
*/ | |
function createBindWrapper(func, thisArg) { | |
var Ctor = createCtorWrapper(func); | |
function wrapper() { | |
var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; | |
return fn.apply(thisArg, arguments); | |
} | |
return wrapper; | |
} | |
/** | |
* Creates a `Set` cache object to optimize linear searches of large arrays. | |
* | |
* @private | |
* @param {Array} [values] The values to cache. | |
* @returns {null|Object} Returns the new cache object if `Set` is supported, else `null`. | |
*/ | |
var createCache = !(nativeCreate && Set) ? constant(null) : function(values) { | |
return new SetCache(values); | |
}; | |
/** | |
* Creates a function that produces an instance of `Ctor` regardless of | |
* whether it was invoked as part of a `new` expression or by `call` or `apply`. | |
* | |
* @private | |
* @param {Function} Ctor The constructor to wrap. | |
* @returns {Function} Returns the new wrapped function. | |
*/ | |
function createCtorWrapper(Ctor) { | |
return function() { | |
// Use a `switch` statement to work with class constructors. | |
// See https://people.mozilla.org/~jorendorff/es6-draft.html#sec-ecmascript-function-objects-call-thisargument-argumentslist | |
// for more details. | |
var args = arguments; | |
switch (args.length) { | |
case 0: return new Ctor; | |
case 1: return new Ctor(args[0]); | |
case 2: return new Ctor(args[0], args[1]); | |
case 3: return new Ctor(args[0], args[1], args[2]); | |
case 4: return new Ctor(args[0], args[1], args[2], args[3]); | |
case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]); | |
} | |
var thisBinding = baseCreate(Ctor.prototype), | |
result = Ctor.apply(thisBinding, args); | |
// Mimic the constructor's `return` behavior. | |
// See https://es5.github.io/#x13.2.2 for more details. | |
return isObject(result) ? result : thisBinding; | |
}; | |
} | |
/** | |
* Creates a `_.find` or `_.findLast` function. | |
* | |
* @private | |
* @param {Function} eachFunc The function to iterate over a collection. | |
* @param {boolean} [fromRight] Specify iterating from right to left. | |
* @returns {Function} Returns the new find function. | |
*/ | |
function createFind(eachFunc, fromRight) { | |
return function(collection, predicate, thisArg) { | |
predicate = getCallback(predicate, thisArg, 3); | |
if (isArray(collection)) { | |
var index = baseFindIndex(collection, predicate, fromRight); | |
return index > -1 ? collection[index] : undefined; | |
} | |
return baseFind(collection, predicate, eachFunc); | |
}; | |
} | |
/** | |
* Creates a function for `_.forEach` or `_.forEachRight`. | |
* | |
* @private | |
* @param {Function} arrayFunc The function to iterate over an array. | |
* @param {Function} eachFunc The function to iterate over a collection. | |
* @returns {Function} Returns the new each function. | |
*/ | |
function createForEach(arrayFunc, eachFunc) { | |
return function(collection, iteratee, thisArg) { | |
return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) | |
? arrayFunc(collection, iteratee) | |
: eachFunc(collection, bindCallback(iteratee, thisArg, 3)); | |
}; | |
} | |
/** | |
* Creates a `_.partial` or `_.partialRight` function. | |
* | |
* @private | |
* @param {boolean} flag The partial bit flag. | |
* @returns {Function} Returns the new partial function. | |
*/ | |
function createPartial(flag) { | |
var partialFunc = restParam(function(func, partials) { | |
var holders = replaceHolders(partials, partialFunc.placeholder); | |
return createWrapper(func, flag, null, partials, holders); | |
}); | |
return partialFunc; | |
} | |
/** | |
* Creates a function for `_.reduce` or `_.reduceRight`. | |
* | |
* @private | |
* @param {Function} arrayFunc The function to iterate over an array. | |
* @param {Function} eachFunc The function to iterate over a collection. | |
* @returns {Function} Returns the new each function. | |
*/ | |
function createReduce(arrayFunc, eachFunc) { | |
return function(collection, iteratee, accumulator, thisArg) { | |
var initFromArray = arguments.length < 3; | |
return (typeof iteratee == 'function' && thisArg === undefined && isArray(collection)) | |
? arrayFunc(collection, iteratee, accumulator, initFromArray) | |
: baseReduce(collection, getCallback(iteratee, thisArg, 4), accumulator, initFromArray, eachFunc); | |
}; | |
} | |
/** | |
* Creates a function that wraps `func` and invokes it with optional `this` | |
* binding of, partial application, and currying. | |
* | |
* @private | |
* @param {Function|string} func The function or method name to reference. | |
* @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. | |
* @param {*} [thisArg] The `this` binding of `func`. | |
* @param {Array} [partials] The arguments to prepend to those provided to the new function. | |
* @param {Array} [holders] The `partials` placeholder indexes. | |
* @param {Array} [partialsRight] The arguments to append to those provided to the new function. | |
* @param {Array} [holdersRight] The `partialsRight` placeholder indexes. | |
* @param {Array} [argPos] The argument positions of the new function. | |
* @param {number} [ary] The arity cap of `func`. | |
* @param {number} [arity] The arity of `func`. | |
* @returns {Function} Returns the new wrapped function. | |
*/ | |
function createHybridWrapper(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) { | |
var isAry = bitmask & ARY_FLAG, | |
isBind = bitmask & BIND_FLAG, | |
isBindKey = bitmask & BIND_KEY_FLAG, | |
isCurry = bitmask & CURRY_FLAG, | |
isCurryBound = bitmask & CURRY_BOUND_FLAG, | |
isCurryRight = bitmask & CURRY_RIGHT_FLAG, | |
Ctor = isBindKey ? null : createCtorWrapper(func); | |
function wrapper() { | |
// Avoid `arguments` object use disqualifying optimizations by | |
// converting it to an array before providing it to other functions. | |
var length = arguments.length, | |
index = length, | |
args = Array(length); | |
while (index--) { | |
args[index] = arguments[index]; | |
} | |
if (partials) { | |
args = composeArgs(args, partials, holders); | |
} | |
if (partialsRight) { | |
args = composeArgsRight(args, partialsRight, holdersRight); | |
} | |
if (isCurry || isCurryRight) { | |
var placeholder = wrapper.placeholder, | |
argsHolders = replaceHolders(args, placeholder); | |
length -= argsHolders.length; | |
if (length < arity) { | |
var newArgPos = argPos ? arrayCopy(argPos) : null, | |
newArity = nativeMax(arity - length, 0), | |
newsHolders = isCurry ? argsHolders : null, | |
newHoldersRight = isCurry ? null : argsHolders, | |
newPartials = isCurry ? args : null, | |
newPartialsRight = isCurry ? null : args; | |
bitmask |= (isCurry ? PARTIAL_FLAG : PARTIAL_RIGHT_FLAG); | |
bitmask &= ~(isCurry ? PARTIAL_RIGHT_FLAG : PARTIAL_FLAG); | |
if (!isCurryBound) { | |
bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); | |
} | |
var newData = [func, bitmask, thisArg, newPartials, newsHolders, newPartialsRight, newHoldersRight, newArgPos, ary, newArity], | |
result = createHybridWrapper.apply(undefined, newData); | |
if (isLaziable(func)) { | |
setData(result, newData); | |
} | |
result.placeholder = placeholder; | |
return result; | |
} | |
} | |
var thisBinding = isBind ? thisArg : this, | |
fn = isBindKey ? thisBinding[func] : func; | |
if (argPos) { | |
args = reorder(args, argPos); | |
} | |
if (isAry && ary < args.length) { | |
args.length = ary; | |
} | |
if (this && this !== root && this instanceof wrapper) { | |
fn = Ctor || createCtorWrapper(func); | |
} | |
return fn.apply(thisBinding, args); | |
} | |
return wrapper; | |
} | |
/** | |
* Creates a function that wraps `func` and invokes it with the optional `this` | |
* binding of `thisArg` and the `partials` prepended to those provided to | |
* the wrapper. | |
* | |
* @private | |
* @param {Function} func The function to partially apply arguments to. | |
* @param {number} bitmask The bitmask of flags. See `createWrapper` for more details. | |
* @param {*} thisArg The `this` binding of `func`. | |
* @param {Array} partials The arguments to prepend to those provided to the new function. | |
* @returns {Function} Returns the new bound function. | |
*/ | |
function createPartialWrapper(func, bitmask, thisArg, partials) { | |
var isBind = bitmask & BIND_FLAG, | |
Ctor = createCtorWrapper(func); | |
function wrapper() { | |
// Avoid `arguments` object use disqualifying optimizations by | |
// converting it to an array before providing it `func`. | |
var argsIndex = -1, | |
argsLength = arguments.length, | |
leftIndex = -1, | |
leftLength = partials.length, | |
args = Array(argsLength + leftLength); | |
while (++leftIndex < leftLength) { | |
args[leftIndex] = partials[leftIndex]; | |
} | |
while (argsLength--) { | |
args[leftIndex++] = arguments[++argsIndex]; | |
} | |
var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func; | |
return fn.apply(isBind ? thisArg : this, args); | |
} | |
return wrapper; | |
} | |
/** | |
* Creates a function that either curries or invokes `func` with optional | |
* `this` binding and partially applied arguments. | |
* | |
* @private | |
* @param {Function|string} func The function or method name to reference. | |
* @param {number} bitmask The bitmask of flags. | |
* The bitmask may be composed of the following flags: | |
* 1 - `_.bind` | |
* 2 - `_.bindKey` | |
* 4 - `_.curry` or `_.curryRight` of a bound function | |
* 8 - `_.curry` | |
* 16 - `_.curryRight` | |
* 32 - `_.partial` | |
* 64 - `_.partialRight` | |
* 128 - `_.rearg` | |
* 256 - `_.ary` | |
* @param {*} [thisArg] The `this` binding of `func`. | |
* @param {Array} [partials] The arguments to be partially applied. | |
* @param {Array} [holders] The `partials` placeholder indexes. | |
* @param {Array} [argPos] The argument positions of the new function. | |
* @param {number} [ary] The arity cap of `func`. | |
* @param {number} [arity] The arity of `func`. | |
* @returns {Function} Returns the new wrapped function. | |
*/ | |
function createWrapper(func, bitmask, thisArg, partials, holders, argPos, ary, arity) { | |
var isBindKey = bitmask & BIND_KEY_FLAG; | |
if (!isBindKey && typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
var length = partials ? partials.length : 0; | |
if (!length) { | |
bitmask &= ~(PARTIAL_FLAG | PARTIAL_RIGHT_FLAG); | |
partials = holders = null; | |
} | |
length -= (holders ? holders.length : 0); | |
if (bitmask & PARTIAL_RIGHT_FLAG) { | |
var partialsRight = partials, | |
holdersRight = holders; | |
partials = holders = null; | |
} | |
var data = isBindKey ? null : getData(func), | |
newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; | |
if (data) { | |
mergeData(newData, data); | |
bitmask = newData[1]; | |
arity = newData[9]; | |
} | |
newData[9] = arity == null | |
? (isBindKey ? 0 : func.length) | |
: (nativeMax(arity - length, 0) || 0); | |
if (bitmask == BIND_FLAG) { | |
var result = createBindWrapper(newData[0], newData[2]); | |
} else if ((bitmask == PARTIAL_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) && !newData[4].length) { | |
result = createPartialWrapper.apply(undefined, newData); | |
} else { | |
result = createHybridWrapper.apply(undefined, newData); | |
} | |
var setter = data ? baseSetData : setData; | |
return setter(result, newData); | |
} | |
/** | |
* A specialized version of `baseIsEqualDeep` for arrays with support for | |
* partial deep comparisons. | |
* | |
* @private | |
* @param {Array} array The array to compare. | |
* @param {Array} other The other array to compare. | |
* @param {Function} equalFunc The function to determine equivalents of values. | |
* @param {Function} [customizer] The function to customize comparing arrays. | |
* @param {boolean} [isLoose] Specify performing partial comparisons. | |
* @param {Array} [stackA] Tracks traversed `value` objects. | |
* @param {Array} [stackB] Tracks traversed `other` objects. | |
* @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. | |
*/ | |
function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) { | |
var index = -1, | |
arrLength = array.length, | |
othLength = other.length; | |
if (arrLength != othLength && !(isLoose && othLength > arrLength)) { | |
return false; | |
} | |
// Ignore non-index properties. | |
while (++index < arrLength) { | |
var arrValue = array[index], | |
othValue = other[index], | |
result = customizer ? customizer(isLoose ? othValue : arrValue, isLoose ? arrValue : othValue, index) : undefined; | |
if (result !== undefined) { | |
if (result) { | |
continue; | |
} | |
return false; | |
} | |
// Recursively compare arrays (susceptible to call stack limits). | |
if (isLoose) { | |
if (!arraySome(other, function(othValue) { | |
return arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB); | |
})) { | |
return false; | |
} | |
} else if (!(arrValue === othValue || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB))) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* A specialized version of `baseIsEqualDeep` for comparing objects of | |
* the same `toStringTag`. | |
* | |
* **Note:** This function only supports comparing values with tags of | |
* `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. | |
* | |
* @private | |
* @param {Object} value The object to compare. | |
* @param {Object} other The other object to compare. | |
* @param {string} tag The `toStringTag` of the objects to compare. | |
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`. | |
*/ | |
function equalByTag(object, other, tag) { | |
switch (tag) { | |
case boolTag: | |
case dateTag: | |
// Coerce dates and booleans to numbers, dates to milliseconds and booleans | |
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal. | |
return +object == +other; | |
case errorTag: | |
return object.name == other.name && object.message == other.message; | |
case numberTag: | |
// Treat `NaN` vs. `NaN` as equal. | |
return (object != +object) | |
? other != +other | |
: object == +other; | |
case regexpTag: | |
case stringTag: | |
// Coerce regexes to strings and treat strings primitives and string | |
// objects as equal. See https://es5.github.io/#x15.10.6.4 for more details. | |
return object == (other + ''); | |
} | |
return false; | |
} | |
/** | |
* A specialized version of `baseIsEqualDeep` for objects with support for | |
* partial deep comparisons. | |
* | |
* @private | |
* @param {Object} object The object to compare. | |
* @param {Object} other The other object to compare. | |
* @param {Function} equalFunc The function to determine equivalents of values. | |
* @param {Function} [customizer] The function to customize comparing values. | |
* @param {boolean} [isLoose] Specify performing partial comparisons. | |
* @param {Array} [stackA] Tracks traversed `value` objects. | |
* @param {Array} [stackB] Tracks traversed `other` objects. | |
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`. | |
*/ | |
function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) { | |
var objProps = keys(object), | |
objLength = objProps.length, | |
othProps = keys(other), | |
othLength = othProps.length; | |
if (objLength != othLength && !isLoose) { | |
return false; | |
} | |
var index = objLength; | |
while (index--) { | |
var key = objProps[index]; | |
if (!(isLoose ? key in other : hasOwnProperty.call(other, key))) { | |
return false; | |
} | |
} | |
var skipCtor = isLoose; | |
while (++index < objLength) { | |
key = objProps[index]; | |
var objValue = object[key], | |
othValue = other[key], | |
result = customizer ? customizer(isLoose ? othValue : objValue, isLoose? objValue : othValue, key) : undefined; | |
// Recursively compare objects (susceptible to call stack limits). | |
if (!(result === undefined ? equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB) : result)) { | |
return false; | |
} | |
skipCtor || (skipCtor = key == 'constructor'); | |
} | |
if (!skipCtor) { | |
var objCtor = object.constructor, | |
othCtor = other.constructor; | |
// Non `Object` object instances with different constructors are not equal. | |
if (objCtor != othCtor && | |
('constructor' in object && 'constructor' in other) && | |
!(typeof objCtor == 'function' && objCtor instanceof objCtor && | |
typeof othCtor == 'function' && othCtor instanceof othCtor)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* Gets the appropriate "callback" function. If the `_.callback` method is | |
* customized this function returns the custom method, otherwise it returns | |
* the `baseCallback` function. If arguments are provided the chosen function | |
* is invoked with them and its result is returned. | |
* | |
* @private | |
* @returns {Function} Returns the chosen function or its result. | |
*/ | |
function getCallback(func, thisArg, argCount) { | |
var result = lodash.callback || callback; | |
result = result === callback ? baseCallback : result; | |
return argCount ? result(func, thisArg, argCount) : result; | |
} | |
/** | |
* Gets metadata for `func`. | |
* | |
* @private | |
* @param {Function} func The function to query. | |
* @returns {*} Returns the metadata for `func`. | |
*/ | |
var getData = !metaMap ? noop : function(func) { | |
return metaMap.get(func); | |
}; | |
/** | |
* Gets the name of `func`. | |
* | |
* @private | |
* @param {Function} func The function to query. | |
* @returns {string} Returns the function name. | |
*/ | |
function getFuncName(func) { | |
var result = func.name, | |
array = realNames[result], | |
length = array ? array.length : 0; | |
while (length--) { | |
var data = array[length], | |
otherFunc = data.func; | |
if (otherFunc == null || otherFunc == func) { | |
return data.name; | |
} | |
} | |
return result; | |
} | |
/** | |
* Gets the appropriate "indexOf" function. If the `_.indexOf` method is | |
* customized this function returns the custom method, otherwise it returns | |
* the `baseIndexOf` function. If arguments are provided the chosen function | |
* is invoked with them and its result is returned. | |
* | |
* @private | |
* @returns {Function|number} Returns the chosen function or its result. | |
*/ | |
function getIndexOf(collection, target, fromIndex) { | |
var result = lodash.indexOf || indexOf; | |
result = result === indexOf ? baseIndexOf : result; | |
return collection ? result(collection, target, fromIndex) : result; | |
} | |
/** | |
* Gets the "length" property value of `object`. | |
* | |
* **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792) | |
* that affects Safari on at least iOS 8.1-8.3 ARM64. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @returns {*} Returns the "length" value. | |
*/ | |
var getLength = baseProperty('length'); | |
/** | |
* Gets the propery names, values, and compare flags of `object`. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @returns {Array} Returns the match data of `object`. | |
*/ | |
function getMatchData(object) { | |
var result = pairs(object), | |
length = result.length; | |
while (length--) { | |
result[length][2] = isStrictComparable(result[length][1]); | |
} | |
return result; | |
} | |
/** | |
* 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 = object == null ? undefined : object[key]; | |
return isNative(value) ? value : undefined; | |
} | |
/** | |
* 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 = new array.constructor(length); | |
// Add array 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) { | |
var Ctor = object.constructor; | |
if (!(typeof Ctor == 'function' && Ctor instanceof Ctor)) { | |
Ctor = Object; | |
} | |
return new Ctor; | |
} | |
/** | |
* 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 {boolean} [isDeep] Specify a deep clone. | |
* @returns {Object} Returns the initialized clone. | |
*/ | |
function initCloneByTag(object, tag, isDeep) { | |
var Ctor = object.constructor; | |
switch (tag) { | |
case arrayBufferTag: | |
return bufferClone(object); | |
case boolTag: | |
case dateTag: | |
return new Ctor(+object); | |
case float32Tag: case float64Tag: | |
case int8Tag: case int16Tag: case int32Tag: | |
case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: | |
var buffer = object.buffer; | |
return new Ctor(isDeep ? bufferClone(buffer) : buffer, object.byteOffset, object.length); | |
case numberTag: | |
case stringTag: | |
return new Ctor(object); | |
case regexpTag: | |
var result = new Ctor(object.source, reFlags.exec(object)); | |
result.lastIndex = object.lastIndex; | |
} | |
return result; | |
} | |
/** | |
* Invokes the method at `path` on `object`. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @param {Array|string} path The path of the method to invoke. | |
* @param {Array} args The arguments to invoke the method with. | |
* @returns {*} Returns the result of the invoked method. | |
*/ | |
function invokePath(object, path, args) { | |
if (object != null && !isKey(path, object)) { | |
path = toPath(path); | |
object = path.length == 1 ? object : baseGet(object, baseSlice(path, 0, -1)); | |
path = last(path); | |
} | |
var func = object == null ? object : object[path]; | |
return func == null ? undefined : func.apply(object, args); | |
} | |
/** | |
* Checks if `value` is array-like. | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is array-like, else `false`. | |
*/ | |
function isArrayLike(value) { | |
return value != null && isLength(getLength(value)); | |
} | |
/** | |
* 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) { | |
value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1; | |
length = length == null ? MAX_SAFE_INTEGER : length; | |
return value > -1 && value % 1 == 0 && value < length; | |
} | |
/** | |
* Checks if the provided arguments are from an iteratee call. | |
* | |
* @private | |
* @param {*} value The potential iteratee value argument. | |
* @param {*} index The potential iteratee index or key argument. | |
* @param {*} object The potential iteratee object argument. | |
* @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`. | |
*/ | |
function isIterateeCall(value, index, object) { | |
if (!isObject(object)) { | |
return false; | |
} | |
var type = typeof index; | |
if (type == 'number' | |
? (isArrayLike(object) && isIndex(index, object.length)) | |
: (type == 'string' && index in object)) { | |
var other = object[index]; | |
return value === value ? (value === other) : (other !== other); | |
} | |
return false; | |
} | |
/** | |
* 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) { | |
var type = typeof value; | |
if ((type == 'string' && reIsPlainProp.test(value)) || type == 'number') { | |
return true; | |
} | |
if (isArray(value)) { | |
return false; | |
} | |
var result = !reIsDeepProp.test(value); | |
return result || (object != null && value in toObject(object)); | |
} | |
/** | |
* Checks if `func` has a lazy counterpart. | |
* | |
* @private | |
* @param {Function} func The function to check. | |
* @returns {boolean} Returns `true` if `func` has a lazy counterpart, else `false`. | |
*/ | |
function isLaziable(func) { | |
var funcName = getFuncName(func); | |
if (!(funcName in LazyWrapper.prototype)) { | |
return false; | |
} | |
var other = lodash[funcName]; | |
if (func === other) { | |
return true; | |
} | |
var data = getData(other); | |
return !!data && func === data[0]; | |
} | |
/** | |
* Checks if `value` is a valid array-like length. | |
* | |
* **Note:** This function is based on [`ToLength`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength). | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a valid length, else `false`. | |
*/ | |
function isLength(value) { | |
return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; | |
} | |
/** | |
* Checks if `value` is suitable for strict equality comparisons, i.e. `===`. | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` if suitable for strict | |
* equality comparisons, else `false`. | |
*/ | |
function isStrictComparable(value) { | |
return value === value && !isObject(value); | |
} | |
/** | |
* Merges the function metadata of `source` into `data`. | |
* | |
* Merging metadata reduces the number of wrappers required to invoke a function. | |
* This is possible because methods like `_.bind`, `_.curry`, and `_.partial` | |
* may be applied regardless of execution order. Methods like `_.ary` and `_.rearg` | |
* augment function arguments, making the order in which they are executed important, | |
* preventing the merging of metadata. However, we make an exception for a safe | |
* common case where curried functions have `_.ary` and or `_.rearg` applied. | |
* | |
* @private | |
* @param {Array} data The destination metadata. | |
* @param {Array} source The source metadata. | |
* @returns {Array} Returns `data`. | |
*/ | |
function mergeData(data, source) { | |
var bitmask = data[1], | |
srcBitmask = source[1], | |
newBitmask = bitmask | srcBitmask, | |
isCommon = newBitmask < ARY_FLAG; | |
var isCombo = | |
(srcBitmask == ARY_FLAG && bitmask == CURRY_FLAG) || | |
(srcBitmask == ARY_FLAG && bitmask == REARG_FLAG && data[7].length <= source[8]) || | |
(srcBitmask == (ARY_FLAG | REARG_FLAG) && bitmask == CURRY_FLAG); | |
// Exit early if metadata can't be merged. | |
if (!(isCommon || isCombo)) { | |
return data; | |
} | |
// Use source `thisArg` if available. | |
if (srcBitmask & BIND_FLAG) { | |
data[2] = source[2]; | |
// Set when currying a bound function. | |
newBitmask |= (bitmask & BIND_FLAG) ? 0 : CURRY_BOUND_FLAG; | |
} | |
// Compose partial arguments. | |
var value = source[3]; | |
if (value) { | |
var partials = data[3]; | |
data[3] = partials ? composeArgs(partials, value, source[4]) : arrayCopy(value); | |
data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : arrayCopy(source[4]); | |
} | |
// Compose partial right arguments. | |
value = source[5]; | |
if (value) { | |
partials = data[5]; | |
data[5] = partials ? composeArgsRight(partials, value, source[6]) : arrayCopy(value); | |
data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : arrayCopy(source[6]); | |
} | |
// Use source `argPos` if available. | |
value = source[7]; | |
if (value) { | |
data[7] = arrayCopy(value); | |
} | |
// Use source `ary` if it's smaller. | |
if (srcBitmask & ARY_FLAG) { | |
data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]); | |
} | |
// Use source `arity` if one is not provided. | |
if (data[9] == null) { | |
data[9] = source[9]; | |
} | |
// Use source `func` and merge bitmasks. | |
data[0] = source[0]; | |
data[1] = newBitmask; | |
return data; | |
} | |
/** | |
* A specialized version of `_.pick` which picks `object` properties specified | |
* by `props`. | |
* | |
* @private | |
* @param {Object} object The source object. | |
* @param {string[]} props The property names to pick. | |
* @returns {Object} Returns the new object. | |
*/ | |
function pickByArray(object, props) { | |
object = toObject(object); | |
var index = -1, | |
length = props.length, | |
result = {}; | |
while (++index < length) { | |
var key = props[index]; | |
if (key in object) { | |
result[key] = object[key]; | |
} | |
} | |
return result; | |
} | |
/** | |
* A specialized version of `_.pick` which picks `object` properties `predicate` | |
* returns truthy for. | |
* | |
* @private | |
* @param {Object} object The source object. | |
* @param {Function} predicate The function invoked per iteration. | |
* @returns {Object} Returns the new object. | |
*/ | |
function pickByCallback(object, predicate) { | |
var result = {}; | |
baseForIn(object, function(value, key, object) { | |
if (predicate(value, key, object)) { | |
result[key] = value; | |
} | |
}); | |
return result; | |
} | |
/** | |
* Reorder `array` according to the specified indexes where the element at | |
* the first index is assigned as the first element, the element at | |
* the second index is assigned as the second element, and so on. | |
* | |
* @private | |
* @param {Array} array The array to reorder. | |
* @param {Array} indexes The arranged array indexes. | |
* @returns {Array} Returns `array`. | |
*/ | |
function reorder(array, indexes) { | |
var arrLength = array.length, | |
length = nativeMin(indexes.length, arrLength), | |
oldArray = arrayCopy(array); | |
while (length--) { | |
var index = indexes[length]; | |
array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined; | |
} | |
return array; | |
} | |
/** | |
* Sets metadata for `func`. | |
* | |
* **Note:** If this function becomes hot, i.e. is invoked a lot in a short | |
* period of time, it will trip its breaker and transition to an identity function | |
* to avoid garbage collection pauses in V8. See [V8 issue 2070](https://code.google.com/p/v8/issues/detail?id=2070) | |
* for more details. | |
* | |
* @private | |
* @param {Function} func The function to associate metadata with. | |
* @param {*} data The metadata. | |
* @returns {Function} Returns `func`. | |
*/ | |
var setData = (function() { | |
var count = 0, | |
lastCalled = 0; | |
return function(key, value) { | |
var stamp = now(), | |
remaining = HOT_SPAN - (stamp - lastCalled); | |
lastCalled = stamp; | |
if (remaining > 0) { | |
if (++count >= HOT_COUNT) { | |
return key; | |
} | |
} else { | |
count = 0; | |
} | |
return baseSetData(key, value); | |
}; | |
}()); | |
/** | |
* A fallback implementation of `_.isPlainObject` which checks if `value` | |
* is an object created by the `Object` constructor or has a `[[Prototype]]` | |
* of `null`. | |
* | |
* @private | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`. | |
*/ | |
function shimIsPlainObject(value) { | |
var Ctor, | |
support = lodash.support; | |
// Exit early for non `Object` objects. | |
if (!(isObjectLike(value) && objToString.call(value) == objectTag) || | |
(!hasOwnProperty.call(value, 'constructor') && | |
(Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) { | |
return false; | |
} | |
// IE < 9 iterates inherited properties before own properties. If the first | |
// iterated property is an object's own property then there are no inherited | |
// enumerable properties. | |
var result; | |
// In most environments an object's own properties are iterated before | |
// its inherited properties. If the last iterated property is an object's | |
// own property then there are no inherited enumerable properties. | |
baseForIn(value, function(subValue, key) { | |
result = key; | |
}); | |
return result === undefined || hasOwnProperty.call(value, result); | |
} | |
/** | |
* A fallback implementation of `Object.keys` which creates an array of the | |
* own enumerable property names of `object`. | |
* | |
* @private | |
* @param {Object} object The object to query. | |
* @returns {Array} Returns the array of property names. | |
*/ | |
function shimKeys(object) { | |
var props = keysIn(object), | |
propsLength = props.length, | |
length = propsLength && object.length; | |
var allowIndexes = !!length && isLength(length) && | |
(isArray(object) || isArguments(object)); | |
var index = -1, | |
result = []; | |
while (++index < propsLength) { | |
var key = props[index]; | |
if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) { | |
result.push(key); | |
} | |
} | |
return result; | |
} | |
/** | |
* Converts `value` to an object if it's not one. | |
* | |
* @private | |
* @param {*} value The value to process. | |
* @returns {Object} Returns the object. | |
*/ | |
function toObject(value) { | |
return isObject(value) ? value : Object(value); | |
} | |
/** | |
* Converts `value` to property path array if it's not one. | |
* | |
* @private | |
* @param {*} value The value to process. | |
* @returns {Array} Returns the property path array. | |
*/ | |
function toPath(value) { | |
if (isArray(value)) { | |
return value; | |
} | |
var result = []; | |
baseToString(value).replace(rePropName, function(match, number, quote, string) { | |
result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match)); | |
}); | |
return result; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates an array of unique `array` values not included in the other | |
* provided arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) | |
* for equality comparisons. | |
* | |
* @static | |
* @memberOf _ | |
* @category Array | |
* @param {Array} array The array to inspect. | |
* @param {...Array} [values] The arrays of values to exclude. | |
* @returns {Array} Returns the new array of filtered values. | |
* @example | |
* | |
* _.difference([1, 2, 3], [4, 2]); | |
* // => [1, 3] | |
*/ | |
var difference = restParam(function(array, values) { | |
return isArrayLike(array) | |
? baseDifference(array, baseFlatten(values, false, true)) | |
: []; | |
}); | |
/** | |
* Gets the index at which the first occurrence of `value` is found in `array` | |
* using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) | |
* for equality comparisons. If `fromIndex` is negative, it is used as the offset | |
* from the end of `array`. If `array` is sorted providing `true` for `fromIndex` | |
* performs a faster binary search. | |
* | |
* @static | |
* @memberOf _ | |
* @category Array | |
* @param {Array} array The array to search. | |
* @param {*} value The value to search for. | |
* @param {boolean|number} [fromIndex=0] The index to search from or `true` | |
* to perform a binary search on a sorted array. | |
* @returns {number} Returns the index of the matched value, else `-1`. | |
* @example | |
* | |
* _.indexOf([1, 2, 1, 2], 2); | |
* // => 1 | |
* | |
* // using `fromIndex` | |
* _.indexOf([1, 2, 1, 2], 2, 2); | |
* // => 3 | |
* | |
* // performing a binary search | |
* _.indexOf([1, 1, 2, 2], 2, true); | |
* // => 2 | |
*/ | |
function indexOf(array, value, fromIndex) { | |
var length = array ? array.length : 0; | |
if (!length) { | |
return -1; | |
} | |
if (typeof fromIndex == 'number') { | |
fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; | |
} else if (fromIndex) { | |
var index = binaryIndex(array, value), | |
other = array[index]; | |
if (value === value ? (value === other) : (other !== other)) { | |
return index; | |
} | |
return -1; | |
} | |
return baseIndexOf(array, value, fromIndex || 0); | |
} | |
/** | |
* Creates an array of unique values that are included in all of the provided | |
* arrays using [`SameValueZero`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-samevaluezero) | |
* for equality comparisons. | |
* | |
* @static | |
* @memberOf _ | |
* @category Array | |
* @param {...Array} [arrays] The arrays to inspect. | |
* @returns {Array} Returns the new array of shared values. | |
* @example | |
* _.intersection([1, 2], [4, 2], [2, 1]); | |
* // => [2] | |
*/ | |
var intersection = restParam(function(arrays) { | |
var othLength = arrays.length, | |
othIndex = othLength, | |
caches = Array(length), | |
indexOf = getIndexOf(), | |
isCommon = indexOf == baseIndexOf, | |
result = []; | |
while (othIndex--) { | |
var value = arrays[othIndex] = isArrayLike(value = arrays[othIndex]) ? value : []; | |
caches[othIndex] = (isCommon && value.length >= 120) ? createCache(othIndex && value) : null; | |
} | |
var array = arrays[0], | |
index = -1, | |
length = array ? array.length : 0, | |
seen = caches[0]; | |
outer: | |
while (++index < length) { | |
value = array[index]; | |
if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value, 0)) < 0) { | |
var othIndex = othLength; | |
while (--othIndex) { | |
var cache = caches[othIndex]; | |
if ((cache ? cacheIndexOf(cache, value) : indexOf(arrays[othIndex], value, 0)) < 0) { | |
continue outer; | |
} | |
} | |
if (seen) { | |
seen.push(value); | |
} | |
result.push(value); | |
} | |
} | |
return result; | |
}); | |
/** | |
* Gets the last element of `array`. | |
* | |
* @static | |
* @memberOf _ | |
* @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; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Checks if `predicate` returns truthy for **all** elements of `collection`. | |
* The predicate is bound to `thisArg` and invoked with three arguments: | |
* (value, index|key, collection). | |
* | |
* If a property name is provided for `predicate` the created `_.property` | |
* style callback returns the property value of the given element. | |
* | |
* If a value is also provided for `thisArg` the created `_.matchesProperty` | |
* style callback returns `true` for elements that have a matching property | |
* value, else `false`. | |
* | |
* If an object is provided for `predicate` the created `_.matches` style | |
* callback returns `true` for elements that have the properties of the given | |
* object, else `false`. | |
* | |
* @static | |
* @memberOf _ | |
* @alias all | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function|Object|string} [predicate=_.identity] The function invoked | |
* per iteration. | |
* @param {*} [thisArg] The `this` binding of `predicate`. | |
* @returns {boolean} Returns `true` if all elements pass the predicate check, | |
* else `false`. | |
* @example | |
* | |
* _.every([true, 1, null, 'yes'], Boolean); | |
* // => false | |
* | |
* var users = [ | |
* { 'user': 'barney', 'active': false }, | |
* { 'user': 'fred', 'active': false } | |
* ]; | |
* | |
* // using the `_.matches` callback shorthand | |
* _.every(users, { 'user': 'barney', 'active': false }); | |
* // => false | |
* | |
* // using the `_.matchesProperty` callback shorthand | |
* _.every(users, 'active', false); | |
* // => true | |
* | |
* // using the `_.property` callback shorthand | |
* _.every(users, 'active'); | |
* // => false | |
*/ | |
function every(collection, predicate, thisArg) { | |
var func = isArray(collection) ? arrayEvery : baseEvery; | |
if (thisArg && isIterateeCall(collection, predicate, thisArg)) { | |
predicate = null; | |
} | |
if (typeof predicate != 'function' || thisArg !== undefined) { | |
predicate = getCallback(predicate, thisArg, 3); | |
} | |
return func(collection, predicate); | |
} | |
/** | |
* Iterates over elements of `collection`, returning the first element | |
* `predicate` returns truthy for. The predicate is bound to `thisArg` and | |
* invoked with three arguments: (value, index|key, collection). | |
* | |
* If a property name is provided for `predicate` the created `_.property` | |
* style callback returns the property value of the given element. | |
* | |
* If a value is also provided for `thisArg` the created `_.matchesProperty` | |
* style callback returns `true` for elements that have a matching property | |
* value, else `false`. | |
* | |
* If an object is provided for `predicate` the created `_.matches` style | |
* callback returns `true` for elements that have the properties of the given | |
* object, else `false`. | |
* | |
* @static | |
* @memberOf _ | |
* @alias detect | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to search. | |
* @param {Function|Object|string} [predicate=_.identity] The function invoked | |
* per iteration. | |
* @param {*} [thisArg] The `this` binding of `predicate`. | |
* @returns {*} Returns the matched element, else `undefined`. | |
* @example | |
* | |
* var users = [ | |
* { 'user': 'barney', 'age': 36, 'active': true }, | |
* { 'user': 'fred', 'age': 40, 'active': false }, | |
* { 'user': 'pebbles', 'age': 1, 'active': true } | |
* ]; | |
* | |
* _.result(_.find(users, function(chr) { | |
* return chr.age < 40; | |
* }), 'user'); | |
* // => 'barney' | |
* | |
* // using the `_.matches` callback shorthand | |
* _.result(_.find(users, { 'age': 1, 'active': true }), 'user'); | |
* // => 'pebbles' | |
* | |
* // using the `_.matchesProperty` callback shorthand | |
* _.result(_.find(users, 'active', false), 'user'); | |
* // => 'fred' | |
* | |
* // using the `_.property` callback shorthand | |
* _.result(_.find(users, 'active'), 'user'); | |
* // => 'barney' | |
*/ | |
var find = createFind(baseEach); | |
/** | |
* Iterates over elements of `collection` invoking `iteratee` for each element. | |
* The `iteratee` is bound to `thisArg` and invoked with three arguments: | |
* (value, index|key, collection). Iteratee functions may exit iteration early | |
* by explicitly returning `false`. | |
* | |
* **Note:** As with other "Collections" methods, objects with a "length" property | |
* are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn` | |
* may be used for object iteration. | |
* | |
* @static | |
* @memberOf _ | |
* @alias each | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} [iteratee=_.identity] The function invoked per iteration. | |
* @param {*} [thisArg] The `this` binding of `iteratee`. | |
* @returns {Array|Object|string} Returns `collection`. | |
* @example | |
* | |
* _([1, 2]).forEach(function(n) { | |
* console.log(n); | |
* }).value(); | |
* // => logs each value from left to right and returns the array | |
* | |
* _.forEach({ 'a': 1, 'b': 2 }, function(n, key) { | |
* console.log(n, key); | |
* }); | |
* // => logs each value-key pair and returns the object (iteration order is not guaranteed) | |
*/ | |
var forEach = createForEach(arrayEach, baseEach); | |
/** | |
* Invokes the method at `path` of each element in `collection`, returning | |
* an array of the results of each invoked method. Any additional arguments | |
* are provided to each invoked method. If `methodName` is a function it is | |
* invoked for, and `this` bound to, each element in `collection`. | |
* | |
* @static | |
* @memberOf _ | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Array|Function|string} path The path of the method to invoke or | |
* the function invoked per iteration. | |
* @param {...*} [args] The arguments to invoke the method with. | |
* @returns {Array} Returns the array of results. | |
* @example | |
* | |
* _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); | |
* // => [[1, 5, 7], [1, 2, 3]] | |
* | |
* _.invoke([123, 456], String.prototype.split, ''); | |
* // => [['1', '2', '3'], ['4', '5', '6']] | |
*/ | |
var invoke = restParam(function(collection, path, args) { | |
var index = -1, | |
isFunc = typeof path == 'function', | |
isProp = isKey(path), | |
result = isArrayLike(collection) ? Array(collection.length) : []; | |
baseEach(collection, function(value) { | |
var func = isFunc ? path : ((isProp && value != null) ? value[path] : null); | |
result[++index] = func ? func.apply(value, args) : invokePath(value, path, args); | |
}); | |
return result; | |
}); | |
/** | |
* Creates an array of values by running each element in `collection` through | |
* `iteratee`. The `iteratee` is bound to `thisArg` and invoked with three | |
* arguments: (value, index|key, collection). | |
* | |
* If a property name is provided for `iteratee` the created `_.property` | |
* style callback returns the property value of the given element. | |
* | |
* If a value is also provided for `thisArg` the created `_.matchesProperty` | |
* style callback returns `true` for elements that have a matching property | |
* value, else `false`. | |
* | |
* If an object is provided for `iteratee` the created `_.matches` style | |
* callback returns `true` for elements that have the properties of the given | |
* object, else `false`. | |
* | |
* Many lodash methods are guarded to work as iteratees for methods like | |
* `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`. | |
* | |
* The guarded methods are: | |
* `ary`, `callback`, `chunk`, `clone`, `create`, `curry`, `curryRight`, | |
* `drop`, `dropRight`, `every`, `fill`, `flatten`, `invert`, `max`, `min`, | |
* `parseInt`, `slice`, `sortBy`, `take`, `takeRight`, `template`, `trim`, | |
* `trimLeft`, `trimRight`, `trunc`, `random`, `range`, `sample`, `some`, | |
* `sum`, `uniq`, and `words` | |
* | |
* @static | |
* @memberOf _ | |
* @alias collect | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function|Object|string} [iteratee=_.identity] The function invoked | |
* per iteration. | |
* @param {*} [thisArg] The `this` binding of `iteratee`. | |
* @returns {Array} Returns the new mapped array. | |
* @example | |
* | |
* function timesThree(n) { | |
* return n * 3; | |
* } | |
* | |
* _.map([1, 2], timesThree); | |
* // => [3, 6] | |
* | |
* _.map({ 'a': 1, 'b': 2 }, timesThree); | |
* // => [3, 6] (iteration order is not guaranteed) | |
* | |
* var users = [ | |
* { 'user': 'barney' }, | |
* { 'user': 'fred' } | |
* ]; | |
* | |
* // using the `_.property` callback shorthand | |
* _.map(users, 'user'); | |
* // => ['barney', 'fred'] | |
*/ | |
function map(collection, iteratee, thisArg) { | |
var func = isArray(collection) ? arrayMap : baseMap; | |
iteratee = getCallback(iteratee, thisArg, 3); | |
return func(collection, iteratee); | |
} | |
/** | |
* Reduces `collection` to a value which is the accumulated result of running | |
* each element in `collection` through `iteratee`, where each successive | |
* invocation is supplied the return value of the previous. If `accumulator` | |
* is not provided the first element of `collection` is used as the initial | |
* value. The `iteratee` is bound to `thisArg` and invoked with four arguments: | |
* (accumulator, value, index|key, collection). | |
* | |
* Many lodash methods are guarded to work as iteratees for methods like | |
* `_.reduce`, `_.reduceRight`, and `_.transform`. | |
* | |
* The guarded methods are: | |
* `assign`, `defaults`, `includes`, `merge`, `sortByAll`, and `sortByOrder` | |
* | |
* @static | |
* @memberOf _ | |
* @alias foldl, inject | |
* @category Collection | |
* @param {Array|Object|string} collection The collection to iterate over. | |
* @param {Function} [iteratee=_.identity] The function invoked per iteration. | |
* @param {*} [accumulator] The initial value. | |
* @param {*} [thisArg] The `this` binding of `iteratee`. | |
* @returns {*} Returns the accumulated value. | |
* @example | |
* | |
* _.reduce([1, 2], function(total, n) { | |
* return total + n; | |
* }); | |
* // => 3 | |
* | |
* _.reduce({ 'a': 1, 'b': 2 }, function(result, n, key) { | |
* result[key] = n * 3; | |
* return result; | |
* }, {}); | |
* // => { 'a': 3, 'b': 6 } (iteration order is not guaranteed) | |
*/ | |
var reduce = createReduce(arrayReduce, baseEach); | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Gets the number of milliseconds that have elapsed since the Unix epoch | |
* (1 January 1970 00:00:00 UTC). | |
* | |
* @static | |
* @memberOf _ | |
* @category Date | |
* @example | |
* | |
* _.defer(function(stamp) { | |
* console.log(_.now() - stamp); | |
* }, _.now()); | |
* // => logs the number of milliseconds it took for the deferred function to be invoked | |
*/ | |
var now = nativeNow || function() { | |
return new Date().getTime(); | |
}; | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates a function that invokes `func` with the `this` binding of `thisArg` | |
* and prepends any additional `_.bind` arguments to those provided to the | |
* bound function. | |
* | |
* The `_.bind.placeholder` value, which defaults to `_` in monolithic builds, | |
* may be used as a placeholder for partially applied arguments. | |
* | |
* **Note:** Unlike native `Function#bind` this method does not set the "length" | |
* property of bound functions. | |
* | |
* @static | |
* @memberOf _ | |
* @category Function | |
* @param {Function} func The function to bind. | |
* @param {*} thisArg The `this` binding of `func`. | |
* @param {...*} [partials] The arguments to be partially applied. | |
* @returns {Function} Returns the new bound function. | |
* @example | |
* | |
* var greet = function(greeting, punctuation) { | |
* return greeting + ' ' + this.user + punctuation; | |
* }; | |
* | |
* var object = { 'user': 'fred' }; | |
* | |
* var bound = _.bind(greet, object, 'hi'); | |
* bound('!'); | |
* // => 'hi fred!' | |
* | |
* // using placeholders | |
* var bound = _.bind(greet, object, _, '!'); | |
* bound('hi'); | |
* // => 'hi fred!' | |
*/ | |
var bind = restParam(function(func, thisArg, partials) { | |
var bitmask = BIND_FLAG; | |
if (partials.length) { | |
var holders = replaceHolders(partials, bind.placeholder); | |
bitmask |= PARTIAL_FLAG; | |
} | |
return createWrapper(func, bitmask, thisArg, partials, holders); | |
}); | |
/** | |
* Defers invoking the `func` until the current call stack has cleared. Any | |
* additional arguments are provided to `func` when it is invoked. | |
* | |
* @static | |
* @memberOf _ | |
* @category Function | |
* @param {Function} func The function to defer. | |
* @param {...*} [args] The arguments to invoke the function with. | |
* @returns {number} Returns the timer id. | |
* @example | |
* | |
* _.defer(function(text) { | |
* console.log(text); | |
* }, 'deferred'); | |
* // logs 'deferred' after one or more milliseconds | |
*/ | |
var defer = restParam(function(func, args) { | |
return baseDelay(func, 1, args); | |
}); | |
/** | |
* Creates a function that invokes `func` with `partial` arguments prepended | |
* to those provided to the new function. This method is like `_.bind` except | |
* it does **not** alter the `this` binding. | |
* | |
* The `_.partial.placeholder` value, which defaults to `_` in monolithic | |
* builds, may be used as a placeholder for partially applied arguments. | |
* | |
* **Note:** This method does not set the "length" property of partially | |
* applied functions. | |
* | |
* @static | |
* @memberOf _ | |
* @category Function | |
* @param {Function} func The function to partially apply arguments to. | |
* @param {...*} [partials] The arguments to be partially applied. | |
* @returns {Function} Returns the new partially applied function. | |
* @example | |
* | |
* var greet = function(greeting, name) { | |
* return greeting + ' ' + name; | |
* }; | |
* | |
* var sayHelloTo = _.partial(greet, 'hello'); | |
* sayHelloTo('fred'); | |
* // => 'hello fred' | |
* | |
* // using placeholders | |
* var greetFred = _.partial(greet, _, 'fred'); | |
* greetFred('hi'); | |
* // => 'hi fred' | |
*/ | |
var partial = createPartial(PARTIAL_FLAG); | |
/** | |
* Creates a function that invokes `func` with the `this` binding of the | |
* created function and arguments from `start` and beyond provided as an array. | |
* | |
* **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters). | |
* | |
* @static | |
* @memberOf _ | |
* @category Function | |
* @param {Function} func The function to apply a rest parameter to. | |
* @param {number} [start=func.length-1] The start position of the rest parameter. | |
* @returns {Function} Returns the new function. | |
* @example | |
* | |
* var say = _.restParam(function(what, names) { | |
* return what + ' ' + _.initial(names).join(', ') + | |
* (_.size(names) > 1 ? ', & ' : '') + _.last(names); | |
* }); | |
* | |
* say('hello', 'fred', 'barney', 'pebbles'); | |
* // => 'hello fred, barney, & pebbles' | |
*/ | |
function restParam(func, start) { | |
if (typeof func != 'function') { | |
throw new TypeError(FUNC_ERROR_TEXT); | |
} | |
start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); | |
return function() { | |
var args = arguments, | |
index = -1, | |
length = nativeMax(args.length - start, 0), | |
rest = Array(length); | |
while (++index < length) { | |
rest[index] = args[start + index]; | |
} | |
switch (start) { | |
case 0: return func.call(this, rest); | |
case 1: return func.call(this, args[0], rest); | |
case 2: return func.call(this, args[0], args[1], rest); | |
} | |
var otherArgs = Array(start + 1); | |
index = -1; | |
while (++index < start) { | |
otherArgs[index] = args[index]; | |
} | |
otherArgs[start] = rest; | |
return func.apply(this, otherArgs); | |
}; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates a clone of `value`. If `isDeep` is `true` nested objects are cloned, | |
* otherwise they are assigned by reference. If `customizer` is provided it is | |
* invoked to produce the cloned values. If `customizer` returns `undefined` | |
* cloning is handled by the method instead. The `customizer` is bound to | |
* `thisArg` and invoked with two argument; (value [, index|key, object]). | |
* | |
* **Note:** This method is loosely based on the | |
* [structured clone algorithm](http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm). | |
* The enumerable properties of `arguments` objects and objects created by | |
* constructors other than `Object` are cloned to plain `Object` objects. An | |
* empty object is returned for uncloneable values such as functions, DOM nodes, | |
* Maps, Sets, and WeakMaps. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to clone. | |
* @param {boolean} [isDeep] Specify a deep clone. | |
* @param {Function} [customizer] The function to customize cloning values. | |
* @param {*} [thisArg] The `this` binding of `customizer`. | |
* @returns {*} Returns the cloned value. | |
* @example | |
* | |
* var users = [ | |
* { 'user': 'barney' }, | |
* { 'user': 'fred' } | |
* ]; | |
* | |
* var shallow = _.clone(users); | |
* shallow[0] === users[0]; | |
* // => true | |
* | |
* var deep = _.clone(users, true); | |
* deep[0] === users[0]; | |
* // => false | |
* | |
* // using a customizer callback | |
* var el = _.clone(document.body, function(value) { | |
* if (_.isElement(value)) { | |
* return value.cloneNode(false); | |
* } | |
* }); | |
* | |
* el === document.body | |
* // => false | |
* el.nodeName | |
* // => BODY | |
* el.childNodes.length; | |
* // => 0 | |
*/ | |
function clone(value, isDeep, customizer, thisArg) { | |
if (isDeep && typeof isDeep != 'boolean' && isIterateeCall(value, isDeep, customizer)) { | |
isDeep = false; | |
} | |
else if (typeof isDeep == 'function') { | |
thisArg = customizer; | |
customizer = isDeep; | |
isDeep = false; | |
} | |
return typeof customizer == 'function' | |
? baseClone(value, isDeep, bindCallback(customizer, thisArg, 1)) | |
: baseClone(value, isDeep); | |
} | |
/** | |
* Checks if `value` is classified as an `arguments` object. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. | |
* @example | |
* | |
* _.isArguments(function() { return arguments; }()); | |
* // => true | |
* | |
* _.isArguments([1, 2, 3]); | |
* // => false | |
*/ | |
function isArguments(value) { | |
return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag; | |
} | |
/** | |
* Checks if `value` is classified as an `Array` object. | |
* | |
* @static | |
* @memberOf _ | |
* @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(function() { return arguments; }()); | |
* // => false | |
*/ | |
var isArray = nativeIsArray || function(value) { | |
return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag; | |
}; | |
/** | |
* Checks if `value` is a DOM element. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. | |
* @example | |
* | |
* _.isElement(document.body); | |
* // => true | |
* | |
* _.isElement('<body>'); | |
* // => false | |
*/ | |
function isElement(value) { | |
return !!value && value.nodeType === 1 && isObjectLike(value) && | |
(objToString.call(value).indexOf('Element') > -1); | |
} | |
// Fallback for environments without DOM support. | |
if (!support.dom) { | |
isElement = function(value) { | |
return !!value && value.nodeType === 1 && isObjectLike(value) && !isPlainObject(value); | |
}; | |
} | |
/** | |
* Performs a deep comparison between two values to determine if they are | |
* equivalent. If `customizer` is provided it is invoked to compare values. | |
* If `customizer` returns `undefined` comparisons are handled by the method | |
* instead. The `customizer` is bound to `thisArg` and invoked with three | |
* arguments: (value, other [, index|key]). | |
* | |
* **Note:** This method supports comparing arrays, booleans, `Date` objects, | |
* numbers, `Object` objects, regexes, and strings. Objects are compared by | |
* their own, not inherited, enumerable properties. Functions and DOM nodes | |
* are **not** supported. Provide a customizer function to extend support | |
* for comparing other values. | |
* | |
* @static | |
* @memberOf _ | |
* @alias eq | |
* @category Lang | |
* @param {*} value The value to compare. | |
* @param {*} other The other value to compare. | |
* @param {Function} [customizer] The function to customize value comparisons. | |
* @param {*} [thisArg] The `this` binding of `customizer`. | |
* @returns {boolean} Returns `true` if the values are equivalent, else `false`. | |
* @example | |
* | |
* var object = { 'user': 'fred' }; | |
* var other = { 'user': 'fred' }; | |
* | |
* object == other; | |
* // => false | |
* | |
* _.isEqual(object, other); | |
* // => true | |
* | |
* // using a customizer callback | |
* var array = ['hello', 'goodbye']; | |
* var other = ['hi', 'goodbye']; | |
* | |
* _.isEqual(array, other, function(value, other) { | |
* if (_.every([value, other], RegExp.prototype.test, /^h(?:i|ello)$/)) { | |
* return true; | |
* } | |
* }); | |
* // => true | |
*/ | |
function isEqual(value, other, customizer, thisArg) { | |
customizer = typeof customizer == 'function' ? bindCallback(customizer, thisArg, 3) : undefined; | |
var result = customizer ? customizer(value, other) : undefined; | |
return result === undefined ? baseIsEqual(value, other, customizer) : !!result; | |
} | |
/** | |
* Checks if `value` is classified as a `Function` object. | |
* | |
* @static | |
* @memberOf _ | |
* @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 | |
*/ | |
var isFunction = !(baseIsFunction(/x/) || (Uint8Array && !baseIsFunction(Uint8Array))) ? baseIsFunction : function(value) { | |
// The use of `Object#toString` avoids issues with the `typeof` operator | |
// in older versions of Chrome and Safari which return 'function' for regexes | |
// and Safari 8 equivalents which return 'object' for typed array constructors. | |
return objToString.call(value) == funcTag; | |
}; | |
/** | |
* Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`. | |
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) | |
* | |
* @static | |
* @memberOf _ | |
* @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(1); | |
* // => false | |
*/ | |
function isObject(value) { | |
// Avoid a V8 JIT bug in Chrome 19-20. | |
// See https://code.google.com/p/v8/issues/detail?id=2291 for more details. | |
var type = typeof value; | |
return !!value && (type == 'object' || type == 'function'); | |
} | |
/** | |
* Checks if `value` is a native function. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is a native function, else `false`. | |
* @example | |
* | |
* _.isNative(Array.prototype.push); | |
* // => true | |
* | |
* _.isNative(_); | |
* // => false | |
*/ | |
function isNative(value) { | |
if (value == null) { | |
return false; | |
} | |
if (objToString.call(value) == funcTag) { | |
return reIsNative.test(fnToString.call(value)); | |
} | |
return isObjectLike(value) && reIsHostCtor.test(value); | |
} | |
/** | |
* Checks if `value` is classified as a `Number` primitive or object. | |
* | |
* **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are classified | |
* as numbers, use the `_.isFinite` method. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. | |
* @example | |
* | |
* _.isNumber(8.4); | |
* // => true | |
* | |
* _.isNumber(NaN); | |
* // => true | |
* | |
* _.isNumber('8.4'); | |
* // => false | |
*/ | |
function isNumber(value) { | |
return typeof value == 'number' || (isObjectLike(value) && objToString.call(value) == numberTag); | |
} | |
/** | |
* Checks if `value` is a plain object, that is, an object created by the | |
* `Object` constructor or one with a `[[Prototype]]` of `null`. | |
* | |
* **Note:** This method assumes objects created by the `Object` constructor | |
* have no inherited enumerable properties. | |
* | |
* @static | |
* @memberOf _ | |
* @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 | |
*/ | |
var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) { | |
if (!(value && objToString.call(value) == objectTag)) { | |
return false; | |
} | |
var valueOf = getNative(value, 'valueOf'), | |
objProto = valueOf && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto); | |
return objProto | |
? (value == objProto || getPrototypeOf(value) == objProto) | |
: shimIsPlainObject(value); | |
}; | |
/** | |
* Checks if `value` is classified as a `String` primitive or object. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. | |
* @example | |
* | |
* _.isString('abc'); | |
* // => true | |
* | |
* _.isString(1); | |
* // => false | |
*/ | |
function isString(value) { | |
return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag); | |
} | |
/** | |
* Checks if `value` is classified as a typed array. | |
* | |
* @static | |
* @memberOf _ | |
* @category Lang | |
* @param {*} value The value to check. | |
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`. | |
* @example | |
* | |
* _.isTypedArray(new Uint8Array); | |
* // => true | |
* | |
* _.isTypedArray([]); | |
* // => false | |
*/ | |
function isTypedArray(value) { | |
return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)]; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Assigns own enumerable properties of source object(s) to the destination | |
* object. Subsequent sources overwrite property assignments of previous sources. | |
* If `customizer` is provided it is invoked to produce the assigned values. | |
* The `customizer` is bound to `thisArg` and invoked with five arguments: | |
* (objectValue, sourceValue, key, object, source). | |
* | |
* **Note:** This method mutates `object` and is based on | |
* [`Object.assign`](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign). | |
* | |
* @static | |
* @memberOf _ | |
* @alias extend | |
* @category Object | |
* @param {Object} object The destination object. | |
* @param {...Object} [sources] The source objects. | |
* @param {Function} [customizer] The function to customize assigned values. | |
* @param {*} [thisArg] The `this` binding of `customizer`. | |
* @returns {Object} Returns `object`. | |
* @example | |
* | |
* _.assign({ 'user': 'barney' }, { 'age': 40 }, { 'user': 'fred' }); | |
* // => { 'user': 'fred', 'age': 40 } | |
* | |
* // using a customizer callback | |
* var defaults = _.partialRight(_.assign, function(value, other) { | |
* return _.isUndefined(value) ? other : value; | |
* }); | |
* | |
* defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); | |
* // => { 'user': 'barney', 'age': 36 } | |
*/ | |
var assign = createAssigner(function(object, source, customizer) { | |
return customizer | |
? assignWith(object, source, customizer) | |
: baseAssign(object, source); | |
}); | |
/** | |
* Assigns own enumerable properties of source object(s) to the destination | |
* object for all destination properties that resolve to `undefined`. Once a | |
* property is set, additional values of the same property are ignored. | |
* | |
* **Note:** This method mutates `object`. | |
* | |
* @static | |
* @memberOf _ | |
* @category Object | |
* @param {Object} object The destination object. | |
* @param {...Object} [sources] The source objects. | |
* @returns {Object} Returns `object`. | |
* @example | |
* | |
* _.defaults({ 'user': 'barney' }, { 'age': 36 }, { 'user': 'fred' }); | |
* // => { 'user': 'barney', 'age': 36 } | |
*/ | |
var defaults = restParam(function(args) { | |
var object = args[0]; | |
if (object == null) { | |
return object; | |
} | |
args.push(assignDefaults); | |
return assign.apply(undefined, args); | |
}); | |
/** | |
* Creates an array of the own enumerable property names of `object`. | |
* | |
* **Note:** Non-object values are coerced to objects. See the | |
* [ES spec](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.keys) | |
* for more details. | |
* | |
* @static | |
* @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'] | |
*/ | |
var keys = !nativeKeys ? shimKeys : function(object) { | |
var Ctor = object == null ? null : object.constructor; | |
if ((typeof Ctor == 'function' && Ctor.prototype === object) || | |
(typeof object != 'function' && isArrayLike(object))) { | |
return shimKeys(object); | |
} | |
return isObject(object) ? nativeKeys(object) : []; | |
}; | |
/** | |
* Creates an array of the own and inherited enumerable property names of `object`. | |
* | |
* **Note:** Non-object values are coerced to objects. | |
* | |
* @static | |
* @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; | |
* | |
* _.keysIn(new Foo); | |
* // => ['a', 'b', 'c'] (iteration order is not guaranteed) | |
*/ | |
function keysIn(object) { | |
if (object == null) { | |
return []; | |
} | |
if (!isObject(object)) { | |
object = Object(object); | |
} | |
var length = object.length; | |
length = (length && isLength(length) && | |
(isArray(object) || isArguments(object)) && length) || 0; | |
var Ctor = object.constructor, | |
index = -1, | |
isProto = typeof Ctor == 'function' && Ctor.prototype === object, | |
result = Array(length), | |
skipIndexes = length > 0; | |
while (++index < length) { | |
result[index] = (index + ''); | |
} | |
for (var key in object) { | |
if (!(skipIndexes && isIndex(key, length)) && | |
!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) { | |
result.push(key); | |
} | |
} | |
return result; | |
} | |
/** | |
* The opposite of `_.pick`; this method creates an object composed of the | |
* own and inherited enumerable properties of `object` that are not omitted. | |
* | |
* @static | |
* @memberOf _ | |
* @category Object | |
* @param {Object} object The source object. | |
* @param {Function|...(string|string[])} [predicate] The function invoked per | |
* iteration or property names to omit, specified as individual property | |
* names or arrays of property names. | |
* @param {*} [thisArg] The `this` binding of `predicate`. | |
* @returns {Object} Returns the new object. | |
* @example | |
* | |
* var object = { 'user': 'fred', 'age': 40 }; | |
* | |
* _.omit(object, 'age'); | |
* // => { 'user': 'fred' } | |
* | |
* _.omit(object, _.isNumber); | |
* // => { 'user': 'fred' } | |
*/ | |
var omit = restParam(function(object, props) { | |
if (object == null) { | |
return {}; | |
} | |
if (typeof props[0] != 'function') { | |
var props = arrayMap(baseFlatten(props), String); | |
return pickByArray(object, baseDifference(keysIn(object), props)); | |
} | |
var predicate = bindCallback(props[0], props[1], 3); | |
return pickByCallback(object, function(value, key, object) { | |
return !predicate(value, key, object); | |
}); | |
}); | |
/** | |
* Creates a two dimensional array of the key-value pairs for `object`, | |
* e.g. `[[key1, value1], [key2, value2]]`. | |
* | |
* @static | |
* @memberOf _ | |
* @category Object | |
* @param {Object} object The object to query. | |
* @returns {Array} Returns the new array of key-value pairs. | |
* @example | |
* | |
* _.pairs({ 'barney': 36, 'fred': 40 }); | |
* // => [['barney', 36], ['fred', 40]] (iteration order is not guaranteed) | |
*/ | |
function pairs(object) { | |
object = toObject(object); | |
var index = -1, | |
props = keys(object), | |
length = props.length, | |
result = Array(length); | |
while (++index < length) { | |
var key = props[index]; | |
result[index] = [key, object[key]]; | |
} | |
return result; | |
} | |
/** | |
* Creates an array of the own enumerable property values of `object`. | |
* | |
* **Note:** Non-object values are coerced to objects. | |
* | |
* @static | |
* @memberOf _ | |
* @category Object | |
* @param {Object} object The object to query. | |
* @returns {Array} Returns the array of property values. | |
* @example | |
* | |
* function Foo() { | |
* this.a = 1; | |
* this.b = 2; | |
* } | |
* | |
* Foo.prototype.c = 3; | |
* | |
* _.values(new Foo); | |
* // => [1, 2] (iteration order is not guaranteed) | |
* | |
* _.values('hi'); | |
* // => ['h', 'i'] | |
*/ | |
function values(object) { | |
return baseValues(object, keys(object)); | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Escapes the `RegExp` special characters "\", "/", "^", "$", ".", "|", "?", | |
* "*", "+", "(", ")", "[", "]", "{" and "}" in `string`. | |
* | |
* @static | |
* @memberOf _ | |
* @category String | |
* @param {string} [string=''] The string to escape. | |
* @returns {string} Returns the escaped string. | |
* @example | |
* | |
* _.escapeRegExp('[lodash](https://lodash.com/)'); | |
* // => '\[lodash\]\(https:\/\/lodash\.com\/\)' | |
*/ | |
function escapeRegExp(string) { | |
string = baseToString(string); | |
return (string && reHasRegExpChars.test(string)) | |
? string.replace(reRegExpChars, '\\$&') | |
: string; | |
} | |
/*------------------------------------------------------------------------*/ | |
/** | |
* Creates a function that invokes `func` with the `this` binding of `thisArg` | |
* and arguments of the created function. If `func` is a property name the | |
* created callback returns the property value for a given element. If `func` | |
* is an object the created callback returns `true` for elements that contain | |
* the equivalent object properties, otherwise it returns `false`. | |
* | |
* @static | |
* @memberOf _ | |
* @alias iteratee | |
* @category Utility | |
* @param {*} [func=_.identity] The value to convert to a callback. | |
* @param {*} [thisArg] The `this` binding of `func`. | |
* @param- {Object} [guard] Enables use as a callback for functions like `_.map`. | |
* @returns {Function} Returns the callback. | |
* @example | |
* | |
* var users = [ | |
* { 'user': 'barney', 'age': 36 }, | |
* { 'user': 'fred', 'age': 40 } | |
* ]; | |
* | |
* // wrap to create custom callback shorthands | |
* _.callback = _.wrap(_.callback, function(callback, func, thisArg) { | |
* var match = /^(.+?)__([gl]t)(.+)$/.exec(func); | |
* if (!match) { | |
* return callback(func, thisArg); | |
* } | |
* return function(object) { | |
* return match[2] == 'gt' | |
* ? object[match[1]] > match[3] | |
* : object[match[1]] < match[3]; | |
* }; | |
* }); | |
* | |
* _.filter(users, 'age__gt36'); | |
* // => [{ 'user': 'fred', 'age': 40 }] | |
*/ | |
function callback(func, thisArg, guard) { | |
if (guard && isIterateeCall(func, thisArg, guard)) { | |
thisArg = null; | |
} | |
return isObjectLike(func) | |
? matches(func) | |
: baseCallback(func, thisArg); | |
} | |
/** | |
* Creates a function that returns `value`. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @param {*} value The value to return from the new function. | |
* @returns {Function} Returns the new function. | |
* @example | |
* | |
* var object = { 'user': 'fred' }; | |
* var getter = _.constant(object); | |
* | |
* getter() === object; | |
* // => true | |
*/ | |
function constant(value) { | |
return function() { | |
return value; | |
}; | |
} | |
/** | |
* This method returns the first argument provided to it. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @param {*} value Any value. | |
* @returns {*} Returns `value`. | |
* @example | |
* | |
* var object = { 'user': 'fred' }; | |
* | |
* _.identity(object) === object; | |
* // => true | |
*/ | |
function identity(value) { | |
return value; | |
} | |
/** | |
* Creates a function that performs a deep comparison between a given object | |
* and `source`, returning `true` if the given object has equivalent property | |
* values, else `false`. | |
* | |
* **Note:** This method supports comparing arrays, booleans, `Date` objects, | |
* numbers, `Object` objects, regexes, and strings. Objects are compared by | |
* their own, not inherited, enumerable properties. For comparing a single | |
* own or inherited property value see `_.matchesProperty`. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @param {Object} source The object of property values to match. | |
* @returns {Function} Returns the new function. | |
* @example | |
* | |
* var users = [ | |
* { 'user': 'barney', 'age': 36, 'active': true }, | |
* { 'user': 'fred', 'age': 40, 'active': false } | |
* ]; | |
* | |
* _.filter(users, _.matches({ 'age': 40, 'active': false })); | |
* // => [{ 'user': 'fred', 'age': 40, 'active': false }] | |
*/ | |
function matches(source) { | |
return baseMatches(baseClone(source, true)); | |
} | |
/** | |
* A no-operation function that returns `undefined` regardless of the | |
* arguments it receives. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @example | |
* | |
* var object = { 'user': 'fred' }; | |
* | |
* _.noop(object) === undefined; | |
* // => true | |
*/ | |
function noop() { | |
// No operation performed. | |
} | |
/** | |
* Creates a function that returns the property value at `path` on a | |
* given object. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @param {Array|string} path The path of the property to get. | |
* @returns {Function} Returns the new function. | |
* @example | |
* | |
* var objects = [ | |
* { 'a': { 'b': { 'c': 2 } } }, | |
* { 'a': { 'b': { 'c': 1 } } } | |
* ]; | |
* | |
* _.map(objects, _.property('a.b.c')); | |
* // => [2, 1] | |
* | |
* _.pluck(_.sortBy(objects, _.property(['a', 'b', 'c'])), 'a.b.c'); | |
* // => [1, 2] | |
*/ | |
function property(path) { | |
return isKey(path) ? baseProperty(path) : basePropertyDeep(path); | |
} | |
/** | |
* Generates a unique ID. If `prefix` is provided the ID is appended to it. | |
* | |
* @static | |
* @memberOf _ | |
* @category Utility | |
* @param {string} [prefix] The value to prefix the ID with. | |
* @returns {string} Returns the unique ID. | |
* @example | |
* | |
* _.uniqueId('contact_'); | |
* // => 'contact_104' | |
* | |
* _.uniqueId(); | |
* // => '105' | |
*/ | |
function uniqueId(prefix) { | |
var id = ++idCounter; | |
return baseToString(prefix) + id; | |
} | |
/*------------------------------------------------------------------------*/ | |
LazyWrapper.prototype = baseCreate(baseLodash.prototype); | |
LazyWrapper.prototype.constructor = LazyWrapper; | |
// Add functions to the `Set` cache. | |
SetCache.prototype.push = cachePush; | |
// Add functions that return wrapped values when chaining. | |
lodash.assign = assign; | |
lodash.bind = bind; | |
lodash.callback = callback; | |
lodash.constant = constant; | |
lodash.defaults = defaults; | |
lodash.defer = defer; | |
lodash.difference = difference; | |
lodash.forEach = forEach; | |
lodash.intersection = intersection; | |
lodash.invoke = invoke; | |
lodash.keys = keys; | |
lodash.keysIn = keysIn; | |
lodash.map = map; | |
lodash.matches = matches; | |
lodash.omit = omit; | |
lodash.pairs = pairs; | |
lodash.partial = partial; | |
lodash.property = property; | |
lodash.restParam = restParam; | |
lodash.values = values; | |
// Add aliases. | |
lodash.collect = map; | |
lodash.each = forEach; | |
lodash.extend = assign; | |
lodash.iteratee = callback; | |
/*------------------------------------------------------------------------*/ | |
// Add functions that return unwrapped values when chaining. | |
lodash.clone = clone; | |
lodash.escapeRegExp = escapeRegExp; | |
lodash.every = every; | |
lodash.find = find; | |
lodash.identity = identity; | |
lodash.indexOf = indexOf; | |
lodash.isArguments = isArguments; | |
lodash.isArray = isArray; | |
lodash.isElement = isElement; | |
lodash.isEqual = isEqual; | |
lodash.isFunction = isFunction; | |
lodash.isNative = isNative; | |
lodash.isNumber = isNumber; | |
lodash.isObject = isObject; | |
lodash.isPlainObject = isPlainObject; | |
lodash.isString = isString; | |
lodash.isTypedArray = isTypedArray; | |
lodash.last = last; | |
lodash.noop = noop; | |
lodash.now = now; | |
lodash.reduce = reduce; | |
lodash.uniqueId = uniqueId; | |
// Add aliases. | |
lodash.all = every; | |
lodash.eq = isEqual; | |
lodash.detect = find; | |
lodash.foldl = reduce; | |
lodash.inject = reduce; | |
/*------------------------------------------------------------------------*/ | |
/** | |
* The semantic version number. | |
* | |
* @static | |
* @memberOf _ | |
* @type string | |
*/ | |
lodash.VERSION = VERSION; | |
// Assign default placeholders. | |
arrayEach(['bind', 'partial'], function(methodName) { | |
lodash[methodName].placeholder = lodash; | |
}); | |
/*--------------------------------------------------------------------------*/ | |
// Some AMD build optimizers like r.js check for condition patterns like the following: | |
if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) { | |
// Expose lodash to the global object when an AMD loader is present to avoid | |
// errors in cases where lodash is loaded by a script tag and not intended | |
// as an AMD module. See http://requirejs.org/docs/errors.html#mismatch for | |
// more details. | |
root._ = lodash; | |
// Define as an anonymous module so, through path mapping, it can be | |
// referenced as the "underscore" module. | |
define(function() { | |
return lodash; | |
}); | |
} | |
// Check for `exports` after `define` in case a build optimizer adds an `exports` object. | |
else if (freeExports && freeModule) { | |
// Export for Node.js or RingoJS. | |
if (moduleExports) { | |
(freeModule.exports = lodash)._ = lodash; | |
} | |
// Export for Rhino with CommonJS support. | |
else { | |
freeExports._ = lodash; | |
} | |
} | |
else { | |
// Export for a browser or Rhino. | |
root._ = lodash; | |
} | |
}.call(this)); | |
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
},{}],2:[function(_dereq_,module,exports){ | |
/*! | |
* EventEmitter2 | |
* https://github.com/hij1nx/EventEmitter2 | |
* | |
* Copyright (c) 2013 hij1nx | |
* Licensed under the MIT license. | |
*/ | |
;!function(undefined) { | |
var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { | |
return Object.prototype.toString.call(obj) === "[object Array]"; | |
}; | |
var defaultMaxListeners = 10; | |
function init() { | |
this._events = {}; | |
if (this._conf) { | |
configure.call(this, this._conf); | |
} | |
} | |
function configure(conf) { | |
if (conf) { | |
this._conf = conf; | |
conf.delimiter && (this.delimiter = conf.delimiter); | |
conf.maxListeners && (this._events.maxListeners = conf.maxListeners); | |
conf.wildcard && (this.wildcard = conf.wildcard); | |
conf.newListener && (this.newListener = conf.newListener); | |
if (this.wildcard) { | |
this.listenerTree = {}; | |
} | |
} | |
} | |
function EventEmitter(conf) { | |
this._events = {}; | |
this.newListener = false; | |
configure.call(this, conf); | |
} | |
// | |
// Attention, function return type now is array, always ! | |
// It has zero elements if no any matches found and one or more | |
// elements (leafs) if there are matches | |
// | |
function searchListenerTree(handlers, type, tree, i) { | |
if (!tree) { | |
return []; | |
} | |
var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, | |
typeLength = type.length, currentType = type[i], nextType = type[i+1]; | |
if (i === typeLength && tree._listeners) { | |
// | |
// If at the end of the event(s) list and the tree has listeners | |
// invoke those listeners. | |
// | |
if (typeof tree._listeners === 'function') { | |
handlers && handlers.push(tree._listeners); | |
return [tree]; | |
} else { | |
for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { | |
handlers && handlers.push(tree._listeners[leaf]); | |
} | |
return [tree]; | |
} | |
} | |
if ((currentType === '*' || currentType === '**') || tree[currentType]) { | |
// | |
// If the event emitted is '*' at this part | |
// or there is a concrete match at this patch | |
// | |
if (currentType === '*') { | |
for (branch in tree) { | |
if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); | |
} | |
} | |
return listeners; | |
} else if(currentType === '**') { | |
endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); | |
if(endReached && tree._listeners) { | |
// The next element has a _listeners, add it to the handlers. | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); | |
} | |
for (branch in tree) { | |
if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | |
if(branch === '*' || branch === '**') { | |
if(tree[branch]._listeners && !endReached) { | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); | |
} | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | |
} else if(branch === nextType) { | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); | |
} else { | |
// No match on this one, shift into the tree but not in the type array. | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | |
} | |
} | |
} | |
return listeners; | |
} | |
listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); | |
} | |
xTree = tree['*']; | |
if (xTree) { | |
// | |
// If the listener tree will allow any match for this part, | |
// then recursively explore all branches of the tree | |
// | |
searchListenerTree(handlers, type, xTree, i+1); | |
} | |
xxTree = tree['**']; | |
if(xxTree) { | |
if(i < typeLength) { | |
if(xxTree._listeners) { | |
// If we have a listener on a '**', it will catch all, so add its handler. | |
searchListenerTree(handlers, type, xxTree, typeLength); | |
} | |
// Build arrays of matching next branches and others. | |
for(branch in xxTree) { | |
if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { | |
if(branch === nextType) { | |
// We know the next element will match, so jump twice. | |
searchListenerTree(handlers, type, xxTree[branch], i+2); | |
} else if(branch === currentType) { | |
// Current node matches, move into the tree. | |
searchListenerTree(handlers, type, xxTree[branch], i+1); | |
} else { | |
isolatedBranch = {}; | |
isolatedBranch[branch] = xxTree[branch]; | |
searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); | |
} | |
} | |
} | |
} else if(xxTree._listeners) { | |
// We have reached the end and still on a '**' | |
searchListenerTree(handlers, type, xxTree, typeLength); | |
} else if(xxTree['*'] && xxTree['*']._listeners) { | |
searchListenerTree(handlers, type, xxTree['*'], typeLength); | |
} | |
} | |
return listeners; | |
} | |
function growListenerTree(type, listener) { | |
type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |
// | |
// Looks for two consecutive '**', if so, don't add the event at all. | |
// | |
for(var i = 0, len = type.length; i+1 < len; i++) { | |
if(type[i] === '**' && type[i+1] === '**') { | |
return; | |
} | |
} | |
var tree = this.listenerTree; | |
var name = type.shift(); | |
while (name) { | |
if (!tree[name]) { | |
tree[name] = {}; | |
} | |
tree = tree[name]; | |
if (type.length === 0) { | |
if (!tree._listeners) { | |
tree._listeners = listener; | |
} | |
else if(typeof tree._listeners === 'function') { | |
tree._listeners = [tree._listeners, listener]; | |
} | |
else if (isArray(tree._listeners)) { | |
tree._listeners.push(listener); | |
if (!tree._listeners.warned) { | |
var m = defaultMaxListeners; | |
if (typeof this._events.maxListeners !== 'undefined') { | |
m = this._events.maxListeners; | |
} | |
if (m > 0 && tree._listeners.length > m) { | |
tree._listeners.warned = true; | |
console.error('(node) warning: possible EventEmitter memory ' + | |
'leak detected. %d listeners added. ' + | |
'Use emitter.setMaxListeners() to increase limit.', | |
tree._listeners.length); | |
console.trace(); | |
} | |
} | |
} | |
return true; | |
} | |
name = type.shift(); | |
} | |
return true; | |
} | |
// By default EventEmitters will print a warning if more than | |
// 10 listeners are added to it. This is a useful default which | |
// helps finding memory leaks. | |
// | |
// Obviously not all Emitters should be limited to 10. This function allows | |
// that to be increased. Set to zero for unlimited. | |
EventEmitter.prototype.delimiter = '.'; | |
EventEmitter.prototype.setMaxListeners = function(n) { | |
this._events || init.call(this); | |
this._events.maxListeners = n; | |
if (!this._conf) this._conf = {}; | |
this._conf.maxListeners = n; | |
}; | |
EventEmitter.prototype.event = ''; | |
EventEmitter.prototype.once = function(event, fn) { | |
this.many(event, 1, fn); | |
return this; | |
}; | |
EventEmitter.prototype.many = function(event, ttl, fn) { | |
var self = this; | |
if (typeof fn !== 'function') { | |
throw new Error('many only accepts instances of Function'); | |
} | |
function listener() { | |
if (--ttl === 0) { | |
self.off(event, listener); | |
} | |
fn.apply(this, arguments); | |
} | |
listener._origin = fn; | |
this.on(event, listener); | |
return self; | |
}; | |
EventEmitter.prototype.emit = function() { | |
this._events || init.call(this); | |
var type = arguments[0]; | |
if (type === 'newListener' && !this.newListener) { | |
if (!this._events.newListener) { return false; } | |
} | |
// Loop through the *_all* functions and invoke them. | |
if (this._all) { | |
var l = arguments.length; | |
var args = new Array(l - 1); | |
for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | |
for (i = 0, l = this._all.length; i < l; i++) { | |
this.event = type; | |
this._all[i].apply(this, args); | |
} | |
} | |
// If there is no 'error' event listener then throw. | |
if (type === 'error') { | |
if (!this._all && | |
!this._events.error && | |
!(this.wildcard && this.listenerTree.error)) { | |
if (arguments[1] instanceof Error) { | |
throw arguments[1]; // Unhandled 'error' event | |
} else { | |
throw new Error("Uncaught, unspecified 'error' event."); | |
} | |
return false; | |
} | |
} | |
var handler; | |
if(this.wildcard) { | |
handler = []; | |
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |
searchListenerTree.call(this, handler, ns, this.listenerTree, 0); | |
} | |
else { | |
handler = this._events[type]; | |
} | |
if (typeof handler === 'function') { | |
this.event = type; | |
if (arguments.length === 1) { | |
handler.call(this); | |
} | |
else if (arguments.length > 1) | |
switch (arguments.length) { | |
case 2: | |
handler.call(this, arguments[1]); | |
break; | |
case 3: | |
handler.call(this, arguments[1], arguments[2]); | |
break; | |
// slower | |
default: | |
var l = arguments.length; | |
var args = new Array(l - 1); | |
for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | |
handler.apply(this, args); | |
} | |
return true; | |
} | |
else if (handler) { | |
var l = arguments.length; | |
var args = new Array(l - 1); | |
for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | |
var listeners = handler.slice(); | |
for (var i = 0, l = listeners.length; i < l; i++) { | |
this.event = type; | |
listeners[i].apply(this, args); | |
} | |
return (listeners.length > 0) || !!this._all; | |
} | |
else { | |
return !!this._all; | |
} | |
}; | |
EventEmitter.prototype.on = function(type, listener) { | |
if (typeof type === 'function') { | |
this.onAny(type); | |
return this; | |
} | |
if (typeof listener !== 'function') { | |
throw new Error('on only accepts instances of Function'); | |
} | |
this._events || init.call(this); | |
// To avoid recursion in the case that type == "newListeners"! Before | |
// adding it to the listeners, first emit "newListeners". | |
this.emit('newListener', type, listener); | |
if(this.wildcard) { | |
growListenerTree.call(this, type, listener); | |
return this; | |
} | |
if (!this._events[type]) { | |
// Optimize the case of one listener. Don't need the extra array object. | |
this._events[type] = listener; | |
} | |
else if(typeof this._events[type] === 'function') { | |
// Adding the second element, need to change to array. | |
this._events[type] = [this._events[type], listener]; | |
} | |
else if (isArray(this._events[type])) { | |
// If we've already got an array, just append. | |
this._events[type].push(listener); | |
// Check for listener leak | |
if (!this._events[type].warned) { | |
var m = defaultMaxListeners; | |
if (typeof this._events.maxListeners !== 'undefined') { | |
m = this._events.maxListeners; | |
} | |
if (m > 0 && this._events[type].length > m) { | |
this._events[type].warned = true; | |
console.error('(node) warning: possible EventEmitter memory ' + | |
'leak detected. %d listeners added. ' + | |
'Use emitter.setMaxListeners() to increase limit.', | |
this._events[type].length); | |
console.trace(); | |
} | |
} | |
} | |
return this; | |
}; | |
EventEmitter.prototype.onAny = function(fn) { | |
if (typeof fn !== 'function') { | |
throw new Error('onAny only accepts instances of Function'); | |
} | |
if(!this._all) { | |
this._all = []; | |
} | |
// Add the function to the event listener collection. | |
this._all.push(fn); | |
return this; | |
}; | |
EventEmitter.prototype.addListener = EventEmitter.prototype.on; | |
EventEmitter.prototype.off = function(type, listener) { | |
if (typeof listener !== 'function') { | |
throw new Error('removeListener only takes instances of Function'); | |
} | |
var handlers,leafs=[]; | |
if(this.wildcard) { | |
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |
leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | |
} | |
else { | |
// does not use listeners(), so no side effect of creating _events[type] | |
if (!this._events[type]) return this; | |
handlers = this._events[type]; | |
leafs.push({_listeners:handlers}); | |
} | |
for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | |
var leaf = leafs[iLeaf]; | |
handlers = leaf._listeners; | |
if (isArray(handlers)) { | |
var position = -1; | |
for (var i = 0, length = handlers.length; i < length; i++) { | |
if (handlers[i] === listener || | |
(handlers[i].listener && handlers[i].listener === listener) || | |
(handlers[i]._origin && handlers[i]._origin === listener)) { | |
position = i; | |
break; | |
} | |
} | |
if (position < 0) { | |
continue; | |
} | |
if(this.wildcard) { | |
leaf._listeners.splice(position, 1); | |
} | |
else { | |
this._events[type].splice(position, 1); | |
} | |
if (handlers.length === 0) { | |
if(this.wildcard) { | |
delete leaf._listeners; | |
} | |
else { | |
delete this._events[type]; | |
} | |
} | |
return this; | |
} | |
else if (handlers === listener || | |
(handlers.listener && handlers.listener === listener) || | |
(handlers._origin && handlers._origin === listener)) { | |
if(this.wildcard) { | |
delete leaf._listeners; | |
} | |
else { | |
delete this._events[type]; | |
} | |
} | |
} | |
return this; | |
}; | |
EventEmitter.prototype.offAny = function(fn) { | |
var i = 0, l = 0, fns; | |
if (fn && this._all && this._all.length > 0) { | |
fns = this._all; | |
for(i = 0, l = fns.length; i < l; i++) { | |
if(fn === fns[i]) { | |
fns.splice(i, 1); | |
return this; | |
} | |
} | |
} else { | |
this._all = []; | |
} | |
return this; | |
}; | |
EventEmitter.prototype.removeListener = EventEmitter.prototype.off; | |
EventEmitter.prototype.removeAllListeners = function(type) { | |
if (arguments.length === 0) { | |
!this._events || init.call(this); | |
return this; | |
} | |
if(this.wildcard) { | |
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |
var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | |
for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | |
var leaf = leafs[iLeaf]; | |
leaf._listeners = null; | |
} | |
} | |
else { | |
if (!this._events[type]) return this; | |
this._events[type] = null; | |
} | |
return this; | |
}; | |
EventEmitter.prototype.listeners = function(type) { | |
if(this.wildcard) { | |
var handlers = []; | |
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | |
searchListenerTree.call(this, handlers, ns, this.listenerTree, 0); | |
return handlers; | |
} | |
this._events || init.call(this); | |
if (!this._events[type]) this._events[type] = []; | |
if (!isArray(this._events[type])) { | |
this._events[type] = [this._events[type]]; | |
} | |
return this._events[type]; | |
}; | |
EventEmitter.prototype.listenersAny = function() { | |
if(this._all) { | |
return this._all; | |
} | |
else { | |
return []; | |
} | |
}; | |
if (typeof define === 'function' && define.amd) { | |
// AMD. Register as an anonymous module. | |
define(function() { | |
return EventEmitter; | |
}); | |
} else if (typeof exports === 'object') { | |
// CommonJS | |
exports.EventEmitter2 = EventEmitter; | |
} | |
else { | |
// Browser global. | |
window.EventEmitter2 = EventEmitter; | |
} | |
}(); | |
},{}],3:[function(_dereq_,module,exports){ | |
var diff = _dereq_('fast-diff'); | |
var is = _dereq_('./is'); | |
var op = _dereq_('./op'); | |
var NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff() | |
var Delta = function (ops) { | |
// Assume we are given a well formed ops | |
if (is.array(ops)) { | |
this.ops = ops; | |
} else if (is.object(ops) && is.array(ops.ops)) { | |
this.ops = ops.ops; | |
} else { | |
this.ops = []; | |
} | |
}; | |
Delta.prototype.insert = function (text, attributes) { | |
var newOp = {}; | |
if (text.length === 0) return this; | |
newOp.insert = text; | |
if (is.object(attributes) && Object.keys(attributes).length > 0) newOp.attributes = attributes; | |
return this.push(newOp); | |
}; | |
Delta.prototype['delete'] = function (length) { | |
if (length <= 0) return this; | |
return this.push({ 'delete': length }); | |
}; | |
Delta.prototype.retain = function (length, attributes) { | |
if (length <= 0) return this; | |
var newOp = { retain: length }; | |
if (is.object(attributes) && Object.keys(attributes).length > 0) newOp.attributes = attributes; | |
return this.push(newOp); | |
}; | |
Delta.prototype.push = function (newOp) { | |
var index = this.ops.length; | |
var lastOp = this.ops[index - 1]; | |
newOp = op.clone(newOp); | |
if (is.object(lastOp)) { | |
if (is.number(newOp['delete']) && is.number(lastOp['delete'])) { | |
this.ops[index - 1] = { 'delete': lastOp['delete'] + newOp['delete'] }; | |
return this; | |
} | |
// Since it does not matter if we insert before or after deleting at the same index, | |
// always prefer to insert first | |
if (is.number(lastOp['delete']) && newOp.insert != null) { | |
index -= 1; | |
lastOp = this.ops[index - 1]; | |
if (!is.object(lastOp)) { | |
this.ops.unshift(newOp); | |
return this; | |
} | |
} | |
if (is.equal(newOp.attributes, lastOp.attributes)) { | |
if (is.string(newOp.insert) && is.string(lastOp.insert)) { | |
this.ops[index - 1] = { insert: lastOp.insert + newOp.insert }; | |
if (is.object(newOp.attributes)) this.ops[index - 1].attributes = newOp.attributes | |
return this; | |
} else if (is.number(newOp.retain) && is.number(lastOp.retain)) { | |
this.ops[index - 1] = { retain: lastOp.retain + newOp.retain }; | |
if (is.object(newOp.attributes)) this.ops[index - 1].attributes = newOp.attributes | |
return this; | |
} | |
} | |
} | |
if (index === this.ops.length) { | |
this.ops.push(newOp); | |
} else { | |
this.ops.splice(index, 0, newOp); | |
} | |
return this; | |
}; | |
Delta.prototype.chop = function () { | |
var lastOp = this.ops[this.ops.length - 1]; | |
if (lastOp && lastOp.retain && !lastOp.attributes) { | |
this.ops.pop(); | |
} | |
return this; | |
}; | |
Delta.prototype.length = function () { | |
return this.ops.reduce(function (length, elem) { | |
return length + op.length(elem); | |
}, 0); | |
}; | |
Delta.prototype.slice = function (start, end) { | |
start = start || 0; | |
if (!is.number(end)) end = Infinity; | |
var delta = new Delta(); | |
var iter = op.iterator(this.ops); | |
var index = 0; | |
while (index < end && iter.hasNext()) { | |
var nextOp; | |
if (index < start) { | |
nextOp = iter.next(start - index); | |
} else { | |
nextOp = iter.next(end - index); | |
delta.push(nextOp); | |
} | |
index += op.length(nextOp); | |
} | |
return delta; | |
}; | |
Delta.prototype.compose = function (other) { | |
var thisIter = op.iterator(this.ops); | |
var otherIter = op.iterator(other.ops); | |
var delta = new Delta(); | |
while (thisIter.hasNext() || otherIter.hasNext()) { | |
if (otherIter.peekType() === 'insert') { | |
delta.push(otherIter.next()); | |
} else if (thisIter.peekType() === 'delete') { | |
delta.push(thisIter.next()); | |
} else { | |
var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); | |
var thisOp = thisIter.next(length); | |
var otherOp = otherIter.next(length); | |
if (is.number(otherOp.retain)) { | |
var newOp = {}; | |
if (is.number(thisOp.retain)) { | |
newOp.retain = length; | |
} else { | |
newOp.insert = thisOp.insert; | |
} | |
// Preserve null when composing with a retain, otherwise remove it for inserts | |
var attributes = op.attributes.compose(thisOp.attributes, otherOp.attributes, is.number(thisOp.retain)); | |
if (attributes) newOp.attributes = attributes; | |
delta.push(newOp); | |
// Other op should be delete, we could be an insert or retain | |
// Insert + delete cancels out | |
} else if (is.number(otherOp['delete']) && is.number(thisOp.retain)) { | |
delta.push(otherOp); | |
} | |
} | |
} | |
return delta.chop(); | |
}; | |
Delta.prototype.diff = function (other) { | |
var delta = new Delta(); | |
if (this.ops === other.ops) { | |
return delta; | |
} | |
var strings = [this.ops, other.ops].map(function (ops) { | |
return ops.map(function (op) { | |
if (op.insert != null) { | |
return is.string(op.insert) ? op.insert : NULL_CHARACTER; | |
} | |
var prep = (ops === other.ops) ? 'on' : 'with'; | |
throw new Error('diff() called ' + prep + ' non-document'); | |
}).join(''); | |
}); | |
var diffResult = diff(strings[0], strings[1]); | |
var thisIter = op.iterator(this.ops); | |
var otherIter = op.iterator(other.ops); | |
diffResult.forEach(function (component) { | |
var length = component[1].length; | |
while (length > 0) { | |
var opLength = 0; | |
switch (component[0]) { | |
case diff.INSERT: | |
opLength = Math.min(otherIter.peekLength(), length); | |
delta.push(otherIter.next(opLength)); | |
break; | |
case diff.DELETE: | |
opLength = Math.min(length, thisIter.peekLength()); | |
thisIter.next(opLength); | |
delta['delete'](opLength); | |
break; | |
case diff.EQUAL: | |
opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length); | |
var thisOp = thisIter.next(opLength); | |
var otherOp = otherIter.next(opLength); | |
if (is.equal(thisOp.insert, otherOp.insert)) { | |
delta.retain(opLength, op.attributes.diff(thisOp.attributes, otherOp.attributes)); | |
} else { | |
delta.push(otherOp)['delete'](opLength); | |
} | |
break; | |
} | |
length -= opLength; | |
} | |
}); | |
return delta.chop(); | |
}; | |
Delta.prototype.transform = function (other, priority) { | |
priority = !!priority; | |
if (is.number(other)) { | |
return this.transformPosition(other, priority); | |
} | |
var thisIter = op.iterator(this.ops); | |
var otherIter = op.iterator(other.ops); | |
var delta = new Delta(); | |
while (thisIter.hasNext() || otherIter.hasNext()) { | |
if (thisIter.peekType() === 'insert' && (priority || otherIter.peekType() !== 'insert')) { | |
delta.retain(op.length(thisIter.next())); | |
} else if (otherIter.peekType() === 'insert') { | |
delta.push(otherIter.next()); | |
} else { | |
var length = Math.min(thisIter.peekLength(), otherIter.peekLength()); | |
var thisOp = thisIter.next(length); | |
var otherOp = otherIter.next(length); | |
if (thisOp['delete']) { | |
// Our delete either makes their delete redundant or removes their retain | |
continue; | |
} else if (otherOp['delete']) { | |
delta.push(otherOp); | |
} else { | |
// We retain either their retain or insert | |
delta.retain(length, op.attributes.transform(thisOp.attributes, otherOp.attributes, priority)); | |
} | |
} | |
} | |
return delta.chop(); | |
}; | |
Delta.prototype.transformPosition = function (index, priority) { | |
priority = !!priority; | |
var thisIter = op.iterator(this.ops); | |
var offset = 0; | |
while (thisIter.hasNext() && offset <= index) { | |
var length = thisIter.peekLength(); | |
var nextType = thisIter.peekType(); | |
thisIter.next(); | |
if (nextType === 'delete') { | |
index -= Math.min(length, index - offset); | |
continue; | |
} else if (nextType === 'insert' && (offset < index || !priority)) { | |
index += length; | |
} | |
offset += length; | |
} | |
return index; | |
}; | |
module.exports = Delta; | |
},{"./is":4,"./op":5,"fast-diff":6}],4:[function(_dereq_,module,exports){ | |
module.exports = { | |
equal: function (a, b) { | |
if (a === b) return true; | |
if (a == null && b == null) return true; | |
if (a == null || b == null) return false; | |
if (!this.object(a) || !this.object(b)) return false; | |
if (Object.keys(a).length != Object.keys(b).length) return false; | |
for(var key in a) { | |
// Only compare one level deep | |
if (a[key] !== b[key]) return false; | |
} | |
return true; | |
}, | |
array: function (value) { | |
return Array.isArray(value); | |
}, | |
number: function (value) { | |
if (typeof value === 'number') return true; | |
if (typeof value === 'object' && Object.prototype.toString.call(value) === '[object Number]') return true; | |
return false; | |
}, | |
object: function (value) { | |
if (!value) return false; | |
return (typeof value === 'function' || typeof value === 'object'); | |
}, | |
string: function (value) { | |
if (typeof value === 'string') return true; | |
if (typeof value === 'object' && Object.prototype.toString.call(value) === '[object String]') return true; | |
return false; | |
} | |
}; | |
},{}],5:[function(_dereq_,module,exports){ | |
var is = _dereq_('./is'); | |
var lib = { | |
attributes: { | |
clone: function (attributes, keepNull) { | |
if (!is.object(attributes)) return {}; | |
return Object.keys(attributes).reduce(function (memo, key) { | |
if (attributes[key] !== undefined && (attributes[key] !== null || keepNull)) { | |
memo[key] = attributes[key]; | |
} | |
return memo; | |
}, {}); | |
}, | |
compose: function (a, b, keepNull) { | |
if (!is.object(a)) a = {}; | |
if (!is.object(b)) b = {}; | |
var attributes = this.clone(b, keepNull); | |
for (var key in a) { | |
if (a[key] !== undefined && b[key] === undefined) { | |
attributes[key] = a[key]; | |
} | |
} | |
return Object.keys(attributes).length > 0 ? attributes : undefined; | |
}, | |
diff: function(a, b) { | |
if (!is.object(a)) a = {}; | |
if (!is.object(b)) b = {}; | |
var attributes = Object.keys(a).concat(Object.keys(b)).reduce(function (attributes, key) { | |
if (a[key] !== b[key]) { | |
attributes[key] = b[key] === undefined ? null : b[key]; | |
} | |
return attributes; | |
}, {}); | |
return Object.keys(attributes).length > 0 ? attributes : undefined; | |
}, | |
transform: function (a, b, priority) { | |
if (!is.object(a)) return b; | |
if (!is.object(b)) return undefined; | |
if (!priority) return b; // b simply overwrites us without priority | |
var attributes = Object.keys(b).reduce(function (attributes, key) { | |
if (a[key] === undefined) attributes[key] = b[key]; // null is a valid value | |
return attributes; | |
}, {}); | |
return Object.keys(attributes).length > 0 ? attributes : undefined; | |
} | |
}, | |
clone: function (op) { | |
var newOp = this.attributes.clone(op); | |
if (is.object(newOp.attributes)) { | |
newOp.attributes = this.attributes.clone(newOp.attributes, true); | |
} | |
return newOp; | |
}, | |
iterator: function (ops) { | |
return new Iterator(ops); | |
}, | |
length: function (op) { | |
if (is.number(op['delete'])) { | |
return op['delete']; | |
} else if (is.number(op.retain)) { | |
return op.retain; | |
} else { | |
return is.string(op.insert) ? op.insert.length : 1; | |
} | |
} | |
}; | |
function Iterator(ops) { | |
this.ops = ops; | |
this.index = 0; | |
this.offset = 0; | |
}; | |
Iterator.prototype.hasNext = function () { | |
return this.peekLength() < Infinity; | |
}; | |
Iterator.prototype.next = function (length) { | |
if (!length) length = Infinity; | |
var nextOp = this.ops[this.index]; | |
if (nextOp) { | |
var offset = this.offset; | |
var opLength = lib.length(nextOp) | |
if (length >= opLength - offset) { | |
length = opLength - offset; | |
this.index += 1; | |
this.offset = 0; | |
} else { | |
this.offset += length; | |
} | |
if (is.number(nextOp['delete'])) { | |
return { 'delete': length }; | |
} else { | |
var retOp = {}; | |
if (nextOp.attributes) { | |
retOp.attributes = nextOp.attributes; | |
} | |
if (is.number(nextOp.retain)) { | |
retOp.retain = length; | |
} else if (is.string(nextOp.insert)) { | |
retOp.insert = nextOp.insert.substr(offset, length); | |
} else { | |
// offset should === 0, length should === 1 | |
retOp.insert = nextOp.insert; | |
} | |
return retOp; | |
} | |
} else { | |
return { retain: Infinity }; | |
} | |
}; | |
Iterator.prototype.peekLength = function () { | |
if (this.ops[this.index]) { | |
// Should never return 0 if our index is being managed correctly | |
return lib.length(this.ops[this.index]) - this.offset; | |
} else { | |
return Infinity; | |
} | |
}; | |
Iterator.prototype.peekType = function () { | |
if (this.ops[this.index]) { | |
if (is.number(this.ops[this.index]['delete'])) { | |
return 'delete'; | |
} else if (is.number(this.ops[this.index].retain)) { | |
return 'retain'; | |
} else { | |
return 'insert'; | |
} | |
} | |
return 'retain'; | |
}; | |
module.exports = lib; | |
},{"./is":4}],6:[function(_dereq_,module,exports){ | |
/** | |
* This library modifies the diff-patch-match library by Neil Fraser | |
* by removing the patch and match functionality and certain advanced | |
* options in the diff function. The original license is as follows: | |
* | |
* === | |
* | |
* Diff Match and Patch | |
* | |
* Copyright 2006 Google Inc. | |
* http://code.google.com/p/google-diff-match-patch/ | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/** | |
* The data structure representing a diff is an array of tuples: | |
* [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] | |
* which means: delete 'Hello', add 'Goodbye' and keep ' world.' | |
*/ | |
var DIFF_DELETE = -1; | |
var DIFF_INSERT = 1; | |
var DIFF_EQUAL = 0; | |
/** | |
* Find the differences between two texts. Simplifies the problem by stripping | |
* any common prefix or suffix off the texts before diffing. | |
* @param {string} text1 Old string to be diffed. | |
* @param {string} text2 New string to be diffed. | |
* @return {Array} Array of diff tuples. | |
*/ | |
function diff_main(text1, text2) { | |
// Check for equality (speedup). | |
if (text1 == text2) { | |
if (text1) { | |
return [[DIFF_EQUAL, text1]]; | |
} | |
return []; | |
} | |
// Trim off common prefix (speedup). | |
var commonlength = diff_commonPrefix(text1, text2); | |
var commonprefix = text1.substring(0, commonlength); | |
text1 = text1.substring(commonlength); | |
text2 = text2.substring(commonlength); | |
// Trim off common suffix (speedup). | |
commonlength = diff_commonSuffix(text1, text2); | |
var commonsuffix = text1.substring(text1.length - commonlength); | |
text1 = text1.substring(0, text1.length - commonlength); | |
text2 = text2.substring(0, text2.length - commonlength); | |
// Compute the diff on the middle block. | |
var diffs = diff_compute_(text1, text2); | |
// Restore the prefix and suffix. | |
if (commonprefix) { | |
diffs.unshift([DIFF_EQUAL, commonprefix]); | |
} | |
if (commonsuffix) { | |
diffs.push([DIFF_EQUAL, commonsuffix]); | |
} | |
diff_cleanupMerge(diffs); | |
return diffs; | |
}; | |
/** | |
* Find the differences between two texts. Assumes that the texts do not | |
* have any common prefix or suffix. | |
* @param {string} text1 Old string to be diffed. | |
* @param {string} text2 New string to be diffed. | |
* @return {Array} Array of diff tuples. | |
*/ | |
function diff_compute_(text1, text2) { | |
var diffs; | |
if (!text1) { | |
// Just add some text (speedup). | |
return [[DIFF_INSERT, text2]]; | |
} | |
if (!text2) { | |
// Just delete some text (speedup). | |
return [[DIFF_DELETE, text1]]; | |
} | |
var longtext = text1.length > text2.length ? text1 : text2; | |
var shorttext = text1.length > text2.length ? text2 : text1; | |
var i = longtext.indexOf(shorttext); | |
if (i != -1) { | |
// Shorter text is inside the longer text (speedup). | |
diffs = [[DIFF_INSERT, longtext.substring(0, i)], | |
[DIFF_EQUAL, shorttext], | |
[DIFF_INSERT, longtext.substring(i + shorttext.length)]]; | |
// Swap insertions for deletions if diff is reversed. | |
if (text1.length > text2.length) { | |
diffs[0][0] = diffs[2][0] = DIFF_DELETE; | |
} | |
return diffs; | |
} | |
if (shorttext.length == 1) { | |
// Single character string. | |
// After the previous speedup, the character can't be an equality. | |
return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | |
} | |
// Check to see if the problem can be split in two. | |
var hm = diff_halfMatch_(text1, text2); | |
if (hm) { | |
// A half-match was found, sort out the return data. | |
var text1_a = hm[0]; | |
var text1_b = hm[1]; | |
var text2_a = hm[2]; | |
var text2_b = hm[3]; | |
var mid_common = hm[4]; | |
// Send both pairs off for separate processing. | |
var diffs_a = diff_main(text1_a, text2_a); | |
var diffs_b = diff_main(text1_b, text2_b); | |
// Merge the results. | |
return diffs_a.concat([[DIFF_EQUAL, mid_common]], diffs_b); | |
} | |
return diff_bisect_(text1, text2); | |
}; | |
/** | |
* Find the 'middle snake' of a diff, split the problem in two | |
* and return the recursively constructed diff. | |
* See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. | |
* @param {string} text1 Old string to be diffed. | |
* @param {string} text2 New string to be diffed. | |
* @return {Array} Array of diff tuples. | |
* @private | |
*/ | |
function diff_bisect_(text1, text2) { | |
// Cache the text lengths to prevent multiple calls. | |
var text1_length = text1.length; | |
var text2_length = text2.length; | |
var max_d = Math.ceil((text1_length + text2_length) / 2); | |
var v_offset = max_d; | |
var v_length = 2 * max_d; | |
var v1 = new Array(v_length); | |
var v2 = new Array(v_length); | |
// Setting all elements to -1 is faster in Chrome & Firefox than mixing | |
// integers and undefined. | |
for (var x = 0; x < v_length; x++) { | |
v1[x] = -1; | |
v2[x] = -1; | |
} | |
v1[v_offset + 1] = 0; | |
v2[v_offset + 1] = 0; | |
var delta = text1_length - text2_length; | |
// If the total number of characters is odd, then the front path will collide | |
// with the reverse path. | |
var front = (delta % 2 != 0); | |
// Offsets for start and end of k loop. | |
// Prevents mapping of space beyond the grid. | |
var k1start = 0; | |
var k1end = 0; | |
var k2start = 0; | |
var k2end = 0; | |
for (var d = 0; d < max_d; d++) { | |
// Walk the front path one step. | |
for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { | |
var k1_offset = v_offset + k1; | |
var x1; | |
if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { | |
x1 = v1[k1_offset + 1]; | |
} else { | |
x1 = v1[k1_offset - 1] + 1; | |
} | |
var y1 = x1 - k1; | |
while (x1 < text1_length && y1 < text2_length && | |
text1.charAt(x1) == text2.charAt(y1)) { | |
x1++; | |
y1++; | |
} | |
v1[k1_offset] = x1; | |
if (x1 > text1_length) { | |
// Ran off the right of the graph. | |
k1end += 2; | |
} else if (y1 > text2_length) { | |
// Ran off the bottom of the graph. | |
k1start += 2; | |
} else if (front) { | |
var k2_offset = v_offset + delta - k1; | |
if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { | |
// Mirror x2 onto top-left coordinate system. | |
var x2 = text1_length - v2[k2_offset]; | |
if (x1 >= x2) { | |
// Overlap detected. | |
return diff_bisectSplit_(text1, text2, x1, y1); | |
} | |
} | |
} | |
} | |
// Walk the reverse path one step. | |
for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { | |
var k2_offset = v_offset + k2; | |
var x2; | |
if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { | |
x2 = v2[k2_offset + 1]; | |
} else { | |
x2 = v2[k2_offset - 1] + 1; | |
} | |
var y2 = x2 - k2; | |
while (x2 < text1_length && y2 < text2_length && | |
text1.charAt(text1_length - x2 - 1) == | |
text2.charAt(text2_length - y2 - 1)) { | |
x2++; | |
y2++; | |
} | |
v2[k2_offset] = x2; | |
if (x2 > text1_length) { | |
// Ran off the left of the graph. | |
k2end += 2; | |
} else if (y2 > text2_length) { | |
// Ran off the top of the graph. | |
k2start += 2; | |
} else if (!front) { | |
var k1_offset = v_offset + delta - k2; | |
if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { | |
var x1 = v1[k1_offset]; | |
var y1 = v_offset + x1 - k1_offset; | |
// Mirror x2 onto top-left coordinate system. | |
x2 = text1_length - x2; | |
if (x1 >= x2) { | |
// Overlap detected. | |
return diff_bisectSplit_(text1, text2, x1, y1); | |
} | |
} | |
} | |
} | |
} | |
// Diff took too long and hit the deadline or | |
// number of diffs equals number of characters, no commonality at all. | |
return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]]; | |
}; | |
/** | |
* Given the location of the 'middle snake', split the diff in two parts | |
* and recurse. | |
* @param {string} text1 Old string to be diffed. | |
* @param {string} text2 New string to be diffed. | |
* @param {number} x Index of split point in text1. | |
* @param {number} y Index of split point in text2. | |
* @return {Array} Array of diff tuples. | |
*/ | |
function diff_bisectSplit_(text1, text2, x, y) { | |
var text1a = text1.substring(0, x); | |
var text2a = text2.substring(0, y); | |
var text1b = text1.substring(x); | |
var text2b = text2.substring(y); | |
// Compute both diffs serially. | |
var diffs = diff_main(text1a, text2a); | |
var diffsb = diff_main(text1b, text2b); | |
return diffs.concat(diffsb); | |
}; | |
/** | |
* Determine the common prefix of two strings. | |
* @param {string} text1 First string. | |
* @param {string} text2 Second string. | |
* @return {number} The number of characters common to the start of each | |
* string. | |
*/ | |
function diff_commonPrefix(text1, text2) { | |
// Quick check for common null cases. | |
if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) { | |
return 0; | |
} | |
// Binary search. | |
// Performance analysis: http://neil.fraser.name/news/2007/10/09/ | |
var pointermin = 0; | |
var pointermax = Math.min(text1.length, text2.length); | |
var pointermid = pointermax; | |
var pointerstart = 0; | |
while (pointermin < pointermid) { | |
if (text1.substring(pointerstart, pointermid) == | |
text2.substring(pointerstart, pointermid)) { | |
pointermin = pointermid; | |
pointerstart = pointermin; | |
} else { | |
pointermax = pointermid; | |
} | |
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); | |
} | |
return pointermid; | |
}; | |
/** | |
* Determine the common suffix of two strings. | |
* @param {string} text1 First string. | |
* @param {string} text2 Second string. | |
* @return {number} The number of characters common to the end of each string. | |
*/ | |
function diff_commonSuffix(text1, text2) { | |
// Quick check for common null cases. | |
if (!text1 || !text2 || | |
text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) { | |
return 0; | |
} | |
// Binary search. | |
// Performance analysis: http://neil.fraser.name/news/2007/10/09/ | |
var pointermin = 0; | |
var pointermax = Math.min(text1.length, text2.length); | |
var pointermid = pointermax; | |
var pointerend = 0; | |
while (pointermin < pointermid) { | |
if (text1.substring(text1.length - pointermid, text1.length - pointerend) == | |
text2.substring(text2.length - pointermid, text2.length - pointerend)) { | |
pointermin = pointermid; | |
pointerend = pointermin; | |
} else { | |
pointermax = pointermid; | |
} | |
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); | |
} | |
return pointermid; | |
}; | |
/** | |
* Do the two texts share a substring which is at least half the length of the | |
* longer text? | |
* This speedup can produce non-minimal diffs. | |
* @param {string} text1 First string. | |
* @param {string} text2 Second string. | |
* @return {Array.<string>} Five element Array, containing the prefix of | |
* text1, the suffix of text1, the prefix of text2, the suffix of | |
* text2 and the common middle. Or null if there was no match. | |
*/ | |
function diff_halfMatch_(text1, text2) { | |
var longtext = text1.length > text2.length ? text1 : text2; | |
var shorttext = text1.length > text2.length ? text2 : text1; | |
if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { | |
return null; // Pointless. | |
} | |
/** | |
* Does a substring of shorttext exist within longtext such that the substring | |
* is at least half the length of longtext? | |
* Closure, but does not reference any external variables. | |
* @param {string} longtext Longer string. | |
* @param {string} shorttext Shorter string. | |
* @param {number} i Start index of quarter length substring within longtext. | |
* @return {Array.<string>} Five element Array, containing the prefix of | |
* longtext, the suffix of longtext, the prefix of shorttext, the suffix | |
* of shorttext and the common middle. Or null if there was no match. | |
* @private | |
*/ | |
function diff_halfMatchI_(longtext, shorttext, i) { | |
// Start with a 1/4 length substring at position i as a seed. | |
var seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); | |
var j = -1; | |
var best_common = ''; | |
var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; | |
while ((j = shorttext.indexOf(seed, j + 1)) != -1) { | |
var prefixLength = diff_commonPrefix(longtext.substring(i), | |
shorttext.substring(j)); | |
var suffixLength = diff_commonSuffix(longtext.substring(0, i), | |
shorttext.substring(0, j)); | |
if (best_common.length < suffixLength + prefixLength) { | |
best_common = shorttext.substring(j - suffixLength, j) + | |
shorttext.substring(j, j + prefixLength); | |
best_longtext_a = longtext.substring(0, i - suffixLength); | |
best_longtext_b = longtext.substring(i + prefixLength); | |
best_shorttext_a = shorttext.substring(0, j - suffixLength); | |
best_shorttext_b = shorttext.substring(j + prefixLength); | |
} | |
} | |
if (best_common.length * 2 >= longtext.length) { | |
return [best_longtext_a, best_longtext_b, | |
best_shorttext_a, best_shorttext_b, best_common]; | |
} else { | |
return null; | |
} | |
} | |
// First check if the second quarter is the seed for a half-match. | |
var hm1 = diff_halfMatchI_(longtext, shorttext, | |
Math.ceil(longtext.length / 4)); | |
// Check again based on the third quarter. | |
var hm2 = diff_halfMatchI_(longtext, shorttext, | |
Math.ceil(longtext.length / 2)); | |
var hm; | |
if (!hm1 && !hm2) { | |
return null; | |
} else if (!hm2) { | |
hm = hm1; | |
} else if (!hm1) { | |
hm = hm2; | |
} else { | |
// Both matched. Select the longest. | |
hm = hm1[4].length > hm2[4].length ? hm1 : hm2; | |
} | |
// A half-match was found, sort out the return data. | |
var text1_a, text1_b, text2_a, text2_b; | |
if (text1.length > text2.length) { | |
text1_a = hm[0]; | |
text1_b = hm[1]; | |
text2_a = hm[2]; | |
text2_b = hm[3]; | |
} else { | |
text2_a = hm[0]; | |
text2_b = hm[1]; | |
text1_a = hm[2]; | |
text1_b = hm[3]; | |
} | |
var mid_common = hm[4]; | |
return [text1_a, text1_b, text2_a, text2_b, mid_common]; | |
}; | |
/** | |
* Reorder and merge like edit sections. Merge equalities. | |
* Any edit section can move as long as it doesn't cross an equality. | |
* @param {Array} diffs Array of diff tuples. | |
*/ | |
function diff_cleanupMerge(diffs) { | |
diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end. | |
var pointer = 0; | |
var count_delete = 0; | |
var count_insert = 0; | |
var text_delete = ''; | |
var text_insert = ''; | |
var commonlength; | |
while (pointer < diffs.length) { | |
switch (diffs[pointer][0]) { | |
case DIFF_INSERT: | |
count_insert++; | |
text_insert += diffs[pointer][1]; | |
pointer++; | |
break; | |
case DIFF_DELETE: | |
count_delete++; | |
text_delete += diffs[pointer][1]; | |
pointer++; | |
break; | |
case DIFF_EQUAL: | |
// Upon reaching an equality, check for prior redundancies. | |
if (count_delete + count_insert > 1) { | |
if (count_delete !== 0 && count_insert !== 0) { | |
// Factor out any common prefixies. | |
commonlength = diff_commonPrefix(text_insert, text_delete); | |
if (commonlength !== 0) { | |
if ((pointer - count_delete - count_insert) > 0 && | |
diffs[pointer - count_delete - count_insert - 1][0] == | |
DIFF_EQUAL) { | |
diffs[pointer - count_delete - count_insert - 1][1] += | |
text_insert.substring(0, commonlength); | |
} else { | |
diffs.splice(0, 0, [DIFF_EQUAL, | |
text_insert.substring(0, commonlength)]); | |
pointer++; | |
} | |
text_insert = text_insert.substring(commonlength); | |
text_delete = text_delete.substring(commonlength); | |
} | |
// Factor out any common suffixies. | |
commonlength = diff_commonSuffix(text_insert, text_delete); | |
if (commonlength !== 0) { | |
diffs[pointer][1] = text_insert.substring(text_insert.length - | |
commonlength) + diffs[pointer][1]; | |
text_insert = text_insert.substring(0, text_insert.length - | |
commonlength); | |
text_delete = text_delete.substring(0, text_delete.length - | |
commonlength); | |
} | |
} | |
// Delete the offending records and add the merged ones. | |
if (count_delete === 0) { | |
diffs.splice(pointer - count_insert, | |
count_delete + count_insert, [DIFF_INSERT, text_insert]); | |
} else if (count_insert === 0) { | |
diffs.splice(pointer - count_delete, | |
count_delete + count_insert, [DIFF_DELETE, text_delete]); | |
} else { | |
diffs.splice(pointer - count_delete - count_insert, | |
count_delete + count_insert, [DIFF_DELETE, text_delete], | |
[DIFF_INSERT, text_insert]); | |
} | |
pointer = pointer - count_delete - count_insert + | |
(count_delete ? 1 : 0) + (count_insert ? 1 : 0) + 1; | |
} else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) { | |
// Merge this equality with the previous one. | |
diffs[pointer - 1][1] += diffs[pointer][1]; | |
diffs.splice(pointer, 1); | |
} else { | |
pointer++; | |
} | |
count_insert = 0; | |
count_delete = 0; | |
text_delete = ''; | |
text_insert = ''; | |
break; | |
} | |
} | |
if (diffs[diffs.length - 1][1] === '') { | |
diffs.pop(); // Remove the dummy entry at the end. | |
} | |
// Second pass: look for single edits surrounded on both sides by equalities | |
// which can be shifted sideways to eliminate an equality. | |
// e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC | |
var changes = false; | |
pointer = 1; | |
// Intentionally ignore the first and last element (don't need checking). | |
while (pointer < diffs.length - 1) { | |
if (diffs[pointer - 1][0] == DIFF_EQUAL && | |
diffs[pointer + 1][0] == DIFF_EQUAL) { | |
// This is a single edit surrounded by equalities. | |
if (diffs[pointer][1].substring(diffs[pointer][1].length - | |
diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) { | |
// Shift the edit over the previous equality. | |
diffs[pointer][1] = diffs[pointer - 1][1] + | |
diffs[pointer][1].substring(0, diffs[pointer][1].length - | |
diffs[pointer - 1][1].length); | |
diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; | |
diffs.splice(pointer - 1, 1); | |
changes = true; | |
} else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == | |
diffs[pointer + 1][1]) { | |
// Shift the edit over the next equality. | |
diffs[pointer - 1][1] += diffs[pointer + 1][1]; | |
diffs[pointer][1] = | |
diffs[pointer][1].substring(diffs[pointer + 1][1].length) + | |
diffs[pointer + 1][1]; | |
diffs.splice(pointer + 1, 1); | |
changes = true; | |
} | |
} | |
pointer++; | |
} | |
// If shifts were made, the diff needs reordering and another shift sweep. | |
if (changes) { | |
diff_cleanupMerge(diffs); | |
} | |
}; | |
var diff = diff_main; | |
diff.INSERT = DIFF_INSERT; | |
diff.DELETE = DIFF_DELETE; | |
diff.EQUAL = DIFF_EQUAL; | |
module.exports = diff; | |
},{}],7:[function(_dereq_,module,exports){ | |
module.exports={"version":"0.19.14"} | |
},{}],8:[function(_dereq_,module,exports){ | |
var Delta, Document, Format, Line, LinkedList, Normalizer, _, dom; | |
_ = _dereq_('lodash'); | |
Delta = _dereq_('rich-text/lib/delta'); | |
dom = _dereq_('../lib/dom'); | |
Format = _dereq_('./format'); | |
Line = _dereq_('./line'); | |
LinkedList = _dereq_('../lib/linked-list'); | |
Normalizer = _dereq_('./normalizer'); | |
Document = (function() { | |
function Document(root, options) { | |
this.root = root; | |
if (options == null) { | |
options = {}; | |
} | |
this.normalizer = new Normalizer(); | |
this.formats = {}; | |
_.each(options.formats, _.bind(this.addFormat, this)); | |
this.setHTML(this.root.innerHTML); | |
} | |
Document.prototype.addFormat = function(name, config) { | |
if (!_.isObject(config)) { | |
config = Format.FORMATS[name]; | |
} | |
if (this.formats[name] != null) { | |
console.warn('Overwriting format', name, this.formats[name]); | |
} | |
this.formats[name] = new Format(config); | |
return this.normalizer.addFormat(config); | |
}; | |
Document.prototype.appendLine = function(lineNode) { | |
return this.insertLineBefore(lineNode, null); | |
}; | |
Document.prototype.findLeafAt = function(index, inclusive) { | |
var line, offset, ref; | |
ref = this.findLineAt(index), line = ref[0], offset = ref[1]; | |
if (line != null) { | |
return line.findLeafAt(offset, inclusive); | |
} else { | |
return [void 0, offset]; | |
} | |
}; | |
Document.prototype.findLine = function(node) { | |
var line; | |
while ((node != null) && (dom.BLOCK_TAGS[node.tagName] == null)) { | |
node = node.parentNode; | |
} | |
line = node != null ? dom(node).data(Line.DATA_KEY) : void 0; | |
if ((line != null ? line.node : void 0) === node) { | |
return line; | |
} else { | |
return void 0; | |
} | |
}; | |
Document.prototype.findLineAt = function(index) { | |
var curLine, length; | |
if (!(this.lines.length > 0)) { | |
return [void 0, index]; | |
} | |
length = this.toDelta().length(); | |
if (index === length) { | |
return [this.lines.last, this.lines.last.length]; | |
} | |
if (index > length) { | |
return [void 0, index - length]; | |
} | |
curLine = this.lines.first; | |
while (curLine != null) { | |
if (index < curLine.length) { | |
return [curLine, index]; | |
} | |
index -= curLine.length; | |
curLine = curLine.next; | |
} | |
return [void 0, index]; | |
}; | |
Document.prototype.getHTML = function() { | |
return this.root.innerHTML.replace(/\>\s+\</g, '> <'); | |
}; | |
Document.prototype.insertLineBefore = function(newLineNode, refLine) { | |
var line; | |
line = new Line(this, newLineNode); | |
if (refLine != null) { | |
if (!dom(newLineNode.parentNode).isElement()) { | |
this.root.insertBefore(newLineNode, refLine.node); | |
} | |
this.lines.insertAfter(refLine.prev, line); | |
} else { | |
if (!dom(newLineNode.parentNode).isElement()) { | |
this.root.appendChild(newLineNode); | |
} | |
this.lines.append(line); | |
} | |
return line; | |
}; | |
Document.prototype.mergeLines = function(line, lineToMerge) { | |
if (lineToMerge.length > 1) { | |
if (line.length === 1) { | |
dom(line.leaves.last.node).remove(); | |
} | |
_.each(dom(lineToMerge.node).childNodes(), function(child) { | |
if (child.tagName !== dom.DEFAULT_BREAK_TAG) { | |
return line.node.appendChild(child); | |
} | |
}); | |
} | |
this.removeLine(lineToMerge); | |
return line.rebuild(); | |
}; | |
Document.prototype.optimizeLines = function() { | |
return _.each(this.lines.toArray(), function(line, i) { | |
line.optimize(); | |
return true; | |
}); | |
}; | |
Document.prototype.rebuild = function() { | |
var lineNode, lines, results; | |
lines = this.lines.toArray(); | |
lineNode = this.root.firstChild; | |
if ((lineNode != null) && (dom.LIST_TAGS[lineNode.tagName] != null)) { | |
lineNode = lineNode.firstChild; | |
} | |
_.each(lines, (function(_this) { | |
return function(line, index) { | |
var newLine, ref; | |
while (line.node !== lineNode) { | |
if (line.node.parentNode === _this.root || ((ref = line.node.parentNode) != null ? ref.parentNode : void 0) === _this.root) { | |
lineNode = _this.normalizer.normalizeLine(lineNode); | |
newLine = _this.insertLineBefore(lineNode, line); | |
lineNode = dom(lineNode).nextLineNode(_this.root); | |
} else { | |
return _this.removeLine(line); | |
} | |
} | |
if (line.outerHTML !== lineNode.outerHTML) { | |
line.node = _this.normalizer.normalizeLine(line.node); | |
line.rebuild(); | |
} | |
return lineNode = dom(lineNode).nextLineNode(_this.root); | |
}; | |
})(this)); | |
results = []; | |
while (lineNode != null) { | |
lineNode = this.normalizer.normalizeLine(lineNode); | |
this.appendLine(lineNode); | |
results.push(lineNode = dom(lineNode).nextLineNode(this.root)); | |
} | |
return results; | |
}; | |
Document.prototype.removeLine = function(line) { | |
if (line.node.parentNode != null) { | |
if (dom.LIST_TAGS[line.node.parentNode.tagName] && line.node.parentNode.childNodes.length === 1) { | |
dom(line.node.parentNode).remove(); | |
} else { | |
dom(line.node).remove(); | |
} | |
} | |
return this.lines.remove(line); | |
}; | |
Document.prototype.setHTML = function(html) { | |
html = Normalizer.stripComments(html); | |
html = Normalizer.stripWhitespace(html); | |
this.root.innerHTML = html; | |
this.lines = new LinkedList(); | |
return this.rebuild(); | |
}; | |
Document.prototype.splitLine = function(line, offset) { | |
var lineNode1, lineNode2, newLine, ref; | |
offset = Math.min(offset, line.length - 1); | |
ref = dom(line.node).split(offset, true), lineNode1 = ref[0], lineNode2 = ref[1]; | |
line.node = lineNode1; | |
line.rebuild(); | |
newLine = this.insertLineBefore(lineNode2, line.next); | |
newLine.formats = _.clone(line.formats); | |
newLine.resetContent(); | |
return newLine; | |
}; | |
Document.prototype.toDelta = function() { | |
var delta, lines; | |
lines = this.lines.toArray(); | |
delta = new Delta(); | |
_.each(lines, function(line) { | |
return _.each(line.delta.ops, function(op) { | |
return delta.push(op); | |
}); | |
}); | |
return delta; | |
}; | |
return Document; | |
})(); | |
module.exports = Document; | |
},{"../lib/dom":17,"../lib/linked-list":18,"./format":10,"./line":12,"./normalizer":13,"lodash":1,"rich-text/lib/delta":3}],9:[function(_dereq_,module,exports){ | |
var Delta, Document, Editor, Line, Selection, _, dom; | |
_ = _dereq_('lodash'); | |
Delta = _dereq_('rich-text/lib/delta'); | |
dom = _dereq_('../lib/dom'); | |
Document = _dereq_('./document'); | |
Line = _dereq_('./line'); | |
Selection = _dereq_('./selection'); | |
Editor = (function() { | |
Editor.sources = { | |
API: 'api', | |
SILENT: 'silent', | |
USER: 'user' | |
}; | |
function Editor(root, quill, options) { | |
this.root = root; | |
this.quill = quill; | |
this.options = options != null ? options : {}; | |
this.root.setAttribute('id', this.options.id); | |
this.doc = new Document(this.root, this.options); | |
this.delta = this.doc.toDelta(); | |
this.length = this.delta.length(); | |
this.selection = new Selection(this.doc, this.quill); | |
this.timer = setInterval(_.bind(this.checkUpdate, this), this.options.pollInterval); | |
this.savedRange = null; | |
this.quill.on("selection-change", (function(_this) { | |
return function(range) { | |
return _this.savedRange = range; | |
}; | |
})(this)); | |
if (!this.options.readOnly) { | |
this.enable(); | |
} | |
} | |
Editor.prototype.destroy = function() { | |
return clearInterval(this.timer); | |
}; | |
Editor.prototype.disable = function() { | |
return this.enable(false); | |
}; | |
Editor.prototype.enable = function(enabled) { | |
if (enabled == null) { | |
enabled = true; | |
} | |
return this.root.setAttribute('contenteditable', enabled); | |
}; | |
Editor.prototype.applyDelta = function(delta, source) { | |
var localDelta; | |
localDelta = this._update(); | |
if (localDelta) { | |
delta = localDelta.transform(delta, true); | |
localDelta = delta.transform(localDelta, false); | |
} | |
if (delta.ops.length > 0) { | |
delta = this._trackDelta((function(_this) { | |
return function() { | |
var index; | |
index = 0; | |
_.each(delta.ops, function(op) { | |
if (_.isString(op.insert)) { | |
_this._insertAt(index, op.insert, op.attributes); | |
return index += op.insert.length; | |
} else if (_.isNumber(op.insert)) { | |
_this._insertEmbed(index, op.attributes); | |
return index += 1; | |
} else if (_.isNumber(op["delete"])) { | |
return _this._deleteAt(index, op["delete"]); | |
} else if (_.isNumber(op.retain)) { | |
_.each(op.attributes, function(value, name) { | |
return _this._formatAt(index, op.retain, name, value); | |
}); | |
return index += op.retain; | |
} | |
}); | |
return _this.selection.shiftAfter(0, 0, _.bind(_this.doc.optimizeLines, _this.doc)); | |
}; | |
})(this)); | |
this.delta = this.doc.toDelta(); | |
this.length = this.delta.length(); | |
this.innerHTML = this.root.innerHTML; | |
if (delta && source !== Editor.sources.SILENT) { | |
this.quill.emit(this.quill.constructor.events.TEXT_CHANGE, delta, source); | |
} | |
} | |
if (localDelta && localDelta.ops.length > 0 && source !== Editor.sources.SILENT) { | |
return this.quill.emit(this.quill.constructor.events.TEXT_CHANGE, localDelta, Editor.sources.USER); | |
} | |
}; | |
Editor.prototype.checkUpdate = function(source) { | |
var delta; | |
if (source == null) { | |
source = 'user'; | |
} | |
if (this.root.parentNode == null) { | |
return clearInterval(this.timer); | |
} | |
delta = this._update(); | |
if (delta) { | |
this.delta = this.delta.compose(delta); | |
this.length = this.delta.length(); | |
this.quill.emit(this.quill.constructor.events.TEXT_CHANGE, delta, source); | |
} | |
if (delta) { | |
source = Editor.sources.SILENT; | |
} | |
return this.selection.update(source); | |
}; | |
Editor.prototype.focus = function() { | |
if (this.selection.range != null) { | |
return this.selection.setRange(this.selection.range); | |
} else { | |
return this.root.focus(); | |
} | |
}; | |
Editor.prototype.getBounds = function(index) { | |
var bounds, containerBounds, leaf, offset, range, ref, side; | |
this.checkUpdate(); | |
ref = this.doc.findLeafAt(index, true), leaf = ref[0], offset = ref[1]; | |
if (leaf == null) { | |
throw new Error('Invalid index'); | |
} | |
containerBounds = this.root.parentNode.getBoundingClientRect(); | |
side = 'left'; | |
if (leaf.length === 0) { | |
bounds = leaf.node.parentNode.getBoundingClientRect(); | |
} else if (dom.VOID_TAGS[leaf.node.tagName]) { | |
bounds = leaf.node.getBoundingClientRect(); | |
if (offset === 1) { | |
side = 'right'; | |
} | |
} else { | |
range = document.createRange(); | |
if (offset < leaf.length) { | |
range.setStart(leaf.node, offset); | |
range.setEnd(leaf.node, offset + 1); | |
} else { | |
range.setStart(leaf.node, offset - 1); | |
range.setEnd(leaf.node, offset); | |
side = 'right'; | |
} | |
bounds = range.getBoundingClientRect(); | |
} | |
return { | |
height: bounds.height, | |
left: bounds[side] - containerBounds.left, | |
top: bounds.top - containerBounds.top | |
}; | |
}; | |
Editor.prototype._deleteAt = function(index, length) { | |
if (length <= 0) { | |
return; | |
} | |
return this.selection.shiftAfter(index, -1 * length, (function(_this) { | |
return function() { | |
var curLine, deleteLength, firstLine, mergeFirstLine, nextLine, offset, ref; | |
ref = _this.doc.findLineAt(index), firstLine = ref[0], offset = ref[1]; | |
curLine = firstLine; | |
mergeFirstLine = firstLine.length - offset <= length && offset > 0; | |
while ((curLine != null) && length > 0) { | |
nextLine = curLine.next; | |
deleteLength = Math.min(curLine.length - offset, length); | |
if (offset === 0 && length >= curLine.length) { | |
_this.doc.removeLine(curLine); | |
} else { | |
curLine.deleteText(offset, deleteLength); | |
} | |
length -= deleteLength; | |
curLine = nextLine; | |
offset = 0; | |
} | |
if (mergeFirstLine && firstLine.next) { | |
return _this.doc.mergeLines(firstLine, firstLine.next); | |
} | |
}; | |
})(this)); | |
}; | |
Editor.prototype._formatAt = function(index, length, name, value) { | |
return this.selection.shiftAfter(index, 0, (function(_this) { | |
return function() { | |
var formatLength, line, offset, ref, results; | |
ref = _this.doc.findLineAt(index), line = ref[0], offset = ref[1]; | |
results = []; | |
while ((line != null) && length > 0) { | |
formatLength = Math.min(length, line.length - offset - 1); | |
if (name == 'link') { | |
line.formatText(offset, formatLength, name, value, _this.options.modules['link-tooltip'].target); | |
} else { | |
line.formatText(offset, formatLength, name, value); | |
} | |
length -= formatLength; | |
if (length > 0) { | |
line.format(name, value); | |
} | |
length -= 1; | |
offset = 0; | |
results.push(line = line.next); | |
} | |
return results; | |
}; | |
})(this)); | |
}; | |
Editor.prototype._insertEmbed = function(index, attributes) { | |
return this.selection.shiftAfter(index, 1, (function(_this) { | |
return function() { | |
var line, offset, ref; | |
ref = _this.doc.findLineAt(index), line = ref[0], offset = ref[1]; | |
return line.insertEmbed(offset, attributes); | |
}; | |
})(this)); | |
}; | |
Editor.prototype._insertAt = function(index, text, formatting) { | |
if (formatting == null) { | |
formatting = {}; | |
} | |
return this.selection.shiftAfter(index, text.length, (function(_this) { | |
return function() { | |
var line, lineTexts, offset, ref; | |
text = text.replace(/\r\n?/g, '\n'); | |
lineTexts = text.split('\n'); | |
ref = _this.doc.findLineAt(index), line = ref[0], offset = ref[1]; | |
return _.each(lineTexts, function(lineText, i) { | |
var nextLine; | |
if ((line == null) || line.length <= offset) { | |
if (i < lineTexts.length - 1 || lineText.length > 0) { | |
line = _this.doc.appendLine(document.createElement(dom.DEFAULT_BLOCK_TAG)); | |
offset = 0; | |
line.insertText(offset, lineText, formatting); | |
line.format(formatting); | |
nextLine = null; | |
} | |
} else { | |
line.insertText(offset, lineText, formatting); | |
if (i < lineTexts.length - 1) { | |
nextLine = _this.doc.splitLine(line, offset + lineText.length); | |
_.each(_.defaults({}, formatting, line.formats), function(value, format) { | |
return line.format(format, formatting[format]); | |
}); | |
offset = 0; | |
} | |
} | |
return line = nextLine; | |
}); | |
}; | |
})(this)); | |
}; | |
Editor.prototype._trackDelta = function(fn) { | |
var ignored, newDelta, newIndex, newLeftDelta, newRightDelta, oldIndex, oldLeftDelta, oldRightDelta, ref, ref1; | |
oldIndex = (ref = this.savedRange) != null ? ref.start : void 0; | |
fn(); | |
newDelta = this.doc.toDelta(); | |
this.savedRange = this.selection.getRange(); | |
newIndex = (ref1 = this.savedRange) != null ? ref1.start : void 0; | |
try { | |
if ((oldIndex != null) && (newIndex != null) && oldIndex <= this.delta.length() && newIndex <= newDelta.length()) { | |
oldRightDelta = this.delta.slice(oldIndex); | |
newRightDelta = newDelta.slice(newIndex); | |
if (_.isEqual(oldRightDelta.ops, newRightDelta.ops)) { | |
oldLeftDelta = this.delta.slice(0, oldIndex); | |
newLeftDelta = newDelta.slice(0, newIndex); | |
return oldLeftDelta.diff(newLeftDelta); | |
} | |
} | |
} catch (_error) { | |
ignored = _error; | |
} | |
return this.delta.diff(newDelta); | |
}; | |
Editor.prototype._update = function() { | |
var delta; | |
if (this.innerHTML === this.root.innerHTML) { | |
return false; | |
} | |
delta = this._trackDelta((function(_this) { | |
return function() { | |
_this.selection.preserve(_.bind(_this.doc.rebuild, _this.doc)); | |
return _this.selection.shiftAfter(0, 0, _.bind(_this.doc.optimizeLines, _this.doc)); | |
}; | |
})(this)); | |
this.innerHTML = this.root.innerHTML; | |
if (delta.ops.length > 0) { | |
return delta; | |
} else { | |
return false; | |
} | |
}; | |
return Editor; | |
})(); | |
module.exports = Editor; | |
},{"../lib/dom":17,"./document":8,"./line":12,"./selection":14,"lodash":1,"rich-text/lib/delta":3}],10:[function(_dereq_,module,exports){ | |
var Format, _, dom; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('../lib/dom'); | |
Format = (function() { | |
Format.types = { | |
LINE: 'line', | |
EMBED: 'embed' | |
}; | |
Format.FORMATS = { | |
bold: { | |
tag: 'B', | |
prepare: 'bold' | |
}, | |
italic: { | |
tag: 'I', | |
prepare: 'italic' | |
}, | |
underline: { | |
tag: 'U', | |
prepare: 'underline' | |
}, | |
strike: { | |
tag: 'S', | |
prepare: 'strikeThrough' | |
}, | |
color: { | |
style: 'color', | |
"default": 'rgb(0, 0, 0)', | |
prepare: 'foreColor' | |
}, | |
background: { | |
style: 'backgroundColor', | |
"default": 'rgb(255, 255, 255)', | |
prepare: 'backColor' | |
}, | |
font: { | |
style: 'fontFamily', | |
"default": "'Helvetica', 'Arial', sans-serif", | |
prepare: 'fontName' | |
}, | |
size: { | |
style: 'fontSize', | |
"default": '13px', | |
prepare: function(value) { | |
return document.execCommand('fontSize', false, dom.convertFontSize(value)); | |
} | |
}, | |
link: { | |
tag: 'A', | |
add: function(node, value, target) { | |
node.setAttribute('href', value); | |
node.setAttribute('target', target); | |
return node; | |
}, | |
remove: function(node) { | |
node.removeAttribute('href'); | |
return node; | |
}, | |
value: function(node) { | |
return node.getAttribute('href'); | |
} | |
}, | |
image: { | |
type: Format.types.EMBED, | |
tag: 'IMG', | |
attribute: 'src' | |
}, | |
align: { | |
type: Format.types.LINE, | |
style: 'textAlign', | |
"default": 'left' | |
}, | |
bullet: { | |
type: Format.types.LINE, | |
exclude: 'list', | |
parentTag: 'UL', | |
tag: 'LI' | |
}, | |
list: { | |
type: Format.types.LINE, | |
exclude: 'bullet', | |
parentTag: 'OL', | |
tag: 'LI' | |
} | |
}; | |
function Format(config) { | |
this.config = config; | |
} | |
Format.prototype.add = function(node, value, target) { | |
var formatNode, inline, parentNode, ref, ref1; | |
if (!value) { | |
return this.remove(node); | |
} | |
if (this.value(node) === value) { | |
return node; | |
} | |
if (_.isString(this.config.parentTag)) { | |
parentNode = document.createElement(this.config.parentTag); | |
dom(node).wrap(parentNode); | |
if (node.parentNode.tagName === ((ref = node.parentNode.previousSibling) != null ? ref.tagName : void 0)) { | |
dom(node.parentNode.previousSibling).merge(node.parentNode); | |
} | |
if (node.parentNode.tagName === ((ref1 = node.parentNode.nextSibling) != null ? ref1.tagName : void 0)) { | |
dom(node.parentNode).merge(node.parentNode.nextSibling); | |
} | |
} | |
if (_.isString(this.config.tag)) { | |
formatNode = document.createElement(this.config.tag); | |
if (dom.VOID_TAGS[formatNode.tagName] != null) { | |
if (node.parentNode != null) { | |
dom(node).replace(formatNode); | |
} | |
node = formatNode; | |
} else if (this.isType(Format.types.LINE)) { | |
node = dom(node).switchTag(this.config.tag); | |
} else { | |
dom(node).wrap(formatNode); | |
node = formatNode; | |
} | |
} | |
if (_.isString(this.config.style) || _.isString(this.config.attribute) || _.isString(this.config["class"])) { | |
if (_.isString(this.config["class"])) { | |
node = this.remove(node); | |
} | |
if (dom(node).isTextNode()) { | |
inline = document.createElement(dom.DEFAULT_INLINE_TAG); | |
dom(node).wrap(inline); | |
node = inline; | |
} | |
if (_.isString(this.config.style)) { | |
if (value !== this.config["default"]) { | |
node.style[this.config.style] = value; | |
} | |
} | |
if (_.isString(this.config.attribute)) { | |
node.setAttribute(this.config.attribute, value); | |
} | |
if (_.isString(this.config["class"])) { | |
dom(node).addClass(this.config["class"] + value); | |
} | |
} | |
if (_.isFunction(this.config.add)) { | |
node = this.config.add(node, value, target); | |
} | |
return node; | |
}; | |
Format.prototype.isType = function(type) { | |
return type === this.config.type; | |
}; | |
Format.prototype.match = function(node) { | |
var c, i, len, ref, ref1; | |
if (!dom(node).isElement()) { | |
return false; | |
} | |
if (_.isString(this.config.parentTag) && ((ref = node.parentNode) != null ? ref.tagName : void 0) !== this.config.parentTag) { | |
return false; | |
} | |
if (_.isString(this.config.tag) && node.tagName !== this.config.tag) { | |
return false; | |
} | |
if (_.isString(this.config.style) && (!node.style[this.config.style] || node.style[this.config.style] === this.config["default"])) { | |
return false; | |
} | |
if (_.isString(this.config.attribute) && !node.hasAttribute(this.config.attribute)) { | |
return false; | |
} | |
if (_.isString(this.config["class"])) { | |
ref1 = dom(node).classes(); | |
for (i = 0, len = ref1.length; i < len; i++) { | |
c = ref1[i]; | |
if (c.indexOf(this.config["class"]) === 0) { | |
return true; | |
} | |
} | |
return false; | |
} | |
return true; | |
}; | |
Format.prototype.prepare = function(value) { | |
if (_.isString(this.config.prepare)) { | |
return document.execCommand(this.config.prepare, false, value); | |
} else if (_.isFunction(this.config.prepare)) { | |
return this.config.prepare(value); | |
} | |
}; | |
Format.prototype.remove = function(node) { | |
var c, i, len, ref; | |
if (!this.match(node)) { | |
return node; | |
} | |
if (_.isString(this.config.style)) { | |
node.style[this.config.style] = ''; | |
if (!node.getAttribute('style')) { | |
node.removeAttribute('style'); | |
} | |
} | |
if (_.isString(this.config.attribute)) { | |
node.removeAttribute(this.config.attribute); | |
} | |
if (_.isString(this.config["class"])) { | |
ref = dom(node).classes(); | |
for (i = 0, len = ref.length; i < len; i++) { | |
c = ref[i]; | |
if (c.indexOf(this.config["class"]) === 0) { | |
dom(node).removeClass(c); | |
} | |
} | |
} | |
if (_.isString(this.config.tag)) { | |
if (this.isType(Format.types.LINE)) { | |
if (_.isString(this.config.parentTag)) { | |
if (node.previousSibling != null) { | |
dom(node).splitBefore(node.parentNode.parentNode); | |
} | |
if (node.nextSibling != null) { | |
dom(node.nextSibling).splitBefore(node.parentNode.parentNode); | |
} | |
} | |
node = dom(node).switchTag(dom.DEFAULT_BLOCK_TAG); | |
} else if (this.isType(Format.types.EMBED)) { | |
dom(node).remove(); | |
return void 0; | |
} else { | |
node = dom(node).switchTag(dom.DEFAULT_INLINE_TAG); | |
} | |
} | |
if (_.isString(this.config.parentTag)) { | |
dom(node.parentNode).unwrap(); | |
} | |
if (_.isFunction(this.config.remove)) { | |
node = this.config.remove(node); | |
} | |
if (node.tagName === dom.DEFAULT_INLINE_TAG && !node.hasAttributes()) { | |
node = dom(node).unwrap(); | |
} | |
return node; | |
}; | |
Format.prototype.value = function(node) { | |
var c, i, len, ref; | |
if (!this.match(node)) { | |
return void 0; | |
} | |
if (this.config.value) { | |
return this.config.value(node); | |
} | |
if (_.isString(this.config.attribute)) { | |
return node.getAttribute(this.config.attribute) || void 0; | |
} else if (_.isString(this.config.style)) { | |
return node.style[this.config.style] || void 0; | |
} else if (_.isString(this.config["class"])) { | |
ref = dom(node).classes(); | |
for (i = 0, len = ref.length; i < len; i++) { | |
c = ref[i]; | |
if (c.indexOf(this.config["class"]) === 0) { | |
return c.slice(this.config["class"].length); | |
} | |
} | |
} else if (_.isString(this.config.tag)) { | |
return true; | |
} | |
return void 0; | |
}; | |
return Format; | |
})(); | |
module.exports = Format; | |
},{"../lib/dom":17,"lodash":1}],11:[function(_dereq_,module,exports){ | |
var Format, Leaf, LinkedList, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('../lib/dom'); | |
Format = _dereq_('./format'); | |
LinkedList = _dereq_('../lib/linked-list'); | |
Leaf = (function(superClass) { | |
extend(Leaf, superClass); | |
Leaf.DATA_KEY = 'leaf'; | |
Leaf.isLeafNode = function(node) { | |
return dom(node).isTextNode() || (node.firstChild == null); | |
}; | |
function Leaf(node1, formats) { | |
this.node = node1; | |
this.formats = _.clone(formats); | |
this.text = dom(this.node).text(); | |
this.length = this.text.length; | |
dom(this.node).data(Leaf.DATA_KEY, this); | |
} | |
Leaf.prototype.deleteText = function(offset, length) { | |
var textNode; | |
if (!(length > 0)) { | |
return; | |
} | |
this.text = this.text.slice(0, offset) + this.text.slice(offset + length); | |
this.length = this.text.length; | |
if (dom.EMBED_TAGS[this.node.tagName] != null) { | |
textNode = document.createTextNode(this.text); | |
dom(textNode).data(Leaf.DATA_KEY, this); | |
return this.node = dom(this.node).replace(textNode); | |
} else { | |
return dom(this.node).text(this.text); | |
} | |
}; | |
Leaf.prototype.insertText = function(offset, text) { | |
var textNode; | |
this.text = this.text.slice(0, offset) + text + this.text.slice(offset); | |
if (dom(this.node).isTextNode()) { | |
dom(this.node).text(this.text); | |
} else { | |
textNode = document.createTextNode(text); | |
dom(textNode).data(Leaf.DATA_KEY, this); | |
if (this.node.tagName === dom.DEFAULT_BREAK_TAG) { | |
this.node = dom(this.node).replace(textNode); | |
} else { | |
this.node.appendChild(textNode); | |
this.node = textNode; | |
} | |
} | |
return this.length = this.text.length; | |
}; | |
return Leaf; | |
})(LinkedList.Node); | |
module.exports = Leaf; | |
},{"../lib/dom":17,"../lib/linked-list":18,"./format":10,"lodash":1}],12:[function(_dereq_,module,exports){ | |
var Delta, Format, Leaf, Line, LinkedList, Normalizer, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
_ = _dereq_('lodash'); | |
Delta = _dereq_('rich-text/lib/delta'); | |
dom = _dereq_('../lib/dom'); | |
Format = _dereq_('./format'); | |
Leaf = _dereq_('./leaf'); | |
Line = _dereq_('./line'); | |
LinkedList = _dereq_('../lib/linked-list'); | |
Normalizer = _dereq_('./normalizer'); | |
Line = (function(superClass) { | |
extend(Line, superClass); | |
Line.DATA_KEY = 'line'; | |
function Line(doc, node1) { | |
this.doc = doc; | |
this.node = node1; | |
this.formats = {}; | |
this.rebuild(); | |
Line.__super__.constructor.call(this, this.node); | |
} | |
Line.prototype.buildLeaves = function(node, formats) { | |
return _.each(dom(node).childNodes(), (function(_this) { | |
return function(node) { | |
var nodeFormats; | |
node = _this.doc.normalizer.normalizeNode(node); | |
nodeFormats = _.clone(formats); | |
_.each(_this.doc.formats, function(format, name) { | |
if (!format.isType(Format.types.LINE) && format.match(node)) { | |
return nodeFormats[name] = format.value(node); | |
} | |
}); | |
if (Leaf.isLeafNode(node)) { | |
return _this.leaves.append(new Leaf(node, nodeFormats)); | |
} else { | |
return _this.buildLeaves(node, nodeFormats); | |
} | |
}; | |
})(this)); | |
}; | |
Line.prototype.deleteText = function(offset, length) { | |
var deleteLength, leaf, ref; | |
if (!(length > 0)) { | |
return; | |
} | |
ref = this.findLeafAt(offset), leaf = ref[0], offset = ref[1]; | |
while ((leaf != null) && length > 0) { | |
deleteLength = Math.min(length, leaf.length - offset); | |
leaf.deleteText(offset, deleteLength); | |
length -= deleteLength; | |
leaf = leaf.next; | |
offset = 0; | |
} | |
return this.rebuild(); | |
}; | |
Line.prototype.findLeaf = function(leafNode) { | |
if (leafNode != null) { | |
return dom(leafNode).data(Leaf.DATA_KEY); | |
} else { | |
return void 0; | |
} | |
}; | |
Line.prototype.findLeafAt = function(offset, inclusive) { | |
var leaf; | |
if (inclusive == null) { | |
inclusive = false; | |
} | |
if (offset >= this.length - 1) { | |
return [this.leaves.last, this.leaves.last.length]; | |
} | |
leaf = this.leaves.first; | |
while (leaf != null) { | |
if (offset < leaf.length || (offset === leaf.length && inclusive)) { | |
return [leaf, offset]; | |
} | |
offset -= leaf.length; | |
leaf = leaf.next; | |
} | |
return [this.leaves.last, offset - this.leaves.last.length]; | |
}; | |
Line.prototype.format = function(name, value) { | |
var formats; | |
if (_.isObject(name)) { | |
formats = name; | |
} else { | |
formats = {}; | |
formats[name] = value; | |
} | |
_.each(formats, (function(_this) { | |
return function(value, name) { | |
var excludeFormat, format; | |
format = _this.doc.formats[name]; | |
if (format == null) { | |
return; | |
} | |
if (format.isType(Format.types.LINE)) { | |
if (format.config.exclude && _this.formats[format.config.exclude]) { | |
excludeFormat = _this.doc.formats[format.config.exclude]; | |
if (excludeFormat != null) { | |
_this.node = excludeFormat.remove(_this.node); | |
delete _this.formats[format.config.exclude]; | |
} | |
} | |
_this.node = format.add(_this.node, value); | |
} | |
if (value) { | |
return _this.formats[name] = value; | |
} else { | |
return delete _this.formats[name]; | |
} | |
}; | |
})(this)); | |
return this.resetContent(); | |
}; | |
Line.prototype.formatText = function(offset, length, name, value, target) { | |
var format, leaf, leafOffset, leftNode, nextLeaf, ref, ref1, ref2, rightNode, targetNode; | |
ref = this.findLeafAt(offset), leaf = ref[0], leafOffset = ref[1]; | |
format = this.doc.formats[name]; | |
if (!((format != null) && format.config.type !== Format.types.LINE)) { | |
return; | |
} | |
while ((leaf != null) && length > 0) { | |
nextLeaf = leaf.next; | |
if ((value && leaf.formats[name] !== value) || (!value && (leaf.formats[name] != null))) { | |
targetNode = leaf.node; | |
if (leaf.formats[name] != null) { | |
dom(targetNode).splitBefore(this.node); | |
while (!format.match(targetNode)) { | |
targetNode = targetNode.parentNode; | |
} | |
dom(targetNode).split(leaf.length); | |
} | |
if (leafOffset > 0) { | |
ref1 = dom(targetNode).split(leafOffset), leftNode = ref1[0], targetNode = ref1[1]; | |
} | |
if (leaf.length > leafOffset + length) { | |
ref2 = dom(targetNode).split(length), targetNode = ref2[0], rightNode = ref2[1]; | |
} | |
format.add(targetNode, value, target); | |
} | |
length -= leaf.length - leafOffset; | |
leafOffset = 0; | |
leaf = nextLeaf; | |
} | |
return this.rebuild(); | |
}; | |
Line.prototype._insert = function(offset, node, formats) { | |
var leaf, leafOffset, nextNode, prevNode, ref, ref1; | |
ref = this.findLeafAt(offset), leaf = ref[0], leafOffset = ref[1]; | |
node = _.reduce(formats, (function(_this) { | |
return function(node, value, name) { | |
var format; | |
format = _this.doc.formats[name]; | |
if (format != null) { | |
node = format.add(node, value); | |
} | |
return node; | |
}; | |
})(this), node); | |
ref1 = dom(leaf.node).split(leafOffset), prevNode = ref1[0], nextNode = ref1[1]; | |
if (nextNode) { | |
nextNode = dom(nextNode).splitBefore(this.node).get(); | |
} | |
this.node.insertBefore(node, nextNode); | |
return this.rebuild(); | |
}; | |
Line.prototype.insertEmbed = function(offset, attributes) { | |
var formatName, leaf, leafOffset, nextNode, node, prevNode, ref, ref1; | |
ref = this.findLeafAt(offset), leaf = ref[0], leafOffset = ref[1]; | |
ref1 = dom(leaf.node).split(leafOffset), prevNode = ref1[0], nextNode = ref1[1]; | |
formatName = _.find(Object.keys(attributes), (function(_this) { | |
return function(name) { | |
return _this.doc.formats[name].isType(Format.types.EMBED); | |
}; | |
})(this)); | |
node = this.doc.formats[formatName].add({}, attributes[formatName]); | |
attributes = _.clone(attributes); | |
delete attributes[formatName]; | |
return this._insert(offset, node, attributes); | |
}; | |
Line.prototype.insertText = function(offset, text, formats) { | |
var leaf, leafOffset, ref; | |
if (formats == null) { | |
formats = {}; | |
} | |
if (!(text.length > 0)) { | |
return; | |
} | |
ref = this.findLeafAt(offset), leaf = ref[0], leafOffset = ref[1]; | |
if (_.isEqual(leaf.formats, formats)) { | |
leaf.insertText(leafOffset, text); | |
return this.resetContent(); | |
} else { | |
return this._insert(offset, document.createTextNode(text), formats); | |
} | |
}; | |
Line.prototype.optimize = function() { | |
Normalizer.optimizeLine(this.node); | |
return this.rebuild(); | |
}; | |
Line.prototype.rebuild = function(force) { | |
if (force == null) { | |
force = false; | |
} | |
if (!force && (this.outerHTML != null) && this.outerHTML === this.node.outerHTML) { | |
if (_.all(this.leaves.toArray(), (function(_this) { | |
return function(leaf) { | |
return dom(leaf.node).isAncestor(_this.node); | |
}; | |
})(this))) { | |
return false; | |
} | |
} | |
this.node = this.doc.normalizer.normalizeNode(this.node); | |
if (dom(this.node).length() === 0 && !this.node.querySelector(dom.DEFAULT_BREAK_TAG)) { | |
this.node.appendChild(document.createElement(dom.DEFAULT_BREAK_TAG)); | |
} | |
this.leaves = new LinkedList(); | |
this.formats = _.reduce(this.doc.formats, (function(_this) { | |
return function(formats, format, name) { | |
if (format.isType(Format.types.LINE)) { | |
if (format.match(_this.node)) { | |
formats[name] = format.value(_this.node); | |
} else { | |
delete formats[name]; | |
} | |
} | |
return formats; | |
}; | |
})(this), this.formats); | |
this.buildLeaves(this.node, {}); | |
this.resetContent(); | |
return true; | |
}; | |
Line.prototype.resetContent = function() { | |
dom(this.node).data(Line.DATA_KEY, this); | |
this.outerHTML = this.node.outerHTML; | |
this.length = 1; | |
this.delta = new Delta(); | |
_.each(this.leaves.toArray(), (function(_this) { | |
return function(leaf) { | |
_this.length += leaf.length; | |
if (dom.EMBED_TAGS[leaf.node.tagName] != null) { | |
return _this.delta.insert(1, leaf.formats); | |
} else { | |
return _this.delta.insert(leaf.text, leaf.formats); | |
} | |
}; | |
})(this)); | |
return this.delta.insert('\n', this.formats); | |
}; | |
return Line; | |
})(LinkedList.Node); | |
module.exports = Line; | |
},{"../lib/dom":17,"../lib/linked-list":18,"./format":10,"./leaf":11,"./line":12,"./normalizer":13,"lodash":1,"rich-text/lib/delta":3}],13:[function(_dereq_,module,exports){ | |
var Normalizer, _, camelize, dom; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('../lib/dom'); | |
camelize = function(str) { | |
str = str.replace(/(?:^|[-_])(\w)/g, function(i, c) { | |
if (c) { | |
return c.toUpperCase(); | |
} else { | |
return ''; | |
} | |
}); | |
return str.charAt(0).toLowerCase() + str.slice(1); | |
}; | |
Normalizer = (function() { | |
Normalizer.ALIASES = { | |
'STRONG': 'B', | |
'EM': 'I', | |
'DEL': 'S', | |
'STRIKE': 'S' | |
}; | |
Normalizer.ATTRIBUTES = { | |
'color': 'color', | |
'face': 'fontFamily', | |
'size': 'fontSize' | |
}; | |
function Normalizer() { | |
this.whitelist = { | |
styles: {}, | |
tags: {} | |
}; | |
this.whitelist.tags[dom.DEFAULT_BREAK_TAG] = true; | |
this.whitelist.tags[dom.DEFAULT_BLOCK_TAG] = true; | |
this.whitelist.tags[dom.DEFAULT_INLINE_TAG] = true; | |
} | |
Normalizer.prototype.addFormat = function(config) { | |
if (config.tag != null) { | |
this.whitelist.tags[config.tag] = true; | |
} | |
if (config.parentTag != null) { | |
this.whitelist.tags[config.parentTag] = true; | |
} | |
if (config.style != null) { | |
return this.whitelist.styles[config.style] = true; | |
} | |
}; | |
Normalizer.prototype.normalizeLine = function(lineNode) { | |
lineNode = Normalizer.wrapInline(lineNode); | |
lineNode = Normalizer.handleBreaks(lineNode); | |
lineNode = Normalizer.pullBlocks(lineNode); | |
lineNode = this.normalizeNode(lineNode); | |
Normalizer.unwrapText(lineNode); | |
if ((lineNode != null) && (dom.LIST_TAGS[lineNode.tagName] != null)) { | |
lineNode = lineNode.firstChild; | |
} | |
return lineNode; | |
}; | |
Normalizer.prototype.normalizeNode = function(node) { | |
if (dom(node).isTextNode()) { | |
return node; | |
} | |
_.each(Normalizer.ATTRIBUTES, function(style, attribute) { | |
var value; | |
if (node.hasAttribute(attribute)) { | |
value = node.getAttribute(attribute); | |
if (attribute === 'size') { | |
value = dom.convertFontSize(value); | |
} | |
node.style[style] = value; | |
return node.removeAttribute(attribute); | |
} | |
}); | |
this.whitelistStyles(node); | |
return this.whitelistTags(node); | |
}; | |
Normalizer.prototype.whitelistStyles = function(node) { | |
var original, styles; | |
original = dom(node).styles(); | |
styles = _.omit(original, (function(_this) { | |
return function(value, key) { | |
return _this.whitelist.styles[camelize(key)] == null; | |
}; | |
})(this)); | |
if (Object.keys(styles).length < Object.keys(original).length) { | |
if (Object.keys(styles).length > 0) { | |
return dom(node).styles(styles, true); | |
} else { | |
return node.removeAttribute('style'); | |
} | |
} | |
}; | |
Normalizer.prototype.whitelistTags = function(node) { | |
if (!dom(node).isElement()) { | |
return node; | |
} | |
if (Normalizer.ALIASES[node.tagName] != null) { | |
node = dom(node).switchTag(Normalizer.ALIASES[node.tagName]); | |
} else if (this.whitelist.tags[node.tagName] == null) { | |
if (dom.BLOCK_TAGS[node.tagName] != null) { | |
node = dom(node).switchTag(dom.DEFAULT_BLOCK_TAG); | |
} else if (!node.hasAttributes() && (node.firstChild != null)) { | |
node = dom(node).unwrap(); | |
} else { | |
node = dom(node).switchTag(dom.DEFAULT_INLINE_TAG); | |
} | |
} | |
return node; | |
}; | |
Normalizer.handleBreaks = function(lineNode) { | |
var breaks; | |
breaks = _.map(lineNode.querySelectorAll(dom.DEFAULT_BREAK_TAG)); | |
_.each(breaks, (function(_this) { | |
return function(br) { | |
if ((br.nextSibling != null) && (!dom.isIE(10) || (br.previousSibling != null))) { | |
return dom(br.nextSibling).splitBefore(lineNode.parentNode); | |
} | |
}; | |
})(this)); | |
return lineNode; | |
}; | |
Normalizer.optimizeLine = function(lineNode) { | |
var lineNodeLength, node, nodes, results; | |
lineNode.normalize(); | |
lineNodeLength = dom(lineNode).length(); | |
nodes = dom(lineNode).descendants(); | |
results = []; | |
while (nodes.length > 0) { | |
node = nodes.pop(); | |
if ((node != null ? node.parentNode : void 0) == null) { | |
continue; | |
} | |
if (dom.EMBED_TAGS[node.tagName] != null) { | |
continue; | |
} | |
if (node.tagName === dom.DEFAULT_BREAK_TAG) { | |
if (lineNodeLength !== 0) { | |
results.push(dom(node).remove()); | |
} else { | |
results.push(void 0); | |
} | |
} else if (dom(node).length() === 0) { | |
nodes.push(node.nextSibling); | |
results.push(dom(node).unwrap()); | |
} else if ((node.previousSibling != null) && node.tagName === node.previousSibling.tagName) { | |
if (_.isEqual(dom(node).attributes(), dom(node.previousSibling).attributes())) { | |
nodes.push(node.firstChild); | |
results.push(dom(node.previousSibling).merge(node)); | |
} else { | |
results.push(void 0); | |
} | |
} else { | |
results.push(void 0); | |
} | |
} | |
return results; | |
}; | |
Normalizer.pullBlocks = function(lineNode) { | |
var curNode; | |
curNode = lineNode.firstChild; | |
while (curNode != null) { | |
if ((dom.BLOCK_TAGS[curNode.tagName] != null) && curNode.tagName !== 'LI') { | |
dom(curNode).isolate(lineNode.parentNode); | |
if ((dom.LIST_TAGS[curNode.tagName] == null) || !curNode.firstChild) { | |
dom(curNode).unwrap(); | |
Normalizer.pullBlocks(lineNode); | |
} else { | |
dom(curNode.parentNode).unwrap(); | |
if (lineNode.parentNode == null) { | |
lineNode = curNode; | |
} | |
} | |
break; | |
} | |
curNode = curNode.nextSibling; | |
} | |
return lineNode; | |
}; | |
Normalizer.stripComments = function(html) { | |
return html.replace(/<!--[\s\S]*?-->/g, ''); | |
}; | |
Normalizer.stripWhitespace = function(html) { | |
html = html.trim(); | |
html = html.replace(/(\r?\n|\r)+/g, ' '); | |
html = html.replace(/\>\s+\</g, '><'); | |
return html; | |
}; | |
Normalizer.wrapInline = function(lineNode) { | |
var blockNode, nextNode; | |
if (dom.BLOCK_TAGS[lineNode.tagName] != null) { | |
return lineNode; | |
} | |
blockNode = document.createElement(dom.DEFAULT_BLOCK_TAG); | |
lineNode.parentNode.insertBefore(blockNode, lineNode); | |
while ((lineNode != null) && (dom.BLOCK_TAGS[lineNode.tagName] == null)) { | |
nextNode = lineNode.nextSibling; | |
blockNode.appendChild(lineNode); | |
lineNode = nextNode; | |
} | |
return blockNode; | |
}; | |
Normalizer.unwrapText = function(lineNode) { | |
var spans; | |
spans = _.map(lineNode.querySelectorAll(dom.DEFAULT_INLINE_TAG)); | |
return _.each(spans, function(span) { | |
if (!span.hasAttributes()) { | |
return dom(span).unwrap(); | |
} | |
}); | |
}; | |
return Normalizer; | |
})(); | |
module.exports = Normalizer; | |
},{"../lib/dom":17,"lodash":1}],14:[function(_dereq_,module,exports){ | |
var Leaf, Normalizer, Range, Selection, _, dom; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('../lib/dom'); | |
Leaf = _dereq_('./leaf'); | |
Normalizer = _dereq_('./normalizer'); | |
Range = _dereq_('../lib/range'); | |
Selection = (function() { | |
function Selection(doc, emitter) { | |
this.doc = doc; | |
this.emitter = emitter; | |
this.focus = false; | |
this.range = new Range(0, 0); | |
this.nullDelay = false; | |
this.update('silent'); | |
} | |
Selection.prototype.checkFocus = function() { | |
return document.activeElement === this.doc.root; | |
}; | |
Selection.prototype.getRange = function(ignoreFocus) { | |
var end, nativeRange, start; | |
if (ignoreFocus == null) { | |
ignoreFocus = false; | |
} | |
if (this.checkFocus()) { | |
nativeRange = this._getNativeRange(); | |
if (nativeRange == null) { | |
return null; | |
} | |
start = this._positionToIndex(nativeRange.startContainer, nativeRange.startOffset); | |
if (nativeRange.startContainer === nativeRange.endContainer && nativeRange.startOffset === nativeRange.endOffset) { | |
end = start; | |
} else { | |
end = this._positionToIndex(nativeRange.endContainer, nativeRange.endOffset); | |
} | |
return new Range(Math.min(start, end), Math.max(start, end)); | |
} else if (ignoreFocus) { | |
return this.range; | |
} else { | |
return null; | |
} | |
}; | |
Selection.prototype.preserve = function(fn) { | |
var endNode, endOffset, nativeRange, ref, ref1, ref2, ref3, startNode, startOffset; | |
nativeRange = this._getNativeRange(); | |
if ((nativeRange != null) && this.checkFocus()) { | |
ref = this._encodePosition(nativeRange.startContainer, nativeRange.startOffset), startNode = ref[0], startOffset = ref[1]; | |
ref1 = this._encodePosition(nativeRange.endContainer, nativeRange.endOffset), endNode = ref1[0], endOffset = ref1[1]; | |
fn(); | |
ref2 = this._decodePosition(startNode, startOffset), startNode = ref2[0], startOffset = ref2[1]; | |
ref3 = this._decodePosition(endNode, endOffset), endNode = ref3[0], endOffset = ref3[1]; | |
return this._setNativeRange(startNode, startOffset, endNode, endOffset); | |
} else { | |
return fn(); | |
} | |
}; | |
Selection.prototype.setRange = function(range, source) { | |
var endNode, endOffset, ref, ref1, ref2, startNode, startOffset; | |
if (range != null) { | |
ref = this._indexToPosition(range.start), startNode = ref[0], startOffset = ref[1]; | |
if (range.isCollapsed()) { | |
ref1 = [startNode, startOffset], endNode = ref1[0], endOffset = ref1[1]; | |
} else { | |
ref2 = this._indexToPosition(range.end), endNode = ref2[0], endOffset = ref2[1]; | |
} | |
this._setNativeRange(startNode, startOffset, endNode, endOffset); | |
} else { | |
this._setNativeRange(null); | |
} | |
return this.update(source); | |
}; | |
Selection.prototype.shiftAfter = function(index, length, fn) { | |
var range; | |
range = this.getRange(); | |
fn(); | |
if (range != null) { | |
range.shift(index, length); | |
return this.setRange(range, 'silent'); | |
} | |
}; | |
Selection.prototype.update = function(source) { | |
var emit, focus, range, toEmit; | |
focus = this.checkFocus(); | |
range = this.getRange(true); | |
emit = source !== 'silent' && (!Range.compare(range, this.range) || focus !== this.focus); | |
toEmit = focus ? range : null; | |
if (toEmit === null && source === 'user' && !this.nullDelay) { | |
return this.nullDelay = true; | |
} else { | |
this.nullDelay = false; | |
this.range = range; | |
this.focus = focus; | |
if (emit) { | |
return this.emitter.emit(this.emitter.constructor.events.SELECTION_CHANGE, toEmit, source); | |
} | |
} | |
}; | |
Selection.prototype._decodePosition = function(node, offset) { | |
var childIndex; | |
if (dom(node).isElement()) { | |
childIndex = dom(node.parentNode).childNodes().indexOf(node); | |
offset += childIndex; | |
node = node.parentNode; | |
} | |
return [node, offset]; | |
}; | |
Selection.prototype._encodePosition = function(node, offset) { | |
var text; | |
while (true) { | |
if (dom(node).isTextNode() || node.tagName === dom.DEFAULT_BREAK_TAG || (dom.EMBED_TAGS[node.tagName] != null)) { | |
return [node, offset]; | |
} else if (offset < node.childNodes.length) { | |
node = node.childNodes[offset]; | |
offset = 0; | |
} else if (node.childNodes.length === 0) { | |
if (this.doc.normalizer.whitelist.tags[node.tagName] == null) { | |
text = document.createTextNode(''); | |
node.appendChild(text); | |
node = text; | |
} | |
return [node, 0]; | |
} else { | |
node = node.lastChild; | |
if (dom(node).isElement()) { | |
if (node.tagName === dom.DEFAULT_BREAK_TAG || (dom.EMBED_TAGS[node.tagName] != null)) { | |
return [node, 1]; | |
} else { | |
offset = node.childNodes.length; | |
} | |
} else { | |
return [node, dom(node).length()]; | |
} | |
} | |
} | |
}; | |
Selection.prototype._getNativeRange = function() { | |
var range, selection; | |
selection = document.getSelection(); | |
if ((selection != null ? selection.rangeCount : void 0) > 0) { | |
range = selection.getRangeAt(0); | |
if (dom(range.startContainer).isAncestor(this.doc.root, true)) { | |
if (range.startContainer === range.endContainer || dom(range.endContainer).isAncestor(this.doc.root, true)) { | |
return range; | |
} | |
} | |
} | |
return null; | |
}; | |
Selection.prototype._indexToPosition = function(index) { | |
var leaf, offset, ref; | |
if (this.doc.lines.length === 0) { | |
return [this.doc.root, 0]; | |
} | |
ref = this.doc.findLeafAt(index, true), leaf = ref[0], offset = ref[1]; | |
return this._decodePosition(leaf.node, offset); | |
}; | |
Selection.prototype._positionToIndex = function(node, offset) { | |
var leaf, leafNode, leafOffset, line, lineOffset, ref; | |
if (dom.isIE(10) && node.tagName === 'BR' && offset === 1) { | |
offset = 0; | |
} | |
ref = this._encodePosition(node, offset), leafNode = ref[0], offset = ref[1]; | |
line = this.doc.findLine(leafNode); | |
if (line == null) { | |
return 0; | |
} | |
leaf = line.findLeaf(leafNode); | |
lineOffset = 0; | |
while (line.prev != null) { | |
line = line.prev; | |
lineOffset += line.length; | |
} | |
if (leaf == null) { | |
return lineOffset; | |
} | |
leafOffset = 0; | |
while (leaf.prev != null) { | |
leaf = leaf.prev; | |
leafOffset += leaf.length; | |
} | |
return lineOffset + leafOffset + offset; | |
}; | |
Selection.prototype._setNativeRange = function(startNode, startOffset, endNode, endOffset) { | |
var nativeRange, selection; | |
selection = document.getSelection(); | |
if (!selection) { | |
return; | |
} | |
if (startNode != null) { | |
if (!this.checkFocus()) { | |
this.doc.root.focus(); | |
} | |
nativeRange = this._getNativeRange(); | |
if ((nativeRange == null) || startNode !== nativeRange.startContainer || startOffset !== nativeRange.startOffset || endNode !== nativeRange.endContainer || endOffset !== nativeRange.endOffset) { | |
selection.removeAllRanges(); | |
nativeRange = document.createRange(); | |
nativeRange.setStart(startNode, startOffset); | |
nativeRange.setEnd(endNode, endOffset); | |
return selection.addRange(nativeRange); | |
} | |
} else { | |
selection.removeAllRanges(); | |
this.doc.root.blur(); | |
if (dom.isIE(11) && !dom.isIE(9)) { | |
return document.body.focus(); | |
} | |
} | |
}; | |
return Selection; | |
})(); | |
module.exports = Selection; | |
},{"../lib/dom":17,"../lib/range":20,"./leaf":11,"./normalizer":13,"lodash":1}],15:[function(_dereq_,module,exports){ | |
_dereq_('./modules/authorship'); | |
_dereq_('./modules/image-tooltip'); | |
_dereq_('./modules/keyboard'); | |
_dereq_('./modules/link-tooltip'); | |
_dereq_('./modules/multi-cursor'); | |
_dereq_('./modules/paste-manager'); | |
_dereq_('./modules/toolbar'); | |
_dereq_('./modules/tooltip'); | |
_dereq_('./modules/undo-manager'); | |
module.exports = _dereq_('./quill'); | |
},{"./modules/authorship":21,"./modules/image-tooltip":22,"./modules/keyboard":23,"./modules/link-tooltip":24,"./modules/multi-cursor":25,"./modules/paste-manager":26,"./modules/toolbar":27,"./modules/tooltip":28,"./modules/undo-manager":29,"./quill":30}],16:[function(_dereq_,module,exports){ | |
var ColorPicker, Picker, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
dom = _dereq_('./dom'); | |
Picker = _dereq_('./picker'); | |
ColorPicker = (function(superClass) { | |
extend(ColorPicker, superClass); | |
function ColorPicker() { | |
ColorPicker.__super__.constructor.apply(this, arguments); | |
dom(this.container).addClass('ql-color-picker'); | |
} | |
ColorPicker.prototype.buildItem = function(picker, option, index) { | |
var item; | |
item = ColorPicker.__super__.buildItem.call(this, picker, option, index); | |
item.style.backgroundColor = option.value; | |
return item; | |
}; | |
return ColorPicker; | |
})(Picker); | |
module.exports = ColorPicker; | |
},{"./dom":17,"./picker":19}],17:[function(_dereq_,module,exports){ | |
var SelectWrapper, Wrapper, _, dom, lastKeyEvent, | |
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
_ = _dereq_('lodash'); | |
lastKeyEvent = null; | |
Wrapper = (function() { | |
function Wrapper(node1) { | |
this.node = node1; | |
this.trigger = bind(this.trigger, this); | |
} | |
Wrapper.prototype.addClass = function(cssClass) { | |
if (this.hasClass(cssClass)) { | |
return; | |
} | |
if (this.node.classList != null) { | |
this.node.classList.add(cssClass); | |
} else if (this.node.className != null) { | |
this.node.className = (this.node.className + ' ' + cssClass).trim(); | |
} | |
return this; | |
}; | |
Wrapper.prototype.attributes = function(attributes) { | |
var attr, i, j, len, ref, value; | |
if (attributes) { | |
_.each(attributes, (function(_this) { | |
return function(value, name) { | |
return _this.node.setAttribute(name, value); | |
}; | |
})(this)); | |
return this; | |
} else { | |
if (this.node.attributes == null) { | |
return {}; | |
} | |
attributes = {}; | |
ref = this.node.attributes; | |
for (i = j = 0, len = ref.length; j < len; i = ++j) { | |
value = ref[i]; | |
attr = this.node.attributes[i]; | |
attributes[attr.name] = attr.value; | |
} | |
return attributes; | |
} | |
}; | |
Wrapper.prototype.child = function(offset) { | |
var child, length; | |
child = this.node.firstChild; | |
length = dom(child).length(); | |
while (child != null) { | |
if (offset < length) { | |
break; | |
} | |
offset -= length; | |
child = child.nextSibling; | |
length = dom(child).length(); | |
} | |
if (child == null) { | |
child = this.node.lastChild; | |
offset = dom(child).length(); | |
} | |
return [child, offset]; | |
}; | |
Wrapper.prototype.childNodes = function() { | |
return _.map(this.node.childNodes); | |
}; | |
Wrapper.prototype.classes = function() { | |
return this.node.className.split(/\s+/); | |
}; | |
Wrapper.prototype.data = function(key, value) { | |
var ref; | |
if (value != null) { | |
if (this.node['ql-data'] == null) { | |
this.node['ql-data'] = {}; | |
} | |
this.node['ql-data'][key] = value; | |
return this; | |
} else { | |
return (ref = this.node['ql-data']) != null ? ref[key] : void 0; | |
} | |
}; | |
Wrapper.prototype.descendants = function() { | |
return _.map(this.node.getElementsByTagName('*')); | |
}; | |
Wrapper.prototype.get = function() { | |
return this.node; | |
}; | |
Wrapper.prototype.hasClass = function(cssClass) { | |
if (this.node.classList != null) { | |
return this.node.classList.contains(cssClass); | |
} else if (this.node.className != null) { | |
return this.classes().indexOf(cssClass) > -1; | |
} | |
return false; | |
}; | |
Wrapper.prototype.isAncestor = function(ancestor, inclusive) { | |
var node; | |
if (inclusive == null) { | |
inclusive = false; | |
} | |
if (ancestor === this.node) { | |
return inclusive; | |
} | |
node = this.node; | |
while (node) { | |
if (node === ancestor) { | |
return true; | |
} | |
node = node.parentNode; | |
} | |
return false; | |
}; | |
Wrapper.prototype.isElement = function() { | |
var ref; | |
return ((ref = this.node) != null ? ref.nodeType : void 0) === dom.ELEMENT_NODE; | |
}; | |
Wrapper.prototype.isTextNode = function() { | |
var ref; | |
return ((ref = this.node) != null ? ref.nodeType : void 0) === dom.TEXT_NODE; | |
}; | |
Wrapper.prototype.isolate = function(root) { | |
if (this.node.nextSibling != null) { | |
dom(this.node.nextSibling).splitBefore(root); | |
} | |
this.splitBefore(root); | |
return this; | |
}; | |
Wrapper.prototype.length = function() { | |
var length; | |
if (this.node == null) { | |
return 0; | |
} | |
length = this.text().length; | |
if (this.isElement()) { | |
length += this.node.querySelectorAll(Object.keys(dom.EMBED_TAGS).join(',')).length; | |
} | |
return length; | |
}; | |
Wrapper.prototype.merge = function(node) { | |
var $node; | |
$node = dom(node); | |
if (this.isElement()) { | |
$node.moveChildren(this.node); | |
this.normalize(); | |
} else { | |
this.text(this.text() + $node.text()); | |
} | |
$node.remove(); | |
return this; | |
}; | |
Wrapper.prototype.moveChildren = function(newParent) { | |
_.each(this.childNodes(), function(child) { | |
return newParent.appendChild(child); | |
}); | |
return this; | |
}; | |
Wrapper.prototype.nextLineNode = function(root) { | |
var nextNode; | |
nextNode = this.node.nextSibling; | |
if ((nextNode == null) && this.node.parentNode !== root) { | |
nextNode = this.node.parentNode.nextSibling; | |
} | |
if ((nextNode != null) && (dom.LIST_TAGS[nextNode.tagName] != null)) { | |
nextNode = nextNode.firstChild; | |
} | |
return nextNode; | |
}; | |
Wrapper.prototype.normalize = function() { | |
var $node, curNode, followingNode, nextNode; | |
curNode = this.node.firstChild; | |
while (curNode != null) { | |
nextNode = curNode.nextSibling; | |
$node = dom(curNode); | |
if ((nextNode != null) && dom(nextNode).isTextNode()) { | |
if ($node.text().length === 0) { | |
$node.remove(); | |
} else if ($node.isTextNode()) { | |
followingNode = nextNode.nextSibling; | |
$node.merge(nextNode); | |
nextNode = followingNode; | |
} | |
} | |
curNode = nextNode; | |
} | |
return this; | |
}; | |
Wrapper.prototype.on = function(eventName, listener) { | |
this.node.addEventListener(eventName, (function(_this) { | |
return function(event) { | |
var arg, propagate; | |
arg = lastKeyEvent && (eventName === 'keydown' || eventName === 'keyup') ? lastKeyEvent : event; | |
propagate = listener.call(_this.node, arg); | |
if (!propagate) { | |
event.preventDefault(); | |
event.stopPropagation(); | |
} | |
return propagate; | |
}; | |
})(this)); | |
return this; | |
}; | |
Wrapper.prototype.remove = function() { | |
var ref; | |
if ((ref = this.node.parentNode) != null) { | |
ref.removeChild(this.node); | |
} | |
this.node = null; | |
return null; | |
}; | |
Wrapper.prototype.removeClass = function(cssClass) { | |
var classArray; | |
if (!this.hasClass(cssClass)) { | |
return; | |
} | |
if (this.node.classList != null) { | |
this.node.classList.remove(cssClass); | |
} else if (this.node.className != null) { | |
classArray = this.classes(); | |
classArray.splice(classArray.indexOf(cssClass), 1); | |
this.node.className = classArray.join(' '); | |
} | |
if (!this.node.getAttribute('class')) { | |
this.node.removeAttribute('class'); | |
} | |
return this; | |
}; | |
Wrapper.prototype.replace = function(newNode) { | |
this.node.parentNode.replaceChild(newNode, this.node); | |
this.node = newNode; | |
return newNode; | |
}; | |
Wrapper.prototype.splitBefore = function(root, force) { | |
var nextNode, parentClone, parentNode, refNode; | |
if (force == null) { | |
force = false; | |
} | |
if (this.node === root || this.node.parentNode === root) { | |
return this; | |
} | |
if ((this.node.previousSibling != null) || force) { | |
parentNode = this.node.parentNode; | |
parentClone = parentNode.cloneNode(false); | |
parentNode.parentNode.insertBefore(parentClone, parentNode.nextSibling); | |
refNode = this.node; | |
while (refNode != null) { | |
nextNode = refNode.nextSibling; | |
parentClone.appendChild(refNode); | |
refNode = nextNode; | |
} | |
return dom(parentClone).splitBefore(root); | |
} else { | |
return dom(this.node.parentNode).splitBefore(root); | |
} | |
}; | |
Wrapper.prototype.split = function(offset, force) { | |
var after, child, childLeft, childRight, left, nextRight, nodeLength, ref, ref1, right; | |
if (force == null) { | |
force = false; | |
} | |
nodeLength = this.length(); | |
offset = Math.max(0, offset); | |
offset = Math.min(offset, nodeLength); | |
if (!(force || offset !== 0)) { | |
return [this.node.previousSibling, this.node, false]; | |
} | |
if (!(force || offset !== nodeLength)) { | |
return [this.node, this.node.nextSibling, false]; | |
} | |
if (this.node.nodeType === dom.TEXT_NODE) { | |
after = this.node.splitText(offset); | |
return [this.node, after, true]; | |
} else { | |
left = this.node; | |
right = this.node.cloneNode(false); | |
this.node.parentNode.insertBefore(right, left.nextSibling); | |
ref = this.child(offset), child = ref[0], offset = ref[1]; | |
ref1 = dom(child).split(offset), childLeft = ref1[0], childRight = ref1[1]; | |
while (childRight !== null) { | |
nextRight = childRight.nextSibling; | |
right.appendChild(childRight); | |
childRight = nextRight; | |
} | |
return [left, right, true]; | |
} | |
}; | |
Wrapper.prototype.styles = function(styles, overwrite) { | |
var obj, styleString; | |
if (overwrite == null) { | |
overwrite = false; | |
} | |
if (styles) { | |
if (!overwrite) { | |
styles = _.defaults(styles, this.styles()); | |
} | |
styleString = _.map(styles, function(style, name) { | |
return name + ": " + style; | |
}).join('; ') + ';'; | |
this.node.setAttribute('style', styleString); | |
return this; | |
} else { | |
styleString = this.node.getAttribute('style') || ''; | |
obj = _.reduce(styleString.split(';'), function(styles, str) { | |
var name, ref, value; | |
ref = str.split(':'), name = ref[0], value = ref[1]; | |
if (name && value) { | |
name = name.trim(); | |
value = value.trim(); | |
styles[name.toLowerCase()] = value; | |
} | |
return styles; | |
}, {}); | |
return obj; | |
} | |
}; | |
Wrapper.prototype.switchTag = function(newTag) { | |
var attributes, newNode; | |
newTag = newTag.toUpperCase(); | |
if (this.node.tagName === newTag) { | |
return this; | |
} | |
newNode = document.createElement(newTag); | |
attributes = this.attributes(); | |
if (dom.VOID_TAGS[newTag] == null) { | |
this.moveChildren(newNode); | |
} | |
this.replace(newNode); | |
return this.attributes(attributes).get(); | |
}; | |
Wrapper.prototype.text = function(text) { | |
if (text != null) { | |
switch (this.node.nodeType) { | |
case dom.ELEMENT_NODE: | |
this.node.textContent = text; | |
break; | |
case dom.TEXT_NODE: | |
this.node.data = text; | |
} | |
return this; | |
} else { | |
switch (this.node.nodeType) { | |
case dom.ELEMENT_NODE: | |
if (this.node.tagName === dom.DEFAULT_BREAK_TAG) { | |
return ""; | |
} | |
if (dom.EMBED_TAGS[this.node.tagName] != null) { | |
return dom.EMBED_TEXT; | |
} | |
if (this.node.textContent != null) { | |
return this.node.textContent; | |
} | |
return ""; | |
case dom.TEXT_NODE: | |
return this.node.data || ""; | |
default: | |
return ""; | |
} | |
} | |
}; | |
Wrapper.prototype.textNodes = function() { | |
var textNode, textNodes, walker; | |
walker = document.createTreeWalker(this.node, NodeFilter.SHOW_TEXT, null, false); | |
textNodes = []; | |
while (textNode = walker.nextNode()) { | |
textNodes.push(textNode); | |
} | |
return textNodes; | |
}; | |
Wrapper.prototype.toggleClass = function(className, state) { | |
if (state == null) { | |
state = !this.hasClass(className); | |
} | |
if (state) { | |
this.addClass(className); | |
} else { | |
this.removeClass(className); | |
} | |
return this; | |
}; | |
Wrapper.prototype.trigger = function(eventName, options) { | |
var event, initFn, modifiers; | |
if (options == null) { | |
options = {}; | |
} | |
if (['keypress', 'keydown', 'keyup'].indexOf(eventName) < 0) { | |
event = document.createEvent('Event'); | |
event.initEvent(eventName, options.bubbles, options.cancelable); | |
} else { | |
event = document.createEvent('KeyboardEvent'); | |
lastKeyEvent = _.clone(options); | |
if (_.isNumber(options.key)) { | |
lastKeyEvent.which = options.key; | |
} else if (_.isString(options.key)) { | |
lastKeyEvent.which = options.key.toUpperCase().charCodeAt(0); | |
} else { | |
lastKeyEvent.which = 0; | |
} | |
if (dom.isIE(10)) { | |
modifiers = []; | |
if (options.altKey) { | |
modifiers.push('Alt'); | |
} | |
if (options.ctrlKey) { | |
modifiers.push('Control'); | |
} | |
if (options.metaKey) { | |
modifiers.push('Meta'); | |
} | |
if (options.shiftKey) { | |
modifiers.push('Shift'); | |
} | |
event.initKeyboardEvent(eventName, options.bubbles, options.cancelable, window, 0, 0, modifiers.join(' '), null, null); | |
} else { | |
initFn = _.isFunction(event.initKeyboardEvent) ? 'initKeyboardEvent' : 'initKeyEvent'; | |
event[initFn](eventName, options.bubbles, options.cancelable, window, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, 0); | |
} | |
} | |
this.node.dispatchEvent(event); | |
lastKeyEvent = null; | |
return this; | |
}; | |
Wrapper.prototype.unwrap = function() { | |
var next, ret; | |
ret = this.node.firstChild; | |
next = this.node.nextSibling; | |
_.each(this.childNodes(), (function(_this) { | |
return function(child) { | |
return _this.node.parentNode.insertBefore(child, next); | |
}; | |
})(this)); | |
this.remove(); | |
return ret; | |
}; | |
Wrapper.prototype.wrap = function(wrapper) { | |
var parent; | |
if (this.node.parentNode != null) { | |
this.node.parentNode.insertBefore(wrapper, this.node); | |
} | |
parent = wrapper; | |
while (parent.firstChild != null) { | |
parent = wrapper.firstChild; | |
} | |
parent.appendChild(this.node); | |
return this; | |
}; | |
return Wrapper; | |
})(); | |
SelectWrapper = (function(superClass) { | |
extend(SelectWrapper, superClass); | |
function SelectWrapper() { | |
return SelectWrapper.__super__.constructor.apply(this, arguments); | |
} | |
SelectWrapper.prototype["default"] = function() { | |
return this.node.querySelector('option[selected]'); | |
}; | |
SelectWrapper.prototype.option = function(option, trigger) { | |
var child, i, j, len, ref, value; | |
if (trigger == null) { | |
trigger = true; | |
} | |
value = _.isElement(option) ? option.value : option; | |
if (value) { | |
value = value.replace(/[^\w]+/g, ''); | |
ref = this.node.children; | |
for (i = j = 0, len = ref.length; j < len; i = ++j) { | |
child = ref[i]; | |
if (child.value.replace(/[^\w]+/g, '') === value) { | |
this.node.selectedIndex = i; | |
break; | |
} | |
} | |
} else { | |
this.node.selectedIndex = -1; | |
} | |
if (trigger) { | |
this.trigger('change'); | |
} | |
return this; | |
}; | |
SelectWrapper.prototype.reset = function(trigger) { | |
var option; | |
if (trigger == null) { | |
trigger = true; | |
} | |
option = this["default"](); | |
if (option != null) { | |
option.selected = true; | |
} else { | |
this.node.selectedIndex = 0; | |
} | |
if (trigger) { | |
this.trigger('change'); | |
} | |
return this; | |
}; | |
SelectWrapper.prototype.value = function() { | |
if (this.node.selectedIndex > -1) { | |
return this.node.options[this.node.selectedIndex].value; | |
} else { | |
return ''; | |
} | |
}; | |
return SelectWrapper; | |
})(Wrapper); | |
dom = function(node) { | |
if ((node != null ? node.tagName : void 0) === 'SELECT') { | |
return new SelectWrapper(node); | |
} else { | |
return new Wrapper(node); | |
} | |
}; | |
dom = _.extend(dom, { | |
ELEMENT_NODE: 1, | |
NOBREAK_SPACE: " ", | |
TEXT_NODE: 3, | |
ZERO_WIDTH_NOBREAK_SPACE: "\uFEFF", | |
DEFAULT_BLOCK_TAG: 'DIV', | |
DEFAULT_BREAK_TAG: 'BR', | |
DEFAULT_INLINE_TAG: 'SPAN', | |
EMBED_TEXT: '!', | |
FONT_SIZES: { | |
'10px': 1, | |
'13px': 2, | |
'16px': 3, | |
'18px': 4, | |
'24px': 5, | |
'32px': 6, | |
'48px': 7 | |
}, | |
KEYS: { | |
BACKSPACE: 8, | |
TAB: 9, | |
ENTER: 13, | |
ESCAPE: 27, | |
LEFT: 37, | |
UP: 38, | |
RIGHT: 39, | |
DOWN: 40, | |
DELETE: 46 | |
}, | |
BLOCK_TAGS: { | |
'ADDRESS': 'ADDRESS', | |
'ARTICLE': 'ARTICLE', | |
'ASIDE': 'ASIDE', | |
'AUDIO': 'AUDIO', | |
'BLOCKQUOTE': 'BLOCKQUOTE', | |
'CANVAS': 'CANVAS', | |
'DD': 'DD', | |
'DIV': 'DIV', | |
'DL': 'DL', | |
'FIGCAPTION': 'FIGCAPTION', | |
'FIGURE': 'FIGURE', | |
'FOOTER': 'FOOTER', | |
'FORM': 'FORM', | |
'H1': 'H1', | |
'H2': 'H2', | |
'H3': 'H3', | |
'H4': 'H4', | |
'H5': 'H5', | |
'H6': 'H6', | |
'HEADER': 'HEADER', | |
'HGROUP': 'HGROUP', | |
'LI': 'LI', | |
'OL': 'OL', | |
'OUTPUT': 'OUTPUT', | |
'P': 'P', | |
'PRE': 'PRE', | |
'SECTION': 'SECTION', | |
'TABLE': 'TABLE', | |
'TBODY': 'TBODY', | |
'TD': 'TD', | |
'TFOOT': 'TFOOT', | |
'TH': 'TH', | |
'THEAD': 'THEAD', | |
'TR': 'TR', | |
'UL': 'UL', | |
'VIDEO': 'VIDEO' | |
}, | |
EMBED_TAGS: { | |
'IMG': 'IMG' | |
}, | |
LINE_TAGS: { | |
'DIV': 'DIV', | |
'LI': 'LI' | |
}, | |
LIST_TAGS: { | |
'OL': 'OL', | |
'UL': 'UL' | |
}, | |
VOID_TAGS: { | |
'AREA': 'AREA', | |
'BASE': 'BASE', | |
'BR': 'BR', | |
'COL': 'COL', | |
'COMMAND': 'COMMAND', | |
'EMBED': 'EMBED', | |
'HR': 'HR', | |
'IMG': 'IMG', | |
'INPUT': 'INPUT', | |
'KEYGEN': 'KEYGEN', | |
'LINK': 'LINK', | |
'META': 'META', | |
'PARAM': 'PARAM', | |
'SOURCE': 'SOURCE', | |
'TRACK': 'TRACK', | |
'WBR': 'WBR' | |
}, | |
convertFontSize: function(size) { | |
var i, s, sources, targets; | |
if (_.isString(size) && size.indexOf('px') > -1) { | |
sources = Object.keys(dom.FONT_SIZES); | |
targets = _.values(dom.FONT_SIZES); | |
} else { | |
targets = Object.keys(dom.FONT_SIZES); | |
sources = _.values(dom.FONT_SIZES); | |
} | |
for (i in sources) { | |
s = sources[i]; | |
if (parseInt(size) <= parseInt(s)) { | |
return targets[i]; | |
} | |
} | |
return _.last(targets); | |
}, | |
isIE: function(maxVersion) { | |
var version; | |
version = document.documentMode; | |
return version && maxVersion >= version; | |
}, | |
isIOS: function() { | |
return /iPhone|iPad/i.test(navigator.userAgent); | |
}, | |
isMac: function() { | |
return /Mac/i.test(navigator.platform); | |
} | |
}); | |
module.exports = dom; | |
},{"lodash":1}],18:[function(_dereq_,module,exports){ | |
var LinkedList, Node; | |
Node = (function() { | |
function Node(data) { | |
this.data = data; | |
this.prev = this.next = null; | |
} | |
return Node; | |
})(); | |
LinkedList = (function() { | |
LinkedList.Node = Node; | |
function LinkedList() { | |
this.length = 0; | |
this.first = this.last = null; | |
} | |
LinkedList.prototype.append = function(node) { | |
if (this.first != null) { | |
node.next = null; | |
this.last.next = node; | |
} else { | |
this.first = node; | |
} | |
node.prev = this.last; | |
this.last = node; | |
return this.length += 1; | |
}; | |
LinkedList.prototype.insertAfter = function(refNode, newNode) { | |
newNode.prev = refNode; | |
if (refNode != null) { | |
newNode.next = refNode.next; | |
if (refNode.next != null) { | |
refNode.next.prev = newNode; | |
} | |
refNode.next = newNode; | |
if (refNode === this.last) { | |
this.last = newNode; | |
} | |
} else { | |
newNode.next = this.first; | |
this.first.prev = newNode; | |
this.first = newNode; | |
} | |
return this.length += 1; | |
}; | |
LinkedList.prototype.remove = function(node) { | |
if (this.length > 1) { | |
if (node.prev != null) { | |
node.prev.next = node.next; | |
} | |
if (node.next != null) { | |
node.next.prev = node.prev; | |
} | |
if (node === this.first) { | |
this.first = node.next; | |
} | |
if (node === this.last) { | |
this.last = node.prev; | |
} | |
} else { | |
this.first = this.last = null; | |
} | |
node.prev = node.next = null; | |
return this.length -= 1; | |
}; | |
LinkedList.prototype.toArray = function() { | |
var arr, cur; | |
arr = []; | |
cur = this.first; | |
while (cur != null) { | |
arr.push(cur); | |
cur = cur.next; | |
} | |
return arr; | |
}; | |
return LinkedList; | |
})(); | |
module.exports = LinkedList; | |
},{}],19:[function(_dereq_,module,exports){ | |
var Picker, _, dom; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('./dom'); | |
Picker = (function() { | |
Picker.TEMPLATE = '<span class="ql-picker-label"></span><span class="ql-picker-options"></span>'; | |
function Picker(select) { | |
this.select = select; | |
this.container = document.createElement('span'); | |
this.buildPicker(); | |
dom(this.container).addClass('ql-picker'); | |
this.select.style.display = 'none'; | |
this.select.parentNode.insertBefore(this.container, this.select); | |
dom(document).on('click', (function(_this) { | |
return function() { | |
_this.close(); | |
return true; | |
}; | |
})(this)); | |
dom(this.label).on('click', (function(_this) { | |
return function() { | |
_.defer(function() { | |
return dom(_this.container).toggleClass('ql-expanded'); | |
}); | |
return false; | |
}; | |
})(this)); | |
dom(this.select).on('change', (function(_this) { | |
return function() { | |
var item, option; | |
if (_this.select.selectedIndex > -1) { | |
item = _this.container.querySelectorAll('.ql-picker-item')[_this.select.selectedIndex]; | |
option = _this.select.options[_this.select.selectedIndex]; | |
} | |
_this.selectItem(item, false); | |
return dom(_this.label).toggleClass('ql-active', option !== dom(_this.select)["default"]()); | |
}; | |
})(this)); | |
} | |
Picker.prototype.buildItem = function(picker, option, index) { | |
var item; | |
item = document.createElement('span'); | |
item.setAttribute('data-value', option.getAttribute('value')); | |
dom(item).addClass('ql-picker-item').text(dom(option).text()).on('click', (function(_this) { | |
return function() { | |
_this.selectItem(item, true); | |
return _this.close(); | |
}; | |
})(this)); | |
if (this.select.selectedIndex === index) { | |
this.selectItem(item, false); | |
} | |
return item; | |
}; | |
Picker.prototype.buildPicker = function() { | |
var picker; | |
_.each(dom(this.select).attributes(), (function(_this) { | |
return function(value, name) { | |
return _this.container.setAttribute(name, value); | |
}; | |
})(this)); | |
this.container.innerHTML = Picker.TEMPLATE; | |
this.label = this.container.querySelector('.ql-picker-label'); | |
picker = this.container.querySelector('.ql-picker-options'); | |
return _.each(this.select.options, (function(_this) { | |
return function(option, i) { | |
var item; | |
item = _this.buildItem(picker, option, i); | |
return picker.appendChild(item); | |
}; | |
})(this)); | |
}; | |
Picker.prototype.close = function() { | |
return dom(this.container).removeClass('ql-expanded'); | |
}; | |
Picker.prototype.selectItem = function(item, trigger) { | |
var selected, value; | |
selected = this.container.querySelector('.ql-selected'); | |
if (selected != null) { | |
dom(selected).removeClass('ql-selected'); | |
} | |
if (item != null) { | |
value = item.getAttribute('data-value'); | |
dom(item).addClass('ql-selected'); | |
dom(this.label).text(dom(item).text()); | |
dom(this.select).option(value, trigger); | |
return this.label.setAttribute('data-value', value); | |
} else { | |
this.label.innerHTML = ' '; | |
return this.label.removeAttribute('data-value'); | |
} | |
}; | |
return Picker; | |
})(); | |
module.exports = Picker; | |
},{"./dom":17,"lodash":1}],20:[function(_dereq_,module,exports){ | |
var Range, _; | |
_ = _dereq_('lodash'); | |
Range = (function() { | |
Range.compare = function(r1, r2) { | |
if (r1 === r2) { | |
return true; | |
} | |
if (!((r1 != null) && (r2 != null))) { | |
return false; | |
} | |
return r1.equals(r2); | |
}; | |
function Range(start, end) { | |
this.start = start; | |
this.end = end; | |
} | |
Range.prototype.equals = function(range) { | |
if (range == null) { | |
return false; | |
} | |
return this.start === range.start && this.end === range.end; | |
}; | |
Range.prototype.shift = function(index, length) { | |
var ref; | |
return ref = _.map([this.start, this.end], function(pos) { | |
if (index > pos) { | |
return pos; | |
} | |
if (length >= 0) { | |
return pos + length; | |
} else { | |
return Math.max(index, pos + length); | |
} | |
}), this.start = ref[0], this.end = ref[1], ref; | |
}; | |
Range.prototype.isCollapsed = function() { | |
return this.start === this.end; | |
}; | |
return Range; | |
})(); | |
module.exports = Range; | |
},{"lodash":1}],21:[function(_dereq_,module,exports){ | |
var Authorship, Delta, Quill, _, dom; | |
Quill = _dereq_('../quill'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Delta = Quill.require('delta'); | |
Authorship = (function() { | |
Authorship.DEFAULTS = { | |
authorId: null, | |
color: 'transparent', | |
enabled: false | |
}; | |
function Authorship(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
if (this.options.button != null) { | |
this.attachButton(this.options.button); | |
} | |
if (this.options.enabled) { | |
this.enable(); | |
} | |
this.quill.addFormat('author', { | |
"class": 'author-' | |
}); | |
if (this.options.authorId == null) { | |
return; | |
} | |
this.quill.on(this.quill.constructor.events.PRE_EVENT, (function(_this) { | |
return function(eventName, delta, origin) { | |
var authorDelta, authorFormat; | |
if (eventName === _this.quill.constructor.events.TEXT_CHANGE && origin === 'user') { | |
authorDelta = new Delta(); | |
authorFormat = { | |
author: _this.options.authorId | |
}; | |
_.each(delta.ops, function(op) { | |
if (op["delete"] != null) { | |
return; | |
} | |
if ((op.insert != null) || ((op.retain != null) && (op.attributes != null))) { | |
op.attributes || (op.attributes = {}); | |
op.attributes.author = _this.options.authorId; | |
return authorDelta.retain(op.retain || op.insert.length || 1, authorFormat); | |
} else { | |
return authorDelta.retain(op.retain); | |
} | |
}); | |
return _this.quill.updateContents(authorDelta, Quill.sources.SILENT); | |
} | |
}; | |
})(this)); | |
this.addAuthor(this.options.authorId, this.options.color); | |
} | |
Authorship.prototype.addAuthor = function(id, color) { | |
var styles; | |
styles = {}; | |
styles[".authorship .author-" + id] = { | |
"background-color": "" + color | |
}; | |
return this.quill.theme.addStyles(styles); | |
}; | |
Authorship.prototype.attachButton = function(button) { | |
var $button; | |
$button = dom(button); | |
return $button.on('click', (function(_this) { | |
return function() { | |
$button.toggleClass('ql-on'); | |
return _this.enable($dom.hasClass('ql-on')); | |
}; | |
})(this)); | |
}; | |
Authorship.prototype.enable = function(enabled) { | |
if (enabled == null) { | |
enabled = true; | |
} | |
return dom(this.quill.root).toggleClass('authorship', enabled); | |
}; | |
Authorship.prototype.disable = function() { | |
return this.enable(false); | |
}; | |
return Authorship; | |
})(); | |
Quill.registerModule('authorship', Authorship); | |
module.exports = Authorship; | |
},{"../quill":30}],22:[function(_dereq_,module,exports){ | |
var Delta, ImageTooltip, Quill, Range, Tooltip, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
Quill = _dereq_('../quill'); | |
Tooltip = _dereq_('./tooltip'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Delta = Quill.require('delta'); | |
Range = Quill.require('range'); | |
ImageTooltip = (function(superClass) { | |
extend(ImageTooltip, superClass); | |
ImageTooltip.DEFAULTS = { | |
template: '<input class="input" type="textbox"> <div class="preview"> <span>Preview</span> </div> <a href="javascript:;" class="cancel">Cancel</a> <a href="javascript:;" class="insert">Insert</a>' | |
}; | |
function ImageTooltip(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
this.options = _.defaults(this.options, Tooltip.DEFAULTS); | |
ImageTooltip.__super__.constructor.call(this, this.quill, this.options); | |
this.preview = this.container.querySelector('.preview'); | |
this.textbox = this.container.querySelector('.input'); | |
dom(this.container).addClass('ql-image-tooltip'); | |
this.initListeners(); | |
} | |
ImageTooltip.prototype.initListeners = function() { | |
dom(this.quill.root).on('focus', _.bind(this.hide, this)); | |
dom(this.container.querySelector('.insert')).on('click', _.bind(this.insertImage, this)); | |
dom(this.container.querySelector('.cancel')).on('click', _.bind(this.hide, this)); | |
dom(this.textbox).on('input', _.bind(this._preview, this)); | |
this.initTextbox(this.textbox, this.insertImage, this.hide); | |
return this.quill.onModuleLoad('toolbar', (function(_this) { | |
return function(toolbar) { | |
_this.toolbar = toolbar; | |
return toolbar.initFormat('image', _.bind(_this._onToolbar, _this)); | |
}; | |
})(this)); | |
}; | |
ImageTooltip.prototype.insertImage = function() { | |
var index, url; | |
url = this._normalizeURL(this.textbox.value); | |
if (this.range == null) { | |
this.range = new Range(0, 0); | |
} | |
if (this.range) { | |
this.preview.innerHTML = '<span>Preview</span>'; | |
this.textbox.value = ''; | |
index = this.range.end; | |
this.quill.insertEmbed(index, 'image', url, 'user'); | |
this.quill.setSelection(index + 1, index + 1); | |
} | |
return this.hide(); | |
}; | |
ImageTooltip.prototype._onToolbar = function(range, value) { | |
if (value) { | |
if (!this.textbox.value) { | |
this.textbox.value = 'http://'; | |
} | |
this.show(); | |
this.textbox.focus(); | |
return _.defer((function(_this) { | |
return function() { | |
return _this.textbox.setSelectionRange(_this.textbox.value.length, _this.textbox.value.length); | |
}; | |
})(this)); | |
} else { | |
this.quill.deleteText(range, 'user'); | |
return this.toolbar.setActive('image', false); | |
} | |
}; | |
ImageTooltip.prototype._preview = function() { | |
var img; | |
if (!this._matchImageURL(this.textbox.value)) { | |
return; | |
} | |
if (this.preview.firstChild.tagName === 'IMG') { | |
return this.preview.firstChild.setAttribute('src', this.textbox.value); | |
} else { | |
img = document.createElement('img'); | |
img.setAttribute('src', this.textbox.value); | |
return this.preview.replaceChild(img, this.preview.firstChild); | |
} | |
}; | |
ImageTooltip.prototype._matchImageURL = function(url) { | |
return /^https?:\/\/.+\.(jpe?g|gif|png)$/.test(url); | |
}; | |
ImageTooltip.prototype._normalizeURL = function(url) { | |
if (!/^https?:\/\//.test(url)) { | |
url = 'http://' + url; | |
} | |
return url; | |
}; | |
return ImageTooltip; | |
})(Tooltip); | |
Quill.registerModule('image-tooltip', ImageTooltip); | |
module.exports = ImageTooltip; | |
},{"../quill":30,"./tooltip":28}],23:[function(_dereq_,module,exports){ | |
var Delta, Keyboard, Quill, _, dom; | |
Quill = _dereq_('../quill'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Delta = Quill.require('delta'); | |
Keyboard = (function() { | |
Keyboard.hotkeys = { | |
BOLD: { | |
key: 'B', | |
metaKey: true | |
}, | |
INDENT: { | |
key: dom.KEYS.TAB | |
}, | |
ITALIC: { | |
key: 'I', | |
metaKey: true | |
}, | |
OUTDENT: { | |
key: dom.KEYS.TAB, | |
shiftKey: true | |
}, | |
UNDERLINE: { | |
key: 'U', | |
metaKey: true | |
} | |
}; | |
function Keyboard(quill, options) { | |
this.quill = quill; | |
this.hotkeys = {}; | |
this._initListeners(); | |
this._initHotkeys(); | |
this.quill.onModuleLoad('toolbar', (function(_this) { | |
return function(toolbar) { | |
return _this.toolbar = toolbar; | |
}; | |
})(this)); | |
} | |
Keyboard.prototype.addHotkey = function(hotkeys, callback) { | |
if (!Array.isArray(hotkeys)) { | |
hotkeys = [hotkeys]; | |
} | |
return _.each(hotkeys, (function(_this) { | |
return function(hotkey) { | |
var base, which; | |
hotkey = _.isObject(hotkey) ? _.clone(hotkey) : { | |
key: hotkey | |
}; | |
hotkey.callback = callback; | |
which = _.isNumber(hotkey.key) ? hotkey.key : hotkey.key.toUpperCase().charCodeAt(0); | |
if ((base = _this.hotkeys)[which] == null) { | |
base[which] = []; | |
} | |
return _this.hotkeys[which].push(hotkey); | |
}; | |
})(this)); | |
}; | |
Keyboard.prototype.toggleFormat = function(range, format) { | |
var delta, value; | |
if (range.isCollapsed()) { | |
delta = this.quill.getContents(Math.max(0, range.start - 1), range.end); | |
} else { | |
delta = this.quill.getContents(range); | |
} | |
value = delta.ops.length === 0 || !_.all(delta.ops, function(op) { | |
var ref; | |
return (ref = op.attributes) != null ? ref[format] : void 0; | |
}); | |
if (range.isCollapsed()) { | |
this.quill.prepareFormat(format, value, Quill.sources.USER); | |
} else { | |
this.quill.formatText(range, format, value, Quill.sources.USER); | |
} | |
if (this.toolbar != null) { | |
return this.toolbar.setActive(format, value); | |
} | |
}; | |
Keyboard.prototype._initEnter = function() { | |
var keys; | |
keys = [ | |
{ | |
key: dom.KEYS.ENTER | |
}, { | |
key: dom.KEYS.ENTER, | |
shiftKey: true | |
} | |
]; | |
return this.addHotkey(keys, (function(_this) { | |
return function(range, hotkey) { | |
var delta, leaf, line, offset, ref, ref1; | |
if (range == null) { | |
return true; | |
} | |
ref = _this.quill.editor.doc.findLineAt(range.start), line = ref[0], offset = ref[1]; | |
ref1 = line.findLeafAt(offset), leaf = ref1[0], offset = ref1[1]; | |
delta = new Delta().retain(range.start).insert('\n', line.formats)["delete"](range.end - range.start); | |
_this.quill.updateContents(delta, Quill.sources.USER); | |
_.each(leaf.formats, function(value, format) { | |
_this.quill.prepareFormat(format, value); | |
if (_this.toolbar != null) { | |
return _this.toolbar.setActive(format, value); | |
} | |
}); | |
return false; | |
}; | |
})(this)); | |
}; | |
Keyboard.prototype._initDeletes = function() { | |
return this.addHotkey([dom.KEYS.DELETE, dom.KEYS.BACKSPACE], (function(_this) { | |
return function(range, hotkey) { | |
var format, line, offset, ref; | |
if ((range != null) && _this.quill.getLength() > 0) { | |
if (range.start !== range.end) { | |
_this.quill.deleteText(range.start, range.end, Quill.sources.USER); | |
} else { | |
if (hotkey.key === dom.KEYS.BACKSPACE) { | |
ref = _this.quill.editor.doc.findLineAt(range.start), line = ref[0], offset = ref[1]; | |
if (offset === 0 && (line.formats.bullet || line.formats.list)) { | |
format = line.formats.bullet ? 'bullet' : 'list'; | |
_this.quill.formatLine(range.start, range.start, format, false); | |
} else if (range.start > 0) { | |
_this.quill.deleteText(range.start - 1, range.start, Quill.sources.USER); | |
} | |
} else if (range.start < _this.quill.getLength() - 1) { | |
_this.quill.deleteText(range.start, range.start + 1, Quill.sources.USER); | |
} | |
} | |
} | |
return false; | |
}; | |
})(this)); | |
}; | |
Keyboard.prototype._initHotkeys = function() { | |
this.addHotkey(Keyboard.hotkeys.INDENT, (function(_this) { | |
return function(range) { | |
_this._onTab(range, false); | |
return false; | |
}; | |
})(this)); | |
this.addHotkey(Keyboard.hotkeys.OUTDENT, (function(_this) { | |
return function(range) { | |
return false; | |
}; | |
})(this)); | |
_.each(['bold', 'italic', 'underline'], (function(_this) { | |
return function(format) { | |
return _this.addHotkey(Keyboard.hotkeys[format.toUpperCase()], function(range) { | |
_this.toggleFormat(range, format); | |
return false; | |
}); | |
}; | |
})(this)); | |
this._initDeletes(); | |
return this._initEnter(); | |
}; | |
Keyboard.prototype._initListeners = function() { | |
return dom(this.quill.root).on('keydown', (function(_this) { | |
return function(event) { | |
var prevent; | |
prevent = false; | |
_.each(_this.hotkeys[event.which], function(hotkey) { | |
var metaKey; | |
metaKey = dom.isMac() ? event.metaKey : event.metaKey || event.ctrlKey; | |
if (!!hotkey.metaKey !== !!metaKey) { | |
return; | |
} | |
if (!!hotkey.shiftKey !== !!event.shiftKey) { | |
return; | |
} | |
if (!!hotkey.altKey !== !!event.altKey) { | |
return; | |
} | |
prevent = hotkey.callback(_this.quill.getSelection(), hotkey, event) === false || prevent; | |
return true; | |
}); | |
return !prevent; | |
}; | |
})(this)); | |
}; | |
Keyboard.prototype._onTab = function(range, shift) { | |
var delta; | |
if (shift == null) { | |
shift = false; | |
} | |
delta = new Delta().retain(range.start).insert("\t")["delete"](range.end - range.start).retain(this.quill.getLength() - range.end); | |
this.quill.updateContents(delta, Quill.sources.USER); | |
return this.quill.setSelection(range.start + 1, range.start + 1); | |
}; | |
return Keyboard; | |
})(); | |
Quill.registerModule('keyboard', Keyboard); | |
module.exports = Keyboard; | |
},{"../quill":30}],24:[function(_dereq_,module,exports){ | |
var LinkTooltip, Quill, Tooltip, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
Quill = _dereq_('../quill'); | |
Tooltip = _dereq_('./tooltip'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
LinkTooltip = (function(superClass) { | |
extend(LinkTooltip, superClass); | |
LinkTooltip.DEFAULTS = { | |
maxLength: 50, | |
template: '<span class="title">Visit URL: </span> <a href="#" class="url" target="_blank" href="about:blank"></a> <input class="input" type="text"> <span> - </span> <a href="javascript:;" class="change">Change</a> <a href="javascript:;" class="remove">Remove</a> <a href="javascript:;" class="done">Done</a>', | |
target: '_self' | |
}; | |
LinkTooltip.hotkeys = { | |
LINK: { | |
key: 'K', | |
metaKey: true | |
} | |
}; | |
function LinkTooltip(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
this.options = _.defaults(this.options, Tooltip.DEFAULTS); | |
LinkTooltip.__super__.constructor.call(this, this.quill, this.options); | |
dom(this.container).addClass('ql-link-tooltip'); | |
this.textbox = this.container.querySelector('.input'); | |
this.link = this.container.querySelector('.url'); | |
this.initListeners(); | |
} | |
LinkTooltip.prototype.initListeners = function() { | |
this.quill.on(this.quill.constructor.events.SELECTION_CHANGE, (function(_this) { | |
return function(range) { | |
var anchor; | |
if (!((range != null) && range.isCollapsed())) { | |
return; | |
} | |
anchor = _this._findAnchor(range); | |
if (anchor) { | |
_this.setMode(anchor.href, false); | |
return _this.show(anchor); | |
} else if (_this.container.style.left !== Tooltip.HIDE_MARGIN) { | |
_this.range = null; | |
return _this.hide(); | |
} | |
}; | |
})(this)); | |
dom(this.container.querySelector('.done')).on('click', _.bind(this.saveLink, this)); | |
dom(this.container.querySelector('.remove')).on('click', (function(_this) { | |
return function() { | |
return _this.removeLink(_this.range); | |
}; | |
})(this)); | |
dom(this.container.querySelector('.change')).on('click', (function(_this) { | |
return function() { | |
return _this.setMode(_this.link.href, true); | |
}; | |
})(this)); | |
this.initTextbox(this.textbox, this.saveLink, this.hide); | |
this.quill.onModuleLoad('toolbar', (function(_this) { | |
return function(toolbar) { | |
_this.toolbar = toolbar; | |
return toolbar.initFormat('link', _.bind(_this._onToolbar, _this)); | |
}; | |
})(this)); | |
return this.quill.onModuleLoad('keyboard', (function(_this) { | |
return function(keyboard) { | |
return keyboard.addHotkey(LinkTooltip.hotkeys.LINK, _.bind(_this._onKeyboard, _this)); | |
}; | |
})(this)); | |
}; | |
LinkTooltip.prototype.saveLink = function() { | |
var anchor, end, url; | |
url = this._normalizeURL(this.textbox.value); | |
if (this.range != null) { | |
end = this.range.end; | |
if (this.range.isCollapsed()) { | |
anchor = this._findAnchor(this.range); | |
if (anchor != null) { | |
anchor.href = url; | |
} | |
} else { | |
this.quill.formatText(this.range, 'link', url, 'user'); | |
} | |
this.quill.setSelection(end, end); | |
} | |
return this.setMode(url, false); | |
}; | |
LinkTooltip.prototype.removeLink = function(range) { | |
if (range.isCollapsed()) { | |
range = this._expandRange(range); | |
} | |
this.hide(); | |
this.quill.formatText(range, 'link', false, 'user'); | |
if (this.toolbar != null) { | |
return this.toolbar.setActive('link', false); | |
} | |
}; | |
LinkTooltip.prototype.setMode = function(url, edit) { | |
var text; | |
if (edit == null) { | |
edit = false; | |
} | |
if (edit) { | |
this.textbox.value = url; | |
_.defer((function(_this) { | |
return function() { | |
_this.textbox.focus(); | |
return _this.textbox.setSelectionRange(0, url.length); | |
}; | |
})(this)); | |
} else { | |
this.link.href = url; | |
url = this.link.href; | |
text = url.length > this.options.maxLength ? url.slice(0, this.options.maxLength) + '...' : url; | |
dom(this.link).text(text); | |
} | |
return dom(this.container).toggleClass('editing', edit); | |
}; | |
LinkTooltip.prototype._findAnchor = function(range) { | |
var leaf, node, offset, ref; | |
ref = this.quill.editor.doc.findLeafAt(range.start, true), leaf = ref[0], offset = ref[1]; | |
if (leaf != null) { | |
node = leaf.node; | |
} | |
while ((node != null) && node !== this.quill.root) { | |
if (node.tagName === 'A') { | |
return node; | |
} | |
node = node.parentNode; | |
} | |
return null; | |
}; | |
LinkTooltip.prototype._expandRange = function(range) { | |
var end, leaf, offset, ref, start; | |
ref = this.quill.editor.doc.findLeafAt(range.start, true), leaf = ref[0], offset = ref[1]; | |
start = range.start - offset; | |
end = start + leaf.length; | |
return { | |
start: start, | |
end: end | |
}; | |
}; | |
LinkTooltip.prototype._onToolbar = function(range, value) { | |
return this._toggle(range, value); | |
}; | |
LinkTooltip.prototype._onKeyboard = function() { | |
var range; | |
range = this.quill.getSelection(); | |
return this._toggle(range, !this._findAnchor(range)); | |
}; | |
LinkTooltip.prototype._toggle = function(range, value) { | |
var nativeRange; | |
if (!range) { | |
return; | |
} | |
if (!value) { | |
return this.removeLink(range); | |
} else if (!range.isCollapsed()) { | |
this.setMode(this._suggestURL(range), true); | |
nativeRange = this.quill.editor.selection._getNativeRange(); | |
return this.show(nativeRange); | |
} | |
}; | |
LinkTooltip.prototype._normalizeURL = function(url) { | |
if (!/^(https?:\/\/|mailto:)/.test(url)) { | |
url = 'http://' + url; | |
} | |
return url; | |
}; | |
LinkTooltip.prototype._suggestURL = function(range) { | |
var text; | |
text = this.quill.getText(range); | |
return this._normalizeURL(text); | |
}; | |
return LinkTooltip; | |
})(Tooltip); | |
Quill.registerModule('link-tooltip', LinkTooltip); | |
module.exports = LinkTooltip; | |
},{"../quill":30,"./tooltip":28}],25:[function(_dereq_,module,exports){ | |
var EventEmitter2, MultiCursor, Quill, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
Quill = _dereq_('../quill'); | |
EventEmitter2 = _dereq_('eventemitter2').EventEmitter2; | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
MultiCursor = (function(superClass) { | |
extend(MultiCursor, superClass); | |
MultiCursor.DEFAULTS = { | |
template: '<span class="cursor-flag"> <span class="cursor-name"></span> </span> <span class="cursor-caret"></span>', | |
timeout: 2500 | |
}; | |
MultiCursor.events = { | |
CURSOR_ADDED: 'cursor-addded', | |
CURSOR_MOVED: 'cursor-moved', | |
CURSOR_REMOVED: 'cursor-removed' | |
}; | |
function MultiCursor(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
this.cursors = {}; | |
this.container = this.quill.addContainer('ql-multi-cursor', true); | |
this.quill.on(this.quill.constructor.events.TEXT_CHANGE, _.bind(this._applyDelta, this)); | |
} | |
MultiCursor.prototype.clearCursors = function() { | |
_.each(Object.keys(this.cursors), _.bind(this.removeCursor, this)); | |
return this.cursors = {}; | |
}; | |
MultiCursor.prototype.moveCursor = function(userId, index) { | |
var cursor; | |
cursor = this.cursors[userId]; | |
cursor.index = index; | |
dom(cursor.elem).removeClass('hidden'); | |
clearTimeout(cursor.timer); | |
cursor.timer = setTimeout((function(_this) { | |
return function() { | |
dom(cursor.elem).addClass('hidden'); | |
return cursor.timer = null; | |
}; | |
})(this), this.options.timeout); | |
this._updateCursor(cursor); | |
return cursor; | |
}; | |
MultiCursor.prototype.removeCursor = function(userId) { | |
var cursor; | |
cursor = this.cursors[userId]; | |
this.emit(MultiCursor.events.CURSOR_REMOVED, cursor); | |
if (cursor != null) { | |
cursor.elem.parentNode.removeChild(cursor.elem); | |
} | |
return delete this.cursors[userId]; | |
}; | |
MultiCursor.prototype.setCursor = function(userId, index, name, color) { | |
var cursor; | |
if (this.cursors[userId] == null) { | |
this.cursors[userId] = cursor = { | |
userId: userId, | |
index: index, | |
color: color, | |
elem: this._buildCursor(name, color) | |
}; | |
this.emit(MultiCursor.events.CURSOR_ADDED, cursor); | |
} | |
_.defer((function(_this) { | |
return function() { | |
return _this.moveCursor(userId, index); | |
}; | |
})(this)); | |
return this.cursors[userId]; | |
}; | |
MultiCursor.prototype.shiftCursors = function(index, length, authorId) { | |
if (authorId == null) { | |
authorId = null; | |
} | |
return _.each(this.cursors, (function(_this) { | |
return function(cursor, id) { | |
if (!(cursor && (cursor.index > index || cursor.userId === authorId))) { | |
return; | |
} | |
return cursor.index += Math.max(length, index - cursor.index); | |
}; | |
})(this)); | |
}; | |
MultiCursor.prototype.update = function() { | |
return _.each(this.cursors, (function(_this) { | |
return function(cursor, id) { | |
if (cursor == null) { | |
return; | |
} | |
_this._updateCursor(cursor); | |
return true; | |
}; | |
})(this)); | |
}; | |
MultiCursor.prototype._applyDelta = function(delta) { | |
var index; | |
index = 0; | |
_.each(delta.ops, (function(_this) { | |
return function(op) { | |
var length, ref; | |
length = 0; | |
if (op.insert != null) { | |
length = op.insert.length || 1; | |
_this.shiftCursors(index, length, (ref = op.attributes) != null ? ref['author'] : void 0); | |
} else if (op["delete"] != null) { | |
_this.shiftCursors(index, -1 * op["delete"], null); | |
} else if (op.retain != null) { | |
_this.shiftCursors(index, 0, null); | |
length = op.retain; | |
} | |
return index += length; | |
}; | |
})(this)); | |
return this.update(); | |
}; | |
MultiCursor.prototype._buildCursor = function(name, color) { | |
var cursor, cursorCaret, cursorFlag, cursorName; | |
cursor = document.createElement('span'); | |
dom(cursor).addClass('cursor'); | |
cursor.innerHTML = this.options.template; | |
cursorFlag = cursor.querySelector('.cursor-flag'); | |
cursorName = cursor.querySelector('.cursor-name'); | |
dom(cursorName).text(name); | |
cursorCaret = cursor.querySelector('.cursor-caret'); | |
cursorCaret.style.backgroundColor = cursorName.style.backgroundColor = color; | |
this.container.appendChild(cursor); | |
return cursor; | |
}; | |
MultiCursor.prototype._updateCursor = function(cursor) { | |
var bounds, flag; | |
bounds = this.quill.getBounds(cursor.index); | |
cursor.elem.style.top = (bounds.top + this.quill.container.scrollTop) + 'px'; | |
cursor.elem.style.left = bounds.left + 'px'; | |
cursor.elem.style.height = bounds.height + 'px'; | |
flag = cursor.elem.querySelector('.cursor-flag'); | |
dom(cursor.elem).toggleClass('top', parseInt(cursor.elem.style.top) <= flag.offsetHeight).toggleClass('left', parseInt(cursor.elem.style.left) <= flag.offsetWidth).toggleClass('right', this.quill.root.offsetWidth - parseInt(cursor.elem.style.left) <= flag.offsetWidth); | |
return this.emit(MultiCursor.events.CURSOR_MOVED, cursor); | |
}; | |
return MultiCursor; | |
})(EventEmitter2); | |
Quill.registerModule('multi-cursor', MultiCursor); | |
module.exports = MultiCursor; | |
},{"../quill":30,"eventemitter2":2}],26:[function(_dereq_,module,exports){ | |
var Delta, Document, PasteManager, Quill, _, dom; | |
Quill = _dereq_('../quill'); | |
Document = _dereq_('../core/document'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Delta = Quill.require('delta'); | |
PasteManager = (function() { | |
function PasteManager(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
this.container = this.quill.addContainer('ql-paste-manager'); | |
this.container.setAttribute('contenteditable', true); | |
dom(this.quill.root).on('paste', _.bind(this._paste, this)); | |
} | |
PasteManager.prototype._paste = function() { | |
var oldDocLength, range; | |
oldDocLength = this.quill.getLength(); | |
range = this.quill.getSelection(); | |
if (range == null) { | |
return; | |
} | |
this.container.focus(); | |
return _.defer((function(_this) { | |
return function() { | |
var delta, doc, lengthAdded, line, lineBottom, offset, ref, windowBottom; | |
doc = new Document(_this.container, _this.quill.options); | |
delta = doc.toDelta(); | |
lengthAdded = Math.max(0, delta.length() - 1); | |
if (lengthAdded > 0) { | |
delta = delta.compose(new Delta().retain(lengthAdded)["delete"](1)); | |
if (range.start > 0) { | |
delta.ops.unshift({ | |
retain: range.start | |
}); | |
} | |
delta["delete"](range.end - range.start); | |
_this.quill.updateContents(delta, 'user'); | |
} | |
_this.quill.setSelection(range.start + lengthAdded, range.start + lengthAdded); | |
ref = _this.quill.editor.doc.findLineAt(range.start + lengthAdded), line = ref[0], offset = ref[1]; | |
lineBottom = line.node.getBoundingClientRect().bottom; | |
windowBottom = document.documentElement.clientHeight; | |
if (lineBottom > windowBottom) { | |
line.node.scrollIntoView(false); | |
} | |
return _this.container.innerHTML = ""; | |
}; | |
})(this)); | |
}; | |
return PasteManager; | |
})(); | |
Quill.registerModule('paste-manager', PasteManager); | |
module.exports = PasteManager; | |
},{"../core/document":8,"../quill":30}],27:[function(_dereq_,module,exports){ | |
var Quill, Toolbar, _, dom; | |
Quill = _dereq_('../quill'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Toolbar = (function() { | |
Toolbar.DEFAULTS = { | |
container: null | |
}; | |
Toolbar.formats = { | |
LINE: { | |
'align': 'align', | |
'bullet': 'bullet', | |
'list': 'list' | |
}, | |
SELECT: { | |
'align': 'align', | |
'background': 'background', | |
'color': 'color', | |
'font': 'font', | |
'size': 'size' | |
}, | |
TOGGLE: { | |
'bold': 'bold', | |
'bullet': 'bullet', | |
'image': 'image', | |
'italic': 'italic', | |
'link': 'link', | |
'list': 'list', | |
'strike': 'strike', | |
'underline': 'underline' | |
}, | |
TOOLTIP: { | |
'image': 'image', | |
'link': 'link' | |
} | |
}; | |
function Toolbar(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
if (_.isString(this.options) || _.isElement(this.options)) { | |
this.options = { | |
container: this.options | |
}; | |
} | |
if (this.options.container == null) { | |
throw new Error('container required for toolbar', this.options); | |
} | |
this.container = _.isString(this.options.container) ? document.querySelector(this.options.container) : this.options.container; | |
this.inputs = {}; | |
this.preventUpdate = false; | |
this.triggering = false; | |
_.each(this.quill.options.formats, (function(_this) { | |
return function(name) { | |
if (Toolbar.formats.TOOLTIP[name] != null) { | |
return; | |
} | |
return _this.initFormat(name, _.bind(_this._applyFormat, _this, name)); | |
}; | |
})(this)); | |
this.quill.on(Quill.events.FORMAT_INIT, (function(_this) { | |
return function(name) { | |
if (Toolbar.formats.TOOLTIP[name] != null) { | |
return; | |
} | |
return _this.initFormat(name, _.bind(_this._applyFormat, _this, name)); | |
}; | |
})(this)); | |
this.quill.on(Quill.events.SELECTION_CHANGE, (function(_this) { | |
return function(range) { | |
if (range != null) { | |
return _this.updateActive(range); | |
} | |
}; | |
})(this)); | |
this.quill.on(Quill.events.TEXT_CHANGE, (function(_this) { | |
return function() { | |
return _this.updateActive(); | |
}; | |
})(this)); | |
this.quill.onModuleLoad('keyboard', (function(_this) { | |
return function(keyboard) { | |
return keyboard.addHotkey([dom.KEYS.BACKSPACE, dom.KEYS.DELETE], function() { | |
return _.defer(_.bind(_this.updateActive, _this)); | |
}); | |
}; | |
})(this)); | |
dom(this.container).addClass('ql-toolbar'); | |
if (dom.isIOS()) { | |
dom(this.container).addClass('ios'); | |
} | |
} | |
Toolbar.prototype.initFormat = function(format, callback) { | |
var eventName, input, selector; | |
selector = ".ql-" + format; | |
if (Toolbar.formats.SELECT[format] != null) { | |
selector = "select" + selector; | |
eventName = 'change'; | |
} else { | |
eventName = 'click'; | |
} | |
input = this.container.querySelector(selector); | |
if (input == null) { | |
return; | |
} | |
this.inputs[format] = input; | |
return dom(input).on(eventName, (function(_this) { | |
return function() { | |
var range, value; | |
value = eventName === 'change' ? dom(input).value() : !dom(input).hasClass('ql-active'); | |
_this.preventUpdate = true; | |
_this.quill.focus(); | |
range = _this.quill.getSelection(); | |
if (range != null) { | |
callback(range, value); | |
} | |
_this.preventUpdate = false; | |
return true; | |
}; | |
})(this)); | |
}; | |
Toolbar.prototype.setActive = function(format, value) { | |
var $input, input, ref, selectValue; | |
if (format === 'image') { | |
value = false; | |
} | |
input = this.inputs[format]; | |
if (input == null) { | |
return; | |
} | |
$input = dom(input); | |
if (input.tagName === 'SELECT') { | |
this.triggering = true; | |
selectValue = $input.value(input); | |
if (value == null) { | |
value = (ref = $input["default"]()) != null ? ref.value : void 0; | |
} | |
if (Array.isArray(value)) { | |
value = ''; | |
} | |
if (value !== selectValue) { | |
if (value != null) { | |
$input.option(value); | |
} else { | |
$input.reset(); | |
} | |
} | |
return this.triggering = false; | |
} else { | |
return $input.toggleClass('ql-active', value || false); | |
} | |
}; | |
Toolbar.prototype.updateActive = function(range, formats) { | |
var activeFormats; | |
if (formats == null) { | |
formats = null; | |
} | |
range || (range = this.quill.getSelection()); | |
if (!((range != null) && !this.preventUpdate)) { | |
return; | |
} | |
activeFormats = this._getActive(range); | |
return _.each(this.inputs, (function(_this) { | |
return function(input, format) { | |
if (!Array.isArray(formats) || formats.indexOf(format) > -1) { | |
_this.setActive(format, activeFormats[format]); | |
} | |
return true; | |
}; | |
})(this)); | |
}; | |
Toolbar.prototype._applyFormat = function(format, range, value) { | |
if (this.triggering) { | |
return; | |
} | |
if (range.isCollapsed()) { | |
this.quill.prepareFormat(format, value, 'user'); | |
} else if (Toolbar.formats.LINE[format] != null) { | |
this.quill.formatLine(range, format, value, 'user'); | |
} else { | |
this.quill.formatText(range, format, value, 'user'); | |
} | |
return _.defer((function(_this) { | |
return function() { | |
_this.updateActive(range, ['bullet', 'list']); | |
return _this.setActive(format, value); | |
}; | |
})(this)); | |
}; | |
Toolbar.prototype._getActive = function(range) { | |
var leafFormats, lineFormats; | |
leafFormats = this._getLeafActive(range); | |
lineFormats = this._getLineActive(range); | |
return _.defaults({}, leafFormats, lineFormats); | |
}; | |
Toolbar.prototype._getLeafActive = function(range) { | |
var contents, formatsArr, line, offset, ref; | |
if (range.isCollapsed()) { | |
ref = this.quill.editor.doc.findLineAt(range.start), line = ref[0], offset = ref[1]; | |
if (offset === 0) { | |
contents = this.quill.getContents(range.start, range.end + 1); | |
} else { | |
contents = this.quill.getContents(range.start - 1, range.end); | |
} | |
} else { | |
contents = this.quill.getContents(range); | |
} | |
formatsArr = _.map(contents.ops, 'attributes'); | |
return this._intersectFormats(formatsArr); | |
}; | |
Toolbar.prototype._getLineActive = function(range) { | |
var firstLine, formatsArr, lastLine, offset, ref, ref1; | |
formatsArr = []; | |
ref = this.quill.editor.doc.findLineAt(range.start), firstLine = ref[0], offset = ref[1]; | |
ref1 = this.quill.editor.doc.findLineAt(range.end), lastLine = ref1[0], offset = ref1[1]; | |
if ((lastLine != null) && lastLine === firstLine) { | |
lastLine = lastLine.next; | |
} | |
while ((firstLine != null) && firstLine !== lastLine) { | |
formatsArr.push(_.clone(firstLine.formats)); | |
firstLine = firstLine.next; | |
} | |
return this._intersectFormats(formatsArr); | |
}; | |
Toolbar.prototype._intersectFormats = function(formatsArr) { | |
return _.reduce(formatsArr.slice(1), function(activeFormats, formats) { | |
var activeKeys, added, formatKeys, intersection, missing; | |
if (formats == null) { | |
formats = {}; | |
} | |
activeKeys = Object.keys(activeFormats); | |
formatKeys = formats != null ? Object.keys(formats) : {}; | |
intersection = _.intersection(activeKeys, formatKeys); | |
missing = _.difference(activeKeys, formatKeys); | |
added = _.difference(formatKeys, activeKeys); | |
_.each(intersection, function(name) { | |
if (Toolbar.formats.SELECT[name] != null) { | |
if (Array.isArray(activeFormats[name])) { | |
if (activeFormats[name].indexOf(formats[name]) < 0) { | |
return activeFormats[name].push(formats[name]); | |
} | |
} else if (activeFormats[name] !== formats[name]) { | |
return activeFormats[name] = [activeFormats[name], formats[name]]; | |
} | |
} | |
}); | |
_.each(missing, function(name) { | |
if (Toolbar.formats.TOGGLE[name] != null) { | |
return delete activeFormats[name]; | |
} else if ((Toolbar.formats.SELECT[name] != null) && !Array.isArray(activeFormats[name])) { | |
return activeFormats[name] = [activeFormats[name]]; | |
} | |
}); | |
_.each(added, function(name) { | |
if (Toolbar.formats.SELECT[name] != null) { | |
return activeFormats[name] = [formats[name]]; | |
} | |
}); | |
return activeFormats; | |
}, formatsArr[0] || {}); | |
}; | |
return Toolbar; | |
})(); | |
Quill.registerModule('toolbar', Toolbar); | |
module.exports = Toolbar; | |
},{"../quill":30}],28:[function(_dereq_,module,exports){ | |
var Quill, Tooltip, _, dom; | |
Quill = _dereq_('../quill'); | |
_ = Quill.require('lodash'); | |
dom = Quill.require('dom'); | |
Tooltip = (function() { | |
Tooltip.DEFAULTS = { | |
offset: 10, | |
template: '' | |
}; | |
Tooltip.HIDE_MARGIN = '-10000px'; | |
function Tooltip(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
this.container = this.quill.addContainer('ql-tooltip'); | |
this.container.innerHTML = this.options.template; | |
this.hide(); | |
this.quill.on(this.quill.constructor.events.TEXT_CHANGE, (function(_this) { | |
return function(delta, source) { | |
if (_this.container.style.left !== Tooltip.HIDE_MARGIN) { | |
_this.range = null; | |
return _this.hide(); | |
} | |
}; | |
})(this)); | |
} | |
Tooltip.prototype.initTextbox = function(textbox, enterCallback, escapeCallback) { | |
return dom(textbox).on('keydown', (function(_this) { | |
return function(event) { | |
switch (event.which) { | |
case dom.KEYS.ENTER: | |
event.preventDefault(); | |
return enterCallback.call(_this); | |
case dom.KEYS.ESCAPE: | |
event.preventDefault(); | |
return escapeCallback.call(_this); | |
default: | |
return true; | |
} | |
}; | |
})(this)); | |
}; | |
Tooltip.prototype.hide = function() { | |
this.container.style.left = Tooltip.HIDE_MARGIN; | |
if (this.range) { | |
this.quill.setSelection(this.range); | |
} | |
return this.range = null; | |
}; | |
Tooltip.prototype.position = function(reference) { | |
var left, offsetBottom, offsetLeft, offsetTop, parentBounds, referenceBounds, top; | |
if (reference != null) { | |
referenceBounds = reference.getBoundingClientRect(); | |
parentBounds = this.quill.container.getBoundingClientRect(); | |
offsetLeft = referenceBounds.left - parentBounds.left; | |
offsetTop = referenceBounds.top - parentBounds.top; | |
offsetBottom = referenceBounds.bottom - parentBounds.bottom; | |
left = offsetLeft + referenceBounds.width / 2 - this.container.offsetWidth / 2; | |
top = offsetTop + referenceBounds.height + this.options.offset; | |
if (top + this.container.offsetHeight > this.quill.container.offsetHeight) { | |
top = offsetTop - this.container.offsetHeight - this.options.offset; | |
} | |
left = Math.max(0, Math.min(left, this.quill.container.offsetWidth - this.container.offsetWidth)); | |
top = Math.max(0, Math.min(top, this.quill.container.offsetHeight - this.container.offsetHeight)); | |
} else { | |
left = this.quill.container.offsetWidth / 2 - this.container.offsetWidth / 2; | |
top = this.quill.container.offsetHeight / 2 - this.container.offsetHeight / 2; | |
} | |
top += this.quill.container.scrollTop; | |
return [left, top]; | |
}; | |
Tooltip.prototype.show = function(reference) { | |
var left, ref, top; | |
this.range = this.quill.getSelection(); | |
ref = this.position(reference), left = ref[0], top = ref[1]; | |
this.container.style.left = left + "px"; | |
this.container.style.top = top + "px"; | |
return this.container.focus(); | |
}; | |
return Tooltip; | |
})(); | |
Quill.registerModule('tooltip', Tooltip); | |
module.exports = Tooltip; | |
},{"../quill":30}],29:[function(_dereq_,module,exports){ | |
var Delta, Quill, UndoManager, _; | |
Quill = _dereq_('../quill'); | |
_ = Quill.require('lodash'); | |
Delta = Quill.require('delta'); | |
UndoManager = (function() { | |
UndoManager.DEFAULTS = { | |
delay: 1000, | |
maxStack: 100 | |
}; | |
UndoManager.hotkeys = { | |
UNDO: { | |
key: 'Z', | |
metaKey: true | |
}, | |
REDO: { | |
key: 'Z', | |
metaKey: true, | |
shiftKey: true | |
} | |
}; | |
function UndoManager(quill, options) { | |
this.quill = quill; | |
this.options = options != null ? options : {}; | |
this.lastRecorded = 0; | |
this.ignoreChange = false; | |
this.clear(); | |
this.initListeners(); | |
} | |
UndoManager.prototype.initListeners = function() { | |
this.quill.onModuleLoad('keyboard', (function(_this) { | |
return function(keyboard) { | |
keyboard.addHotkey(UndoManager.hotkeys.UNDO, function() { | |
_this.quill.editor.checkUpdate(); | |
_this.undo(); | |
return false; | |
}); | |
return keyboard.addHotkey(UndoManager.hotkeys.REDO, function() { | |
_this.quill.editor.checkUpdate(); | |
_this.redo(); | |
return false; | |
}); | |
}; | |
})(this)); | |
return this.quill.on(this.quill.constructor.events.TEXT_CHANGE, (function(_this) { | |
return function(delta, origin) { | |
if (_this.ignoreChange) { | |
return; | |
} | |
_this.record(delta, _this.oldDelta); | |
return _this.oldDelta = _this.quill.getContents(); | |
}; | |
})(this)); | |
}; | |
UndoManager.prototype.clear = function() { | |
this.stack = { | |
undo: [], | |
redo: [] | |
}; | |
return this.oldDelta = this.quill.getContents(); | |
}; | |
UndoManager.prototype.record = function(changeDelta, oldDelta) { | |
var change, ignored, timestamp, undoDelta; | |
if (!(changeDelta.ops.length > 0)) { | |
return; | |
} | |
this.stack.redo = []; | |
try { | |
undoDelta = this.quill.getContents().diff(this.oldDelta); | |
timestamp = new Date().getTime(); | |
if (this.lastRecorded + this.options.delay > timestamp && this.stack.undo.length > 0) { | |
change = this.stack.undo.pop(); | |
undoDelta = undoDelta.compose(change.undo); | |
changeDelta = change.redo.compose(changeDelta); | |
} else { | |
this.lastRecorded = timestamp; | |
} | |
this.stack.undo.push({ | |
redo: changeDelta, | |
undo: undoDelta | |
}); | |
if (this.stack.undo.length > this.options.maxStack) { | |
return this.stack.undo.unshift(); | |
} | |
} catch (_error) { | |
ignored = _error; | |
console.warn('Could not record change... clearing undo stack.'); | |
return this.clear(); | |
} | |
}; | |
UndoManager.prototype.redo = function() { | |
return this._change('redo', 'undo'); | |
}; | |
UndoManager.prototype.undo = function() { | |
return this._change('undo', 'redo'); | |
}; | |
UndoManager.prototype._getLastChangeIndex = function(delta) { | |
var index, lastIndex; | |
lastIndex = 0; | |
index = 0; | |
_.each(delta.ops, function(op) { | |
if (op.insert != null) { | |
return lastIndex = Math.max(index + (op.insert.length || 1), lastIndex); | |
} else if (op["delete"] != null) { | |
return lastIndex = Math.max(index, lastIndex); | |
} else if (op.retain != null) { | |
if (op.attributes != null) { | |
lastIndex = Math.max(index + op.retain, lastIndex); | |
} | |
return index += op.retain; | |
} | |
}); | |
return lastIndex; | |
}; | |
UndoManager.prototype._change = function(source, dest) { | |
var change, index; | |
if (this.stack[source].length > 0) { | |
change = this.stack[source].pop(); | |
this.lastRecorded = 0; | |
this.ignoreChange = true; | |
this.quill.updateContents(change[source], 'user'); | |
this.ignoreChange = false; | |
index = this._getLastChangeIndex(change[source]); | |
this.quill.setSelection(index, index); | |
this.oldDelta = this.quill.getContents(); | |
return this.stack[dest].push(change); | |
} | |
}; | |
return UndoManager; | |
})(); | |
Quill.registerModule('undo-manager', UndoManager); | |
module.exports = UndoManager; | |
},{"../quill":30}],30:[function(_dereq_,module,exports){ | |
var Delta, Editor, EventEmitter2, Format, Normalizer, Quill, Range, _, dom, pkg, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty, | |
slice = [].slice; | |
_ = _dereq_('lodash'); | |
pkg = _dereq_('../package.json'); | |
Delta = _dereq_('rich-text/lib/delta'); | |
EventEmitter2 = _dereq_('eventemitter2').EventEmitter2; | |
dom = _dereq_('./lib/dom'); | |
Editor = _dereq_('./core/editor'); | |
Format = _dereq_('./core/format'); | |
Normalizer = _dereq_('./core/normalizer'); | |
Range = _dereq_('./lib/range'); | |
Quill = (function(superClass) { | |
extend(Quill, superClass); | |
Quill.version = pkg.version; | |
Quill.editors = []; | |
Quill.modules = []; | |
Quill.themes = []; | |
Quill.DEFAULTS = { | |
formats: ['align', 'bold', 'italic', 'strike', 'underline', 'color', 'background', 'font', 'size', 'link', 'image', 'bullet', 'list'], | |
modules: { | |
'keyboard': true, | |
'paste-manager': true, | |
'undo-manager': true | |
}, | |
pollInterval: 100, | |
readOnly: false, | |
styles: {}, | |
theme: 'base' | |
}; | |
Quill.events = { | |
FORMAT_INIT: 'format-init', | |
MODULE_INIT: 'module-init', | |
POST_EVENT: 'post-event', | |
PRE_EVENT: 'pre-event', | |
SELECTION_CHANGE: 'selection-change', | |
TEXT_CHANGE: 'text-change' | |
}; | |
Quill.sources = Editor.sources; | |
Quill.registerModule = function(name, module) { | |
if (Quill.modules[name] != null) { | |
console.warn("Overwriting " + name + " module"); | |
} | |
return Quill.modules[name] = module; | |
}; | |
Quill.registerTheme = function(name, theme) { | |
if (Quill.themes[name] != null) { | |
console.warn("Overwriting " + name + " theme"); | |
} | |
return Quill.themes[name] = theme; | |
}; | |
Quill.require = function(name) { | |
switch (name) { | |
case 'lodash': | |
return _; | |
case 'delta': | |
return Delta; | |
case 'format': | |
return Format; | |
case 'normalizer': | |
return Normalizer; | |
case 'dom': | |
return dom; | |
case 'range': | |
return Range; | |
default: | |
return null; | |
} | |
}; | |
function Quill(container1, options) { | |
var html, moduleOptions, themeClass; | |
this.container = container1; | |
if (options == null) { | |
options = {}; | |
} | |
if (_.isString(this.container)) { | |
this.container = document.querySelector(this.container); | |
} | |
if (this.container == null) { | |
throw new Error('Invalid Quill container'); | |
} | |
moduleOptions = _.defaults(options.modules || {}, Quill.DEFAULTS.modules); | |
html = this.container.innerHTML; | |
this.container.innerHTML = ''; | |
this.options = _.defaults(options, Quill.DEFAULTS); | |
this.options.modules = moduleOptions; | |
this.options.id = this.id = "ql-editor-" + (Quill.editors.length + 1); | |
this.modules = {}; | |
this.root = this.addContainer('ql-editor'); | |
this.editor = new Editor(this.root, this, this.options); | |
Quill.editors.push(this); | |
this.setHTML(html, Quill.sources.SILENT); | |
themeClass = Quill.themes[this.options.theme]; | |
if (themeClass == null) { | |
throw new Error("Cannot load " + this.options.theme + " theme. Are you sure you registered it?"); | |
} | |
this.theme = new themeClass(this, this.options); | |
_.each(this.options.modules, (function(_this) { | |
return function(option, name) { | |
return _this.addModule(name, option); | |
}; | |
})(this)); | |
} | |
Quill.prototype.destroy = function() { | |
var html; | |
html = this.getHTML(); | |
_.each(this.modules, function(module, name) { | |
if (_.isFunction(module.destroy)) { | |
return module.destroy(); | |
} | |
}); | |
this.editor.destroy(); | |
this.removeAllListeners(); | |
Quill.editors.splice(_.indexOf(Quill.editors, this), 1); | |
return this.container.innerHTML = html; | |
}; | |
Quill.prototype.addContainer = function(className, before) { | |
var container, refNode; | |
if (before == null) { | |
before = false; | |
} | |
refNode = before ? this.root : null; | |
container = document.createElement('div'); | |
dom(container).addClass(className); | |
this.container.insertBefore(container, refNode); | |
return container; | |
}; | |
Quill.prototype.addFormat = function(name, config) { | |
this.editor.doc.addFormat(name, config); | |
return this.emit(Quill.events.FORMAT_INIT, name); | |
}; | |
Quill.prototype.addModule = function(name, options) { | |
var moduleClass; | |
moduleClass = Quill.modules[name]; | |
if (moduleClass == null) { | |
throw new Error("Cannot load " + name + " module. Are you sure you registered it?"); | |
} | |
if (options === true) { | |
options = {}; | |
} | |
options = _.defaults(options, this.theme.constructor.OPTIONS[name] || {}, moduleClass.DEFAULTS || {}); | |
this.modules[name] = new moduleClass(this, options); | |
this.emit(Quill.events.MODULE_INIT, name, this.modules[name]); | |
return this.modules[name]; | |
}; | |
Quill.prototype.deleteText = function(start, end, source) { | |
var delta, formats, ref; | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
ref = this._buildParams(start, end, {}, source), start = ref[0], end = ref[1], formats = ref[2], source = ref[3]; | |
if (!(end > start)) { | |
return; | |
} | |
delta = new Delta().retain(start)["delete"](end - start); | |
return this.editor.applyDelta(delta, source); | |
}; | |
Quill.prototype.emit = function() { | |
var args, eventName; | |
eventName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; | |
Quill.__super__.emit.apply(this, [Quill.events.PRE_EVENT, eventName].concat(slice.call(args))); | |
Quill.__super__.emit.apply(this, [eventName].concat(slice.call(args))); | |
return Quill.__super__.emit.apply(this, [Quill.events.POST_EVENT, eventName].concat(slice.call(args))); | |
}; | |
Quill.prototype.focus = function() { | |
return this.editor.focus(); | |
}; | |
Quill.prototype.formatLine = function(start, end, name, value, source) { | |
var formats, line, offset, ref, ref1; | |
ref = this._buildParams(start, end, name, value, source), start = ref[0], end = ref[1], formats = ref[2], source = ref[3]; | |
ref1 = this.editor.doc.findLineAt(end), line = ref1[0], offset = ref1[1]; | |
if (line != null) { | |
end += line.length - offset; | |
} | |
return this.formatText(start, end, formats, source); | |
}; | |
Quill.prototype.formatText = function(start, end, name, value, source) { | |
var delta, formats, ref; | |
ref = this._buildParams(start, end, name, value, source), start = ref[0], end = ref[1], formats = ref[2], source = ref[3]; | |
formats = _.reduce(formats, (function(_this) { | |
return function(formats, value, name) { | |
var format; | |
format = _this.editor.doc.formats[name]; | |
if (!(value && value !== format.config["default"])) { | |
formats[name] = null; | |
} | |
return formats; | |
}; | |
})(this), formats); | |
delta = new Delta().retain(start).retain(end - start, formats); | |
return this.editor.applyDelta(delta, source); | |
}; | |
Quill.prototype.getBounds = function(index) { | |
return this.editor.getBounds(index); | |
}; | |
Quill.prototype.getContents = function(start, end) { | |
if (start == null) { | |
start = 0; | |
} | |
if (end == null) { | |
end = null; | |
} | |
if (_.isObject(start)) { | |
end = start.end; | |
start = start.start; | |
} | |
return this.editor.delta.slice(start, end); | |
}; | |
Quill.prototype.getHTML = function() { | |
return this.editor.doc.getHTML(); | |
}; | |
Quill.prototype.getLength = function() { | |
return this.editor.length; | |
}; | |
Quill.prototype.getModule = function(name) { | |
return this.modules[name]; | |
}; | |
Quill.prototype.getSelection = function() { | |
this.editor.checkUpdate(); | |
return this.editor.selection.getRange(); | |
}; | |
Quill.prototype.getText = function(start, end) { | |
if (start == null) { | |
start = 0; | |
} | |
if (end == null) { | |
end = null; | |
} | |
return _.map(this.getContents(start, end).ops, function(op) { | |
if (_.isString(op.insert)) { | |
return op.insert; | |
} else { | |
return ''; | |
} | |
}).join(''); | |
}; | |
Quill.prototype.insertEmbed = function(index, type, url, source) { | |
var delta, end, formats, ref; | |
ref = this._buildParams(index, 0, type, url, source), index = ref[0], end = ref[1], formats = ref[2], source = ref[3]; | |
delta = new Delta().retain(index).insert(1, formats); | |
return this.editor.applyDelta(delta, source); | |
}; | |
Quill.prototype.insertText = function(index, text, name, value, source) { | |
var delta, end, formats, ref; | |
ref = this._buildParams(index, 0, name, value, source), index = ref[0], end = ref[1], formats = ref[2], source = ref[3]; | |
if (!(text.length > 0)) { | |
return; | |
} | |
delta = new Delta().retain(index).insert(text, formats); | |
return this.editor.applyDelta(delta, source); | |
}; | |
Quill.prototype.onModuleLoad = function(name, callback) { | |
if (this.modules[name]) { | |
return callback(this.modules[name]); | |
} | |
return this.on(Quill.events.MODULE_INIT, function(moduleName, module) { | |
if (moduleName === name) { | |
return callback(module); | |
} | |
}); | |
}; | |
Quill.prototype.prepareFormat = function(name, value, source) { | |
var format, range; | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
format = this.editor.doc.formats[name]; | |
if (format == null) { | |
return; | |
} | |
range = this.getSelection(); | |
if (!(range != null ? range.isCollapsed() : void 0)) { | |
return; | |
} | |
if (format.isType(Format.types.LINE)) { | |
return this.formatLine(range, name, value, source); | |
} else { | |
return format.prepare(value); | |
} | |
}; | |
Quill.prototype.setContents = function(delta, source) { | |
var lastOp; | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
if (Array.isArray(delta)) { | |
delta = new Delta(delta.slice()); | |
} else { | |
delta = new Delta(delta.ops.slice()); | |
} | |
lastOp = _.last(delta.slice(delta.length() - 1).ops); | |
delta["delete"](this.getLength() - 1); | |
if ((lastOp != null) && _.isString(lastOp.insert) && _.last(lastOp.insert) === '\n') { | |
delta["delete"](1); | |
} | |
return this.updateContents(delta, source); | |
}; | |
Quill.prototype.setHTML = function(html, source) { | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
if (!html.trim()) { | |
html = "<" + dom.DEFAULT_BLOCK_TAG + "><" + dom.DEFAULT_BREAK_TAG + "></" + dom.DEFAULT_BLOCK_TAG + ">"; | |
} | |
this.editor.doc.setHTML(html); | |
return this.editor.checkUpdate(source); | |
}; | |
Quill.prototype.setSelection = function(start, end, source) { | |
var range; | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
if (_.isNumber(start) && _.isNumber(end)) { | |
range = new Range(start, end); | |
} else { | |
range = start; | |
source = end || source; | |
} | |
return this.editor.selection.setRange(range, source); | |
}; | |
Quill.prototype.setText = function(text, source) { | |
var delta; | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
delta = new Delta().insert(text); | |
return this.setContents(delta, source); | |
}; | |
Quill.prototype.updateContents = function(delta, source) { | |
if (source == null) { | |
source = Quill.sources.API; | |
} | |
if (Array.isArray(delta)) { | |
delta = { | |
ops: delta | |
}; | |
} | |
return this.editor.applyDelta(delta, source); | |
}; | |
Quill.prototype._buildParams = function() { | |
var formats, params; | |
params = 1 <= arguments.length ? slice.call(arguments, 0) : []; | |
if (_.isObject(params[0])) { | |
params.splice(0, 1, params[0].start, params[0].end); | |
} | |
if (_.isString(params[2])) { | |
formats = {}; | |
formats[params[2]] = params[3]; | |
params.splice(2, 2, formats); | |
} | |
if (params[3] == null) { | |
params[3] = Quill.sources.API; | |
} | |
return params; | |
}; | |
return Quill; | |
})(EventEmitter2); | |
Quill.registerTheme('base', _dereq_('./themes/base')); | |
Quill.registerTheme('snow', _dereq_('./themes/snow')); | |
module.exports = Quill; | |
},{"../package.json":7,"./core/editor":9,"./core/format":10,"./core/normalizer":13,"./lib/dom":17,"./lib/range":20,"./themes/base":32,"./themes/snow":33,"eventemitter2":2,"lodash":1,"rich-text/lib/delta":3}],31:[function(_dereq_,module,exports){ | |
module.exports = ".ql-image-tooltip{padding:10px;width:300px}.ql-image-tooltip:after{clear:both;content:\"\";display:table}.ql-image-tooltip a{border:1px solid #000;box-sizing:border-box;display:inline-block;float:left;padding:5px;text-align:center;width:50%}.ql-image-tooltip img{bottom:0;left:0;margin:auto;max-height:100%;max-width:100%;position:absolute;right:0;top:0}.ql-image-tooltip .input{box-sizing:border-box;width:100%}.ql-image-tooltip .preview{margin:10px 0;position:relative;border:1px dashed #000;height:200px}.ql-image-tooltip .preview span{display:inline-block;position:absolute;text-align:center;top:40%;width:100%}.ql-link-tooltip{padding:5px 10px}.ql-link-tooltip input.input{width:170px}.ql-link-tooltip a.done,.ql-link-tooltip input.input{display:none}.ql-link-tooltip a.change{margin-right:4px}.ql-link-tooltip.editing a.done,.ql-link-tooltip.editing input.input{display:inline-block}.ql-link-tooltip.editing a.change,.ql-link-tooltip.editing a.remove,.ql-link-tooltip.editing a.url{display:none}.ql-multi-cursor{position:absolute;left:0;top:0;z-index:1000}.ql-multi-cursor .cursor{margin-left:-1px;position:absolute}.ql-multi-cursor .cursor-flag{bottom:100%;position:absolute;white-space:nowrap}.ql-multi-cursor .cursor-name{display:inline-block;color:#fff;padding:2px 8px}.ql-multi-cursor .cursor-caret{height:100%;position:absolute;width:2px}.ql-multi-cursor .cursor.hidden .cursor-flag{display:none}.ql-multi-cursor .cursor.top .cursor-flag{bottom:auto;top:100%}.ql-multi-cursor .cursor.right .cursor-flag{right:-2px}.ql-paste-manager{left:-100000px;position:absolute;top:50%}.ql-toolbar{box-sizing:border-box}.ql-tooltip{background-color:#fff;border:1px solid #000;box-sizing:border-box;position:absolute;top:0;white-space:nowrap;z-index:2000}.ql-tooltip a{cursor:pointer;text-decoration:none}.ql-container{box-sizing:border-box;cursor:text;font-family:Helvetica,Arial,sans-serif;font-size:13px;height:100%;line-height:1.42;margin:0;overflow-x:hidden;overflow-y:auto;padding:12px 15px;position:relative}.ql-editor{box-sizing:border-box;min-height:100%;outline:0;tab-size:4;white-space:pre-wrap}.ql-editor div{margin:0;padding:0}.ql-editor a{text-decoration:underline}.ql-editor b{font-weight:700}.ql-editor i{font-style:italic}.ql-editor s{text-decoration:line-through}.ql-editor u{text-decoration:underline}.ql-editor a,.ql-editor b,.ql-editor i,.ql-editor s,.ql-editor span,.ql-editor u{background-color:inherit}.ql-editor img{max-width:100%}.ql-editor blockquote,.ql-editor ol,.ql-editor ul{margin:0 0 0 2em;padding:0}.ql-editor ol{list-style-type:decimal}.ql-editor ul{list-style-type:disc}.ql-editor.ql-ie-10 br,.ql-editor.ql-ie-9 br{display:none}"; | |
},{}],32:[function(_dereq_,module,exports){ | |
var BaseTheme, _, baseStyles, dom; | |
_ = _dereq_('lodash'); | |
dom = _dereq_('../../lib/dom'); | |
baseStyles = _dereq_('./base.styl'); | |
BaseTheme = (function() { | |
BaseTheme.OPTIONS = {}; | |
BaseTheme.objToCss = function(obj) { | |
return _.map(obj, function(value, key) { | |
var innerStr; | |
innerStr = _.map(value, function(innerValue, innerKey) { | |
return innerKey + ": " + innerValue + ";"; | |
}).join(' '); | |
return key + " { " + innerStr + " }"; | |
}).join("\n"); | |
}; | |
function BaseTheme(quill, options) { | |
var version; | |
this.quill = quill; | |
this.options = options; | |
dom(this.quill.container).addClass('ql-container'); | |
if (this.options.styles) { | |
this.addStyles(baseStyles + BaseTheme.objToCss(this.options.styles)); | |
} | |
if (dom.isIE(10)) { | |
version = dom.isIE(9) ? '9' : '10'; | |
dom(this.quill.root).addClass('ql-ie-' + version); | |
} | |
} | |
BaseTheme.prototype.addStyles = function(css) { | |
var style; | |
if (_.isObject(css)) { | |
css = BaseTheme.objToCss(css); | |
} | |
style = document.createElement('style'); | |
style.type = 'text/css'; | |
style.appendChild(document.createTextNode(css)); | |
return document.head.appendChild(style); | |
}; | |
return BaseTheme; | |
})(); | |
module.exports = BaseTheme; | |
},{"../../lib/dom":17,"./base.styl":31,"lodash":1}],33:[function(_dereq_,module,exports){ | |
var BaseTheme, ColorPicker, Picker, SnowTheme, _, dom, | |
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | |
hasProp = {}.hasOwnProperty; | |
_ = _dereq_('lodash'); | |
ColorPicker = _dereq_('../../lib/color-picker'); | |
BaseTheme = _dereq_('../base'); | |
dom = _dereq_('../../lib/dom'); | |
Picker = _dereq_('../../lib/picker'); | |
SnowTheme = (function(superClass) { | |
extend(SnowTheme, superClass); | |
SnowTheme.COLORS = ["#000000", "#e60000", "#ff9900", "#ffff00", "#008A00", "#0066cc", "#9933ff", "#ffffff", "#facccc", "#ffebcc", "#ffffcc", "#cce8cc", "#cce0f5", "#ebd6ff", "#bbbbbb", "#f06666", "#ffc266", "#ffff66", "#66b966", "#66a3e0", "#c285ff", "#888888", "#a10000", "#b26b00", "#b2b200", "#006100", "#0047b2", "#6b24b2", "#444444", "#5c0000", "#663d00", "#666600", "#003700", "#002966", "#3d1466"]; | |
SnowTheme.OPTIONS = { | |
'multi-cursor': { | |
template: '<span class="cursor-flag"> <span class="cursor-triangle top"></span> <span class="cursor-name"></span> <span class="cursor-triangle bottom"></span> </span> <span class="cursor-caret"></span>' | |
} | |
}; | |
function SnowTheme(quill, options) { | |
this.quill = quill; | |
this.options = options; | |
SnowTheme.__super__.constructor.apply(this, arguments); | |
dom(this.quill.container).addClass('ql-snow'); | |
this.pickers = []; | |
this.quill.on(this.quill.constructor.events.SELECTION_CHANGE, (function(_this) { | |
return function(range) { | |
if (range != null) { | |
return _.invoke(_this.pickers, 'close'); | |
} | |
}; | |
})(this)); | |
this.quill.onModuleLoad('multi-cursor', _.bind(this.extendMultiCursor, this)); | |
this.quill.onModuleLoad('toolbar', _.bind(this.extendToolbar, this)); | |
} | |
SnowTheme.prototype.extendMultiCursor = function(module) { | |
return module.on(module.constructor.events.CURSOR_ADDED, function(cursor) { | |
var bottomTriangle, topTriangle; | |
bottomTriangle = cursor.elem.querySelector('.cursor-triangle.bottom'); | |
topTriangle = cursor.elem.querySelector('.cursor-triangle.top'); | |
return bottomTriangle.style.borderTopColor = topTriangle.style.borderBottomColor = cursor.color; | |
}); | |
}; | |
SnowTheme.prototype.extendToolbar = function(module) { | |
dom(module.container).addClass('ql-snow'); | |
_.each(['color', 'background', 'font', 'size', 'align'], (function(_this) { | |
return function(format) { | |
var picker, select; | |
select = module.container.querySelector(".ql-" + format); | |
if (select == null) { | |
return; | |
} | |
switch (format) { | |
case 'font': | |
case 'size': | |
case 'align': | |
picker = new Picker(select); | |
break; | |
case 'color': | |
case 'background': | |
picker = new ColorPicker(select); | |
_.each(picker.container.querySelectorAll('.ql-picker-item'), function(item, i) { | |
if (i < 7) { | |
return dom(item).addClass('ql-primary-color'); | |
} | |
}); | |
} | |
if (picker != null) { | |
return _this.pickers.push(picker); | |
} | |
}; | |
})(this)); | |
return _.each(dom(module.container).textNodes(), function(node) { | |
if (dom(node).text().trim().length === 0) { | |
return dom(node).remove(); | |
} | |
}); | |
}; | |
return SnowTheme; | |
})(BaseTheme); | |
module.exports = SnowTheme; | |
},{"../../lib/color-picker":16,"../../lib/dom":17,"../../lib/picker":19,"../base":32,"lodash":1}]},{},[15])(15) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment