mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-18 00:25:07 +00:00
21407 lines
637 KiB
JavaScript
21407 lines
637 KiB
JavaScript
/** vim: et:ts=4:sw=4:sts=4
|
|
* @license RequireJS 2.1.10 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved.
|
|
* Available via the MIT or new BSD license.
|
|
* see: http://github.com/jrburke/requirejs for details
|
|
*/
|
|
//Not using strict: uneven strict support in browsers, #392, and causes
|
|
//problems with requirejs.exec()/transpiler plugins that may not be strict.
|
|
/*jslint regexp: true, nomen: true, sloppy: true */
|
|
/*global window, navigator, document, importScripts, setTimeout, opera */
|
|
|
|
var requirejs, require, define;
|
|
(function (global) {
|
|
var req, s, head, baseElement, dataMain, src,
|
|
interactiveScript, currentlyAddingScript, mainScript, subPath,
|
|
version = '2.1.10',
|
|
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
|
|
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
|
|
jsSuffixRegExp = /\.js$/,
|
|
currDirRegExp = /^\.\//,
|
|
op = Object.prototype,
|
|
ostring = op.toString,
|
|
hasOwn = op.hasOwnProperty,
|
|
ap = Array.prototype,
|
|
apsp = ap.splice,
|
|
isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document),
|
|
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
|
|
//PS3 indicates loaded and complete, but need to wait for complete
|
|
//specifically. Sequence is 'loading', 'loaded', execution,
|
|
// then 'complete'. The UA check is unfortunate, but not sure how
|
|
//to feature test w/o causing perf issues.
|
|
readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ?
|
|
/^complete$/ : /^(complete|loaded)$/,
|
|
defContextName = '_',
|
|
//Oh the tragedy, detecting opera. See the usage of isOpera for reason.
|
|
isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]',
|
|
contexts = {},
|
|
cfg = {},
|
|
globalDefQueue = [],
|
|
useInteractive = false;
|
|
|
|
function isFunction(it) {
|
|
return ostring.call(it) === '[object Function]';
|
|
}
|
|
|
|
function isArray(it) {
|
|
return ostring.call(it) === '[object Array]';
|
|
}
|
|
|
|
/**
|
|
* Helper function for iterating over an array. If the func returns
|
|
* a true value, it will break out of the loop.
|
|
*/
|
|
function each(ary, func) {
|
|
if (ary) {
|
|
var i;
|
|
for (i = 0; i < ary.length; i += 1) {
|
|
if (ary[i] && func(ary[i], i, ary)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper function for iterating over an array backwards. If the func
|
|
* returns a true value, it will break out of the loop.
|
|
*/
|
|
function eachReverse(ary, func) {
|
|
if (ary) {
|
|
var i;
|
|
for (i = ary.length - 1; i > -1; i -= 1) {
|
|
if (ary[i] && func(ary[i], i, ary)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function hasProp(obj, prop) {
|
|
return hasOwn.call(obj, prop);
|
|
}
|
|
|
|
function getOwn(obj, prop) {
|
|
return hasProp(obj, prop) && obj[prop];
|
|
}
|
|
|
|
/**
|
|
* Cycles over properties in an object and calls a function for each
|
|
* property value. If the function returns a truthy value, then the
|
|
* iteration is stopped.
|
|
*/
|
|
function eachProp(obj, func) {
|
|
var prop;
|
|
for (prop in obj) {
|
|
if (hasProp(obj, prop)) {
|
|
if (func(obj[prop], prop)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Simple function to mix in properties from source into target,
|
|
* but only if target does not already have a property of the same name.
|
|
*/
|
|
function mixin(target, source, force, deepStringMixin) {
|
|
if (source) {
|
|
eachProp(source, function (value, prop) {
|
|
if (force || !hasProp(target, prop)) {
|
|
if (deepStringMixin && typeof value === 'object' && value &&
|
|
!isArray(value) && !isFunction(value) &&
|
|
!(value instanceof RegExp)) {
|
|
|
|
if (!target[prop]) {
|
|
target[prop] = {};
|
|
}
|
|
mixin(target[prop], value, force, deepStringMixin);
|
|
} else {
|
|
target[prop] = value;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return target;
|
|
}
|
|
|
|
//Similar to Function.prototype.bind, but the 'this' object is specified
|
|
//first, since it is easier to read/figure out what 'this' will be.
|
|
function bind(obj, fn) {
|
|
return function () {
|
|
return fn.apply(obj, arguments);
|
|
};
|
|
}
|
|
|
|
function scripts() {
|
|
return document.getElementsByTagName('script');
|
|
}
|
|
|
|
function defaultOnError(err) {
|
|
throw err;
|
|
}
|
|
|
|
//Allow getting a global that expressed in
|
|
//dot notation, like 'a.b.c'.
|
|
function getGlobal(value) {
|
|
if (!value) {
|
|
return value;
|
|
}
|
|
var g = global;
|
|
each(value.split('.'), function (part) {
|
|
g = g[part];
|
|
});
|
|
return g;
|
|
}
|
|
|
|
/**
|
|
* Constructs an error with a pointer to an URL with more information.
|
|
* @param {String} id the error ID that maps to an ID on a web page.
|
|
* @param {String} message human readable error.
|
|
* @param {Error} [err] the original error, if there is one.
|
|
*
|
|
* @returns {Error}
|
|
*/
|
|
function makeError(id, msg, err, requireModules) {
|
|
var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);
|
|
e.requireType = id;
|
|
e.requireModules = requireModules;
|
|
if (err) {
|
|
e.originalError = err;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
if (typeof define !== 'undefined') {
|
|
//If a define is already in play via another AMD loader,
|
|
//do not overwrite.
|
|
return;
|
|
}
|
|
|
|
if (typeof requirejs !== 'undefined') {
|
|
if (isFunction(requirejs)) {
|
|
//Do not overwrite and existing requirejs instance.
|
|
return;
|
|
}
|
|
cfg = requirejs;
|
|
requirejs = undefined;
|
|
}
|
|
|
|
//Allow for a require config object
|
|
if (typeof require !== 'undefined' && !isFunction(require)) {
|
|
//assume it is a config object.
|
|
cfg = require;
|
|
require = undefined;
|
|
}
|
|
|
|
function newContext(contextName) {
|
|
var inCheckLoaded, Module, context, handlers,
|
|
checkLoadedTimeoutId,
|
|
config = {
|
|
//Defaults. Do not set a default for map
|
|
//config to speed up normalize(), which
|
|
//will run faster if there is no default.
|
|
waitSeconds: 7,
|
|
baseUrl: './',
|
|
paths: {},
|
|
bundles: {},
|
|
pkgs: {},
|
|
shim: {},
|
|
config: {}
|
|
},
|
|
registry = {},
|
|
//registry of just enabled modules, to speed
|
|
//cycle breaking code when lots of modules
|
|
//are registered, but not activated.
|
|
enabledRegistry = {},
|
|
undefEvents = {},
|
|
defQueue = [],
|
|
defined = {},
|
|
urlFetched = {},
|
|
bundlesMap = {},
|
|
requireCounter = 1,
|
|
unnormalizedCounter = 1;
|
|
|
|
/**
|
|
* Trims the . and .. from an array of path segments.
|
|
* It will keep a leading path segment if a .. will become
|
|
* the first path segment, to help with module name lookups,
|
|
* which act like paths, but can be remapped. But the end result,
|
|
* all paths that use this function should look normalized.
|
|
* NOTE: this method MODIFIES the input array.
|
|
* @param {Array} ary the array of path segments.
|
|
*/
|
|
function trimDots(ary) {
|
|
var i, part, length = ary.length;
|
|
for (i = 0; i < length; i++) {
|
|
part = ary[i];
|
|
if (part === '.') {
|
|
ary.splice(i, 1);
|
|
i -= 1;
|
|
} else if (part === '..') {
|
|
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
|
|
//End of the line. Keep at least one non-dot
|
|
//path segment at the front so it can be mapped
|
|
//correctly to disk. Otherwise, there is likely
|
|
//no path mapping for a path starting with '..'.
|
|
//This can still fail, but catches the most reasonable
|
|
//uses of ..
|
|
break;
|
|
} else if (i > 0) {
|
|
ary.splice(i - 1, 2);
|
|
i -= 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a relative module name, like ./something, normalize it to
|
|
* a real name that can be mapped to a path.
|
|
* @param {String} name the relative name
|
|
* @param {String} baseName a real name that the name arg is relative
|
|
* to.
|
|
* @param {Boolean} applyMap apply the map config to the value. Should
|
|
* only be done if this normalization is for a dependency ID.
|
|
* @returns {String} normalized name
|
|
*/
|
|
function normalize(name, baseName, applyMap) {
|
|
var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
|
|
foundMap, foundI, foundStarMap, starI,
|
|
baseParts = baseName && baseName.split('/'),
|
|
normalizedBaseParts = baseParts,
|
|
map = config.map,
|
|
starMap = map && map['*'];
|
|
|
|
//Adjust any relative paths.
|
|
if (name && name.charAt(0) === '.') {
|
|
//If have a base name, try to normalize against it,
|
|
//otherwise, assume it is a top-level require that will
|
|
//be relative to baseUrl in the end.
|
|
if (baseName) {
|
|
//Convert baseName to array, and lop off the last part,
|
|
//so that . matches that 'directory' and not name of the baseName's
|
|
//module. For instance, baseName of 'one/two/three', maps to
|
|
//'one/two/three.js', but we want the directory, 'one/two' for
|
|
//this normalization.
|
|
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
|
|
name = name.split('/');
|
|
lastIndex = name.length - 1;
|
|
|
|
// If wanting node ID compatibility, strip .js from end
|
|
// of IDs. Have to do this here, and not in nameToUrl
|
|
// because node allows either .js or non .js to map
|
|
// to same file.
|
|
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
|
|
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
|
|
}
|
|
|
|
name = normalizedBaseParts.concat(name);
|
|
trimDots(name);
|
|
name = name.join('/');
|
|
} else if (name.indexOf('./') === 0) {
|
|
// No baseName, so this is ID is resolved relative
|
|
// to baseUrl, pull off the leading dot.
|
|
name = name.substring(2);
|
|
}
|
|
}
|
|
|
|
//Apply map config if available.
|
|
if (applyMap && map && (baseParts || starMap)) {
|
|
nameParts = name.split('/');
|
|
|
|
outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
|
|
nameSegment = nameParts.slice(0, i).join('/');
|
|
|
|
if (baseParts) {
|
|
//Find the longest baseName segment match in the config.
|
|
//So, do joins on the biggest to smallest lengths of baseParts.
|
|
for (j = baseParts.length; j > 0; j -= 1) {
|
|
mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
|
|
|
|
//baseName segment has config, find if it has one for
|
|
//this name.
|
|
if (mapValue) {
|
|
mapValue = getOwn(mapValue, nameSegment);
|
|
if (mapValue) {
|
|
//Match, update name to the new value.
|
|
foundMap = mapValue;
|
|
foundI = i;
|
|
break outerLoop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check for a star map match, but just hold on to it,
|
|
//if there is a shorter segment match later in a matching
|
|
//config, then favor over this star map.
|
|
if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
|
|
foundStarMap = getOwn(starMap, nameSegment);
|
|
starI = i;
|
|
}
|
|
}
|
|
|
|
if (!foundMap && foundStarMap) {
|
|
foundMap = foundStarMap;
|
|
foundI = starI;
|
|
}
|
|
|
|
if (foundMap) {
|
|
nameParts.splice(0, foundI, foundMap);
|
|
name = nameParts.join('/');
|
|
}
|
|
}
|
|
|
|
// If the name points to a package's name, use
|
|
// the package main instead.
|
|
pkgMain = getOwn(config.pkgs, name);
|
|
|
|
return pkgMain ? pkgMain : name;
|
|
}
|
|
|
|
function removeScript(name) {
|
|
if (isBrowser) {
|
|
each(scripts(), function (scriptNode) {
|
|
if (scriptNode.getAttribute('data-requiremodule') === name &&
|
|
scriptNode.getAttribute('data-requirecontext') === context.contextName) {
|
|
scriptNode.parentNode.removeChild(scriptNode);
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function hasPathFallback(id) {
|
|
var pathConfig = getOwn(config.paths, id);
|
|
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) {
|
|
//Pop off the first array value, since it failed, and
|
|
//retry
|
|
pathConfig.shift();
|
|
context.require.undef(id);
|
|
context.require([id]);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//Turns a plugin!resource to [plugin, resource]
|
|
//with the plugin being undefined if the name
|
|
//did not have a plugin prefix.
|
|
function splitPrefix(name) {
|
|
var prefix,
|
|
index = name ? name.indexOf('!') : -1;
|
|
if (index > -1) {
|
|
prefix = name.substring(0, index);
|
|
name = name.substring(index + 1, name.length);
|
|
}
|
|
return [prefix, name];
|
|
}
|
|
|
|
/**
|
|
* Creates a module mapping that includes plugin prefix, module
|
|
* name, and path. If parentModuleMap is provided it will
|
|
* also normalize the name via require.normalize()
|
|
*
|
|
* @param {String} name the module name
|
|
* @param {String} [parentModuleMap] parent module map
|
|
* for the module name, used to resolve relative names.
|
|
* @param {Boolean} isNormalized: is the ID already normalized.
|
|
* This is true if this call is done for a define() module ID.
|
|
* @param {Boolean} applyMap: apply the map config to the ID.
|
|
* Should only be true if this map is for a dependency.
|
|
*
|
|
* @returns {Object}
|
|
*/
|
|
function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
|
|
var url, pluginModule, suffix, nameParts,
|
|
prefix = null,
|
|
parentName = parentModuleMap ? parentModuleMap.name : null,
|
|
originalName = name,
|
|
isDefine = true,
|
|
normalizedName = '';
|
|
|
|
//If no name, then it means it is a require call, generate an
|
|
//internal name.
|
|
if (!name) {
|
|
isDefine = false;
|
|
name = '_@r' + (requireCounter += 1);
|
|
}
|
|
|
|
nameParts = splitPrefix(name);
|
|
prefix = nameParts[0];
|
|
name = nameParts[1];
|
|
|
|
if (prefix) {
|
|
prefix = normalize(prefix, parentName, applyMap);
|
|
pluginModule = getOwn(defined, prefix);
|
|
}
|
|
|
|
//Account for relative paths if there is a base name.
|
|
if (name) {
|
|
if (prefix) {
|
|
if (pluginModule && pluginModule.normalize) {
|
|
//Plugin is loaded, use its normalize method.
|
|
normalizedName = pluginModule.normalize(name, function (name) {
|
|
return normalize(name, parentName, applyMap);
|
|
});
|
|
} else {
|
|
normalizedName = normalize(name, parentName, applyMap);
|
|
}
|
|
} else {
|
|
//A regular module.
|
|
normalizedName = normalize(name, parentName, applyMap);
|
|
|
|
//Normalized name may be a plugin ID due to map config
|
|
//application in normalize. The map config values must
|
|
//already be normalized, so do not need to redo that part.
|
|
nameParts = splitPrefix(normalizedName);
|
|
prefix = nameParts[0];
|
|
normalizedName = nameParts[1];
|
|
isNormalized = true;
|
|
|
|
url = context.nameToUrl(normalizedName);
|
|
}
|
|
}
|
|
|
|
//If the id is a plugin id that cannot be determined if it needs
|
|
//normalization, stamp it with a unique ID so two matching relative
|
|
//ids that may conflict can be separate.
|
|
suffix = prefix && !pluginModule && !isNormalized ?
|
|
'_unnormalized' + (unnormalizedCounter += 1) :
|
|
'';
|
|
|
|
return {
|
|
prefix: prefix,
|
|
name: normalizedName,
|
|
parentMap: parentModuleMap,
|
|
unnormalized: !!suffix,
|
|
url: url,
|
|
originalName: originalName,
|
|
isDefine: isDefine,
|
|
id: (prefix ?
|
|
prefix + '!' + normalizedName :
|
|
normalizedName) + suffix
|
|
};
|
|
}
|
|
|
|
function getModule(depMap) {
|
|
var id = depMap.id,
|
|
mod = getOwn(registry, id);
|
|
|
|
if (!mod) {
|
|
mod = registry[id] = new context.Module(depMap);
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
function on(depMap, name, fn) {
|
|
var id = depMap.id,
|
|
mod = getOwn(registry, id);
|
|
|
|
if (hasProp(defined, id) &&
|
|
(!mod || mod.defineEmitComplete)) {
|
|
if (name === 'defined') {
|
|
fn(defined[id]);
|
|
}
|
|
} else {
|
|
mod = getModule(depMap);
|
|
if (mod.error && name === 'error') {
|
|
fn(mod.error);
|
|
} else {
|
|
mod.on(name, fn);
|
|
}
|
|
}
|
|
}
|
|
|
|
function onError(err, errback) {
|
|
var ids = err.requireModules,
|
|
notified = false;
|
|
|
|
if (errback) {
|
|
errback(err);
|
|
} else {
|
|
each(ids, function (id) {
|
|
var mod = getOwn(registry, id);
|
|
if (mod) {
|
|
//Set error on module, so it skips timeout checks.
|
|
mod.error = err;
|
|
if (mod.events.error) {
|
|
notified = true;
|
|
mod.emit('error', err);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (!notified) {
|
|
req.onError(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal method to transfer globalQueue items to this context's
|
|
* defQueue.
|
|
*/
|
|
function takeGlobalQueue() {
|
|
//Push all the globalDefQueue items into the context's defQueue
|
|
if (globalDefQueue.length) {
|
|
//Array splice in the values since the context code has a
|
|
//local var ref to defQueue, so cannot just reassign the one
|
|
//on context.
|
|
apsp.apply(defQueue,
|
|
[defQueue.length, 0].concat(globalDefQueue));
|
|
globalDefQueue = [];
|
|
}
|
|
}
|
|
|
|
handlers = {
|
|
'require': function (mod) {
|
|
if (mod.require) {
|
|
return mod.require;
|
|
} else {
|
|
return (mod.require = context.makeRequire(mod.map));
|
|
}
|
|
},
|
|
'exports': function (mod) {
|
|
mod.usingExports = true;
|
|
if (mod.map.isDefine) {
|
|
if (mod.exports) {
|
|
return mod.exports;
|
|
} else {
|
|
return (mod.exports = defined[mod.map.id] = {});
|
|
}
|
|
}
|
|
},
|
|
'module': function (mod) {
|
|
if (mod.module) {
|
|
return mod.module;
|
|
} else {
|
|
return (mod.module = {
|
|
id: mod.map.id,
|
|
uri: mod.map.url,
|
|
config: function () {
|
|
return getOwn(config.config, mod.map.id) || {};
|
|
},
|
|
exports: handlers.exports(mod)
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
function cleanRegistry(id) {
|
|
//Clean up machinery used for waiting modules.
|
|
delete registry[id];
|
|
delete enabledRegistry[id];
|
|
}
|
|
|
|
function breakCycle(mod, traced, processed) {
|
|
var id = mod.map.id;
|
|
|
|
if (mod.error) {
|
|
mod.emit('error', mod.error);
|
|
} else {
|
|
traced[id] = true;
|
|
each(mod.depMaps, function (depMap, i) {
|
|
var depId = depMap.id,
|
|
dep = getOwn(registry, depId);
|
|
|
|
//Only force things that have not completed
|
|
//being defined, so still in the registry,
|
|
//and only if it has not been matched up
|
|
//in the module already.
|
|
if (dep && !mod.depMatched[i] && !processed[depId]) {
|
|
if (getOwn(traced, depId)) {
|
|
mod.defineDep(i, defined[depId]);
|
|
mod.check(); //pass false?
|
|
} else {
|
|
breakCycle(dep, traced, processed);
|
|
}
|
|
}
|
|
});
|
|
processed[id] = true;
|
|
}
|
|
}
|
|
|
|
function checkLoaded() {
|
|
var err, usingPathFallback,
|
|
waitInterval = config.waitSeconds * 1000,
|
|
//It is possible to disable the wait interval by using waitSeconds of 0.
|
|
expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(),
|
|
noLoads = [],
|
|
reqCalls = [],
|
|
stillLoading = false,
|
|
needCycleCheck = true;
|
|
|
|
//Do not bother if this call was a result of a cycle break.
|
|
if (inCheckLoaded) {
|
|
return;
|
|
}
|
|
|
|
inCheckLoaded = true;
|
|
|
|
//Figure out the state of all the modules.
|
|
eachProp(enabledRegistry, function (mod) {
|
|
var map = mod.map,
|
|
modId = map.id;
|
|
|
|
//Skip things that are not enabled or in error state.
|
|
if (!mod.enabled) {
|
|
return;
|
|
}
|
|
|
|
if (!map.isDefine) {
|
|
reqCalls.push(mod);
|
|
}
|
|
|
|
if (!mod.error) {
|
|
//If the module should be executed, and it has not
|
|
//been inited and time is up, remember it.
|
|
if (!mod.inited && expired) {
|
|
if (hasPathFallback(modId)) {
|
|
usingPathFallback = true;
|
|
stillLoading = true;
|
|
} else {
|
|
noLoads.push(modId);
|
|
removeScript(modId);
|
|
}
|
|
} else if (!mod.inited && mod.fetched && map.isDefine) {
|
|
stillLoading = true;
|
|
if (!map.prefix) {
|
|
//No reason to keep looking for unfinished
|
|
//loading. If the only stillLoading is a
|
|
//plugin resource though, keep going,
|
|
//because it may be that a plugin resource
|
|
//is waiting on a non-plugin cycle.
|
|
return (needCycleCheck = false);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if (expired && noLoads.length) {
|
|
//If wait time expired, throw error of unloaded modules.
|
|
err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads);
|
|
err.contextName = context.contextName;
|
|
return onError(err);
|
|
}
|
|
|
|
//Not expired, check for a cycle.
|
|
if (needCycleCheck) {
|
|
each(reqCalls, function (mod) {
|
|
breakCycle(mod, {}, {});
|
|
});
|
|
}
|
|
|
|
//If still waiting on loads, and the waiting load is something
|
|
//other than a plugin resource, or there are still outstanding
|
|
//scripts, then just try back later.
|
|
if ((!expired || usingPathFallback) && stillLoading) {
|
|
//Something is still waiting to load. Wait for it, but only
|
|
//if a timeout is not already in effect.
|
|
if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) {
|
|
checkLoadedTimeoutId = setTimeout(function () {
|
|
checkLoadedTimeoutId = 0;
|
|
checkLoaded();
|
|
}, 50);
|
|
}
|
|
}
|
|
|
|
inCheckLoaded = false;
|
|
}
|
|
|
|
Module = function (map) {
|
|
this.events = getOwn(undefEvents, map.id) || {};
|
|
this.map = map;
|
|
this.shim = getOwn(config.shim, map.id);
|
|
this.depExports = [];
|
|
this.depMaps = [];
|
|
this.depMatched = [];
|
|
this.pluginMaps = {};
|
|
this.depCount = 0;
|
|
|
|
/* this.exports this.factory
|
|
this.depMaps = [],
|
|
this.enabled, this.fetched
|
|
*/
|
|
};
|
|
|
|
Module.prototype = {
|
|
init: function (depMaps, factory, errback, options) {
|
|
options = options || {};
|
|
|
|
//Do not do more inits if already done. Can happen if there
|
|
//are multiple define calls for the same module. That is not
|
|
//a normal, common case, but it is also not unexpected.
|
|
if (this.inited) {
|
|
return;
|
|
}
|
|
|
|
this.factory = factory;
|
|
|
|
if (errback) {
|
|
//Register for errors on this module.
|
|
this.on('error', errback);
|
|
} else if (this.events.error) {
|
|
//If no errback already, but there are error listeners
|
|
//on this module, set up an errback to pass to the deps.
|
|
errback = bind(this, function (err) {
|
|
this.emit('error', err);
|
|
});
|
|
}
|
|
|
|
//Do a copy of the dependency array, so that
|
|
//source inputs are not modified. For example
|
|
//"shim" deps are passed in here directly, and
|
|
//doing a direct modification of the depMaps array
|
|
//would affect that config.
|
|
this.depMaps = depMaps && depMaps.slice(0);
|
|
|
|
this.errback = errback;
|
|
|
|
//Indicate this module has be initialized
|
|
this.inited = true;
|
|
|
|
this.ignore = options.ignore;
|
|
|
|
//Could have option to init this module in enabled mode,
|
|
//or could have been previously marked as enabled. However,
|
|
//the dependencies are not known until init is called. So
|
|
//if enabled previously, now trigger dependencies as enabled.
|
|
if (options.enabled || this.enabled) {
|
|
//Enable this module and dependencies.
|
|
//Will call this.check()
|
|
this.enable();
|
|
} else {
|
|
this.check();
|
|
}
|
|
},
|
|
|
|
defineDep: function (i, depExports) {
|
|
//Because of cycles, defined callback for a given
|
|
//export can be called more than once.
|
|
if (!this.depMatched[i]) {
|
|
this.depMatched[i] = true;
|
|
this.depCount -= 1;
|
|
this.depExports[i] = depExports;
|
|
}
|
|
},
|
|
|
|
fetch: function () {
|
|
if (this.fetched) {
|
|
return;
|
|
}
|
|
this.fetched = true;
|
|
|
|
context.startTime = (new Date()).getTime();
|
|
|
|
var map = this.map;
|
|
|
|
//If the manager is for a plugin managed resource,
|
|
//ask the plugin to load it now.
|
|
if (this.shim) {
|
|
context.makeRequire(this.map, {
|
|
enableBuildCallback: true
|
|
})(this.shim.deps || [], bind(this, function () {
|
|
return map.prefix ? this.callPlugin() : this.load();
|
|
}));
|
|
} else {
|
|
//Regular dependency.
|
|
return map.prefix ? this.callPlugin() : this.load();
|
|
}
|
|
},
|
|
|
|
load: function () {
|
|
var url = this.map.url;
|
|
|
|
//Regular dependency.
|
|
if (!urlFetched[url]) {
|
|
urlFetched[url] = true;
|
|
context.load(this.map.id, url);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Checks if the module is ready to define itself, and if so,
|
|
* define it.
|
|
*/
|
|
check: function () {
|
|
if (!this.enabled || this.enabling) {
|
|
return;
|
|
}
|
|
|
|
var err, cjsModule,
|
|
id = this.map.id,
|
|
depExports = this.depExports,
|
|
exports = this.exports,
|
|
factory = this.factory;
|
|
|
|
if (!this.inited) {
|
|
this.fetch();
|
|
} else if (this.error) {
|
|
this.emit('error', this.error);
|
|
} else if (!this.defining) {
|
|
//The factory could trigger another require call
|
|
//that would result in checking this module to
|
|
//define itself again. If already in the process
|
|
//of doing that, skip this work.
|
|
this.defining = true;
|
|
|
|
if (this.depCount < 1 && !this.defined) {
|
|
if (isFunction(factory)) {
|
|
//If there is an error listener, favor passing
|
|
//to that instead of throwing an error. However,
|
|
//only do it for define()'d modules. require
|
|
//errbacks should not be called for failures in
|
|
//their callbacks (#699). However if a global
|
|
//onError is set, use that.
|
|
if ((this.events.error && this.map.isDefine) ||
|
|
req.onError !== defaultOnError) {
|
|
try {
|
|
exports = context.execCb(id, factory, depExports, exports);
|
|
} catch (e) {
|
|
err = e;
|
|
}
|
|
} else {
|
|
exports = context.execCb(id, factory, depExports, exports);
|
|
}
|
|
|
|
// Favor return value over exports. If node/cjs in play,
|
|
// then will not have a return value anyway. Favor
|
|
// module.exports assignment over exports object.
|
|
if (this.map.isDefine && exports === undefined) {
|
|
cjsModule = this.module;
|
|
if (cjsModule) {
|
|
exports = cjsModule.exports;
|
|
} else if (this.usingExports) {
|
|
//exports already set the defined value.
|
|
exports = this.exports;
|
|
}
|
|
}
|
|
|
|
if (err) {
|
|
err.requireMap = this.map;
|
|
err.requireModules = this.map.isDefine ? [this.map.id] : null;
|
|
err.requireType = this.map.isDefine ? 'define' : 'require';
|
|
return onError((this.error = err));
|
|
}
|
|
|
|
} else {
|
|
//Just a literal value
|
|
exports = factory;
|
|
}
|
|
|
|
this.exports = exports;
|
|
|
|
if (this.map.isDefine && !this.ignore) {
|
|
defined[id] = exports;
|
|
|
|
if (req.onResourceLoad) {
|
|
req.onResourceLoad(context, this.map, this.depMaps);
|
|
}
|
|
}
|
|
|
|
//Clean up
|
|
cleanRegistry(id);
|
|
|
|
this.defined = true;
|
|
}
|
|
|
|
//Finished the define stage. Allow calling check again
|
|
//to allow define notifications below in the case of a
|
|
//cycle.
|
|
this.defining = false;
|
|
|
|
if (this.defined && !this.defineEmitted) {
|
|
this.defineEmitted = true;
|
|
this.emit('defined', this.exports);
|
|
this.defineEmitComplete = true;
|
|
}
|
|
|
|
}
|
|
},
|
|
|
|
callPlugin: function () {
|
|
var map = this.map,
|
|
id = map.id,
|
|
//Map already normalized the prefix.
|
|
pluginMap = makeModuleMap(map.prefix);
|
|
|
|
//Mark this as a dependency for this plugin, so it
|
|
//can be traced for cycles.
|
|
this.depMaps.push(pluginMap);
|
|
|
|
on(pluginMap, 'defined', bind(this, function (plugin) {
|
|
var load, normalizedMap, normalizedMod,
|
|
bundleId = getOwn(bundlesMap, this.map.id),
|
|
name = this.map.name,
|
|
parentName = this.map.parentMap ? this.map.parentMap.name : null,
|
|
localRequire = context.makeRequire(map.parentMap, {
|
|
enableBuildCallback: true
|
|
});
|
|
|
|
//If current map is not normalized, wait for that
|
|
//normalized name to load instead of continuing.
|
|
if (this.map.unnormalized) {
|
|
//Normalize the ID if the plugin allows it.
|
|
if (plugin.normalize) {
|
|
name = plugin.normalize(name, function (name) {
|
|
return normalize(name, parentName, true);
|
|
}) || '';
|
|
}
|
|
|
|
//prefix and name should already be normalized, no need
|
|
//for applying map config again either.
|
|
normalizedMap = makeModuleMap(map.prefix + '!' + name,
|
|
this.map.parentMap);
|
|
on(normalizedMap,
|
|
'defined', bind(this, function (value) {
|
|
this.init([], function () { return value; }, null, {
|
|
enabled: true,
|
|
ignore: true
|
|
});
|
|
}));
|
|
|
|
normalizedMod = getOwn(registry, normalizedMap.id);
|
|
if (normalizedMod) {
|
|
//Mark this as a dependency for this plugin, so it
|
|
//can be traced for cycles.
|
|
this.depMaps.push(normalizedMap);
|
|
|
|
if (this.events.error) {
|
|
normalizedMod.on('error', bind(this, function (err) {
|
|
this.emit('error', err);
|
|
}));
|
|
}
|
|
normalizedMod.enable();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//If a paths config, then just load that file instead to
|
|
//resolve the plugin, as it is built into that paths layer.
|
|
if (bundleId) {
|
|
this.map.url = context.nameToUrl(bundleId);
|
|
this.load();
|
|
return;
|
|
}
|
|
|
|
load = bind(this, function (value) {
|
|
this.init([], function () { return value; }, null, {
|
|
enabled: true
|
|
});
|
|
});
|
|
|
|
load.error = bind(this, function (err) {
|
|
this.inited = true;
|
|
this.error = err;
|
|
err.requireModules = [id];
|
|
|
|
//Remove temp unnormalized modules for this module,
|
|
//since they will never be resolved otherwise now.
|
|
eachProp(registry, function (mod) {
|
|
if (mod.map.id.indexOf(id + '_unnormalized') === 0) {
|
|
cleanRegistry(mod.map.id);
|
|
}
|
|
});
|
|
|
|
onError(err);
|
|
});
|
|
|
|
//Allow plugins to load other code without having to know the
|
|
//context or how to 'complete' the load.
|
|
load.fromText = bind(this, function (text, textAlt) {
|
|
/*jslint evil: true */
|
|
var moduleName = map.name,
|
|
moduleMap = makeModuleMap(moduleName),
|
|
hasInteractive = useInteractive;
|
|
|
|
//As of 2.1.0, support just passing the text, to reinforce
|
|
//fromText only being called once per resource. Still
|
|
//support old style of passing moduleName but discard
|
|
//that moduleName in favor of the internal ref.
|
|
if (textAlt) {
|
|
text = textAlt;
|
|
}
|
|
|
|
//Turn off interactive script matching for IE for any define
|
|
//calls in the text, then turn it back on at the end.
|
|
if (hasInteractive) {
|
|
useInteractive = false;
|
|
}
|
|
|
|
//Prime the system by creating a module instance for
|
|
//it.
|
|
getModule(moduleMap);
|
|
|
|
//Transfer any config to this other module.
|
|
if (hasProp(config.config, id)) {
|
|
config.config[moduleName] = config.config[id];
|
|
}
|
|
|
|
try {
|
|
req.exec(text);
|
|
} catch (e) {
|
|
return onError(makeError('fromtexteval',
|
|
'fromText eval for ' + id +
|
|
' failed: ' + e,
|
|
e,
|
|
[id]));
|
|
}
|
|
|
|
if (hasInteractive) {
|
|
useInteractive = true;
|
|
}
|
|
|
|
//Mark this as a dependency for the plugin
|
|
//resource
|
|
this.depMaps.push(moduleMap);
|
|
|
|
//Support anonymous modules.
|
|
context.completeLoad(moduleName);
|
|
|
|
//Bind the value of that module to the value for this
|
|
//resource ID.
|
|
localRequire([moduleName], load);
|
|
});
|
|
|
|
//Use parentName here since the plugin's name is not reliable,
|
|
//could be some weird string with no path that actually wants to
|
|
//reference the parentName's path.
|
|
plugin.load(map.name, localRequire, load, config);
|
|
}));
|
|
|
|
context.enable(pluginMap, this);
|
|
this.pluginMaps[pluginMap.id] = pluginMap;
|
|
},
|
|
|
|
enable: function () {
|
|
enabledRegistry[this.map.id] = this;
|
|
this.enabled = true;
|
|
|
|
//Set flag mentioning that the module is enabling,
|
|
//so that immediate calls to the defined callbacks
|
|
//for dependencies do not trigger inadvertent load
|
|
//with the depCount still being zero.
|
|
this.enabling = true;
|
|
|
|
//Enable each dependency
|
|
each(this.depMaps, bind(this, function (depMap, i) {
|
|
var id, mod, handler;
|
|
|
|
if (typeof depMap === 'string') {
|
|
//Dependency needs to be converted to a depMap
|
|
//and wired up to this module.
|
|
depMap = makeModuleMap(depMap,
|
|
(this.map.isDefine ? this.map : this.map.parentMap),
|
|
false,
|
|
!this.skipMap);
|
|
this.depMaps[i] = depMap;
|
|
|
|
handler = getOwn(handlers, depMap.id);
|
|
|
|
if (handler) {
|
|
this.depExports[i] = handler(this);
|
|
return;
|
|
}
|
|
|
|
this.depCount += 1;
|
|
|
|
on(depMap, 'defined', bind(this, function (depExports) {
|
|
this.defineDep(i, depExports);
|
|
this.check();
|
|
}));
|
|
|
|
if (this.errback) {
|
|
on(depMap, 'error', bind(this, this.errback));
|
|
}
|
|
}
|
|
|
|
id = depMap.id;
|
|
mod = registry[id];
|
|
|
|
//Skip special modules like 'require', 'exports', 'module'
|
|
//Also, don't call enable if it is already enabled,
|
|
//important in circular dependency cases.
|
|
if (!hasProp(handlers, id) && mod && !mod.enabled) {
|
|
context.enable(depMap, this);
|
|
}
|
|
}));
|
|
|
|
//Enable each plugin that is used in
|
|
//a dependency
|
|
eachProp(this.pluginMaps, bind(this, function (pluginMap) {
|
|
var mod = getOwn(registry, pluginMap.id);
|
|
if (mod && !mod.enabled) {
|
|
context.enable(pluginMap, this);
|
|
}
|
|
}));
|
|
|
|
this.enabling = false;
|
|
|
|
this.check();
|
|
},
|
|
|
|
on: function (name, cb) {
|
|
var cbs = this.events[name];
|
|
if (!cbs) {
|
|
cbs = this.events[name] = [];
|
|
}
|
|
cbs.push(cb);
|
|
},
|
|
|
|
emit: function (name, evt) {
|
|
each(this.events[name], function (cb) {
|
|
cb(evt);
|
|
});
|
|
if (name === 'error') {
|
|
//Now that the error handler was triggered, remove
|
|
//the listeners, since this broken Module instance
|
|
//can stay around for a while in the registry.
|
|
delete this.events[name];
|
|
}
|
|
}
|
|
};
|
|
|
|
function callGetModule(args) {
|
|
//Skip modules already defined.
|
|
if (!hasProp(defined, args[0])) {
|
|
getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]);
|
|
}
|
|
}
|
|
|
|
function removeListener(node, func, name, ieName) {
|
|
//Favor detachEvent because of IE9
|
|
//issue, see attachEvent/addEventListener comment elsewhere
|
|
//in this file.
|
|
if (node.detachEvent && !isOpera) {
|
|
//Probably IE. If not it will throw an error, which will be
|
|
//useful to know.
|
|
if (ieName) {
|
|
node.detachEvent(ieName, func);
|
|
}
|
|
} else {
|
|
node.removeEventListener(name, func, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given an event from a script node, get the requirejs info from it,
|
|
* and then removes the event listeners on the node.
|
|
* @param {Event} evt
|
|
* @returns {Object}
|
|
*/
|
|
function getScriptData(evt) {
|
|
//Using currentTarget instead of target for Firefox 2.0's sake. Not
|
|
//all old browsers will be supported, but this one was easy enough
|
|
//to support and still makes sense.
|
|
var node = evt.currentTarget || evt.srcElement;
|
|
|
|
//Remove the listeners once here.
|
|
removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange');
|
|
removeListener(node, context.onScriptError, 'error');
|
|
|
|
return {
|
|
node: node,
|
|
id: node && node.getAttribute('data-requiremodule')
|
|
};
|
|
}
|
|
|
|
function intakeDefines() {
|
|
var args;
|
|
|
|
//Any defined modules in the global queue, intake them now.
|
|
takeGlobalQueue();
|
|
|
|
//Make sure any remaining defQueue items get properly processed.
|
|
while (defQueue.length) {
|
|
args = defQueue.shift();
|
|
if (args[0] === null) {
|
|
return onError(makeError('mismatch', 'Mismatched anonymous define() module: ' + args[args.length - 1]));
|
|
} else {
|
|
//args are id, deps, factory. Should be normalized by the
|
|
//define() function.
|
|
callGetModule(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
context = {
|
|
config: config,
|
|
contextName: contextName,
|
|
registry: registry,
|
|
defined: defined,
|
|
urlFetched: urlFetched,
|
|
defQueue: defQueue,
|
|
Module: Module,
|
|
makeModuleMap: makeModuleMap,
|
|
nextTick: req.nextTick,
|
|
onError: onError,
|
|
|
|
/**
|
|
* Set a configuration for the context.
|
|
* @param {Object} cfg config object to integrate.
|
|
*/
|
|
configure: function (cfg) {
|
|
//Make sure the baseUrl ends in a slash.
|
|
if (cfg.baseUrl) {
|
|
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
|
|
cfg.baseUrl += '/';
|
|
}
|
|
}
|
|
|
|
//Save off the paths since they require special processing,
|
|
//they are additive.
|
|
var shim = config.shim,
|
|
objs = {
|
|
paths: true,
|
|
bundles: true,
|
|
config: true,
|
|
map: true
|
|
};
|
|
|
|
eachProp(cfg, function (value, prop) {
|
|
if (objs[prop]) {
|
|
if (!config[prop]) {
|
|
config[prop] = {};
|
|
}
|
|
mixin(config[prop], value, true, true);
|
|
} else {
|
|
config[prop] = value;
|
|
}
|
|
});
|
|
|
|
//Reverse map the bundles
|
|
if (cfg.bundles) {
|
|
eachProp(cfg.bundles, function (value, prop) {
|
|
each(value, function (v) {
|
|
if (v !== prop) {
|
|
bundlesMap[v] = prop;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
//Merge shim
|
|
if (cfg.shim) {
|
|
eachProp(cfg.shim, function (value, id) {
|
|
//Normalize the structure
|
|
if (isArray(value)) {
|
|
value = {
|
|
deps: value
|
|
};
|
|
}
|
|
if ((value.exports || value.init) && !value.exportsFn) {
|
|
value.exportsFn = context.makeShimExports(value);
|
|
}
|
|
shim[id] = value;
|
|
});
|
|
config.shim = shim;
|
|
}
|
|
|
|
//Adjust packages if necessary.
|
|
if (cfg.packages) {
|
|
each(cfg.packages, function (pkgObj) {
|
|
var location, name;
|
|
|
|
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
|
|
|
|
name = pkgObj.name;
|
|
location = pkgObj.location;
|
|
if (location) {
|
|
config.paths[name] = pkgObj.location;
|
|
}
|
|
|
|
//Save pointer to main module ID for pkg name.
|
|
//Remove leading dot in main, so main paths are normalized,
|
|
//and remove any trailing .js, since different package
|
|
//envs have different conventions: some use a module name,
|
|
//some use a file name.
|
|
config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main')
|
|
.replace(currDirRegExp, '')
|
|
.replace(jsSuffixRegExp, '');
|
|
});
|
|
}
|
|
|
|
//If there are any "waiting to execute" modules in the registry,
|
|
//update the maps for them, since their info, like URLs to load,
|
|
//may have changed.
|
|
eachProp(registry, function (mod, id) {
|
|
//If module already has init called, since it is too
|
|
//late to modify them, and ignore unnormalized ones
|
|
//since they are transient.
|
|
if (!mod.inited && !mod.map.unnormalized) {
|
|
mod.map = makeModuleMap(id);
|
|
}
|
|
});
|
|
|
|
//If a deps array or a config callback is specified, then call
|
|
//require with those args. This is useful when require is defined as a
|
|
//config object before require.js is loaded.
|
|
if (cfg.deps || cfg.callback) {
|
|
context.require(cfg.deps || [], cfg.callback);
|
|
}
|
|
},
|
|
|
|
makeShimExports: function (value) {
|
|
function fn() {
|
|
var ret;
|
|
if (value.init) {
|
|
ret = value.init.apply(global, arguments);
|
|
}
|
|
return ret || (value.exports && getGlobal(value.exports));
|
|
}
|
|
return fn;
|
|
},
|
|
|
|
makeRequire: function (relMap, options) {
|
|
options = options || {};
|
|
|
|
function localRequire(deps, callback, errback) {
|
|
var id, map, requireMod;
|
|
|
|
if (options.enableBuildCallback && callback && isFunction(callback)) {
|
|
callback.__requireJsBuild = true;
|
|
}
|
|
|
|
if (typeof deps === 'string') {
|
|
if (isFunction(callback)) {
|
|
//Invalid call
|
|
return onError(makeError('requireargs', 'Invalid require call'), errback);
|
|
}
|
|
|
|
//If require|exports|module are requested, get the
|
|
//value for them from the special handlers. Caveat:
|
|
//this only works while module is being defined.
|
|
if (relMap && hasProp(handlers, deps)) {
|
|
return handlers[deps](registry[relMap.id]);
|
|
}
|
|
|
|
//Synchronous access to one module. If require.get is
|
|
//available (as in the Node adapter), prefer that.
|
|
if (req.get) {
|
|
return req.get(context, deps, relMap, localRequire);
|
|
}
|
|
|
|
//Normalize module name, if it contains . or ..
|
|
map = makeModuleMap(deps, relMap, false, true);
|
|
id = map.id;
|
|
|
|
if (!hasProp(defined, id)) {
|
|
return onError(makeError('notloaded', 'Module name "' +
|
|
id +
|
|
'" has not been loaded yet for context: ' +
|
|
contextName +
|
|
(relMap ? '' : '. Use require([])')));
|
|
}
|
|
return defined[id];
|
|
}
|
|
|
|
//Grab defines waiting in the global queue.
|
|
intakeDefines();
|
|
|
|
//Mark all the dependencies as needing to be loaded.
|
|
context.nextTick(function () {
|
|
//Some defines could have been added since the
|
|
//require call, collect them.
|
|
intakeDefines();
|
|
|
|
requireMod = getModule(makeModuleMap(null, relMap));
|
|
|
|
//Store if map config should be applied to this require
|
|
//call for dependencies.
|
|
requireMod.skipMap = options.skipMap;
|
|
|
|
requireMod.init(deps, callback, errback, {
|
|
enabled: true
|
|
});
|
|
|
|
checkLoaded();
|
|
});
|
|
|
|
return localRequire;
|
|
}
|
|
|
|
mixin(localRequire, {
|
|
isBrowser: isBrowser,
|
|
|
|
/**
|
|
* Converts a module name + .extension into an URL path.
|
|
* *Requires* the use of a module name. It does not support using
|
|
* plain URLs like nameToUrl.
|
|
*/
|
|
toUrl: function (moduleNamePlusExt) {
|
|
var ext,
|
|
index = moduleNamePlusExt.lastIndexOf('.'),
|
|
segment = moduleNamePlusExt.split('/')[0],
|
|
isRelative = segment === '.' || segment === '..';
|
|
|
|
//Have a file extension alias, and it is not the
|
|
//dots from a relative path.
|
|
if (index !== -1 && (!isRelative || index > 1)) {
|
|
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
|
|
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
|
|
}
|
|
|
|
return context.nameToUrl(normalize(moduleNamePlusExt,
|
|
relMap && relMap.id, true), ext, true);
|
|
},
|
|
|
|
defined: function (id) {
|
|
return hasProp(defined, makeModuleMap(id, relMap, false, true).id);
|
|
},
|
|
|
|
specified: function (id) {
|
|
id = makeModuleMap(id, relMap, false, true).id;
|
|
return hasProp(defined, id) || hasProp(registry, id);
|
|
}
|
|
});
|
|
|
|
//Only allow undef on top level require calls
|
|
if (!relMap) {
|
|
localRequire.undef = function (id) {
|
|
//Bind any waiting define() calls to this context,
|
|
//fix for #408
|
|
takeGlobalQueue();
|
|
|
|
var map = makeModuleMap(id, relMap, true),
|
|
mod = getOwn(registry, id);
|
|
|
|
removeScript(id);
|
|
|
|
delete defined[id];
|
|
delete urlFetched[map.url];
|
|
delete undefEvents[id];
|
|
|
|
//Clean queued defines too. Go backwards
|
|
//in array so that the splices do not
|
|
//mess up the iteration.
|
|
eachReverse(defQueue, function(args, i) {
|
|
if(args[0] === id) {
|
|
defQueue.splice(i, 1);
|
|
}
|
|
});
|
|
|
|
if (mod) {
|
|
//Hold on to listeners in case the
|
|
//module will be attempted to be reloaded
|
|
//using a different config.
|
|
if (mod.events.defined) {
|
|
undefEvents[id] = mod.events;
|
|
}
|
|
|
|
cleanRegistry(id);
|
|
}
|
|
};
|
|
}
|
|
|
|
return localRequire;
|
|
},
|
|
|
|
/**
|
|
* Called to enable a module if it is still in the registry
|
|
* awaiting enablement. A second arg, parent, the parent module,
|
|
* is passed in for context, when this method is overriden by
|
|
* the optimizer. Not shown here to keep code compact.
|
|
*/
|
|
enable: function (depMap) {
|
|
var mod = getOwn(registry, depMap.id);
|
|
if (mod) {
|
|
getModule(depMap).enable();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Internal method used by environment adapters to complete a load event.
|
|
* A load event could be a script load or just a load pass from a synchronous
|
|
* load call.
|
|
* @param {String} moduleName the name of the module to potentially complete.
|
|
*/
|
|
completeLoad: function (moduleName) {
|
|
var found, args, mod,
|
|
shim = getOwn(config.shim, moduleName) || {},
|
|
shExports = shim.exports;
|
|
|
|
takeGlobalQueue();
|
|
|
|
while (defQueue.length) {
|
|
args = defQueue.shift();
|
|
if (args[0] === null) {
|
|
args[0] = moduleName;
|
|
//If already found an anonymous module and bound it
|
|
//to this name, then this is some other anon module
|
|
//waiting for its completeLoad to fire.
|
|
if (found) {
|
|
break;
|
|
}
|
|
found = true;
|
|
} else if (args[0] === moduleName) {
|
|
//Found matching define call for this script!
|
|
found = true;
|
|
}
|
|
|
|
callGetModule(args);
|
|
}
|
|
|
|
//Do this after the cycle of callGetModule in case the result
|
|
//of those calls/init calls changes the registry.
|
|
mod = getOwn(registry, moduleName);
|
|
|
|
if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) {
|
|
if (config.enforceDefine && (!shExports || !getGlobal(shExports))) {
|
|
if (hasPathFallback(moduleName)) {
|
|
return;
|
|
} else {
|
|
return onError(makeError('nodefine',
|
|
'No define call for ' + moduleName,
|
|
null,
|
|
[moduleName]));
|
|
}
|
|
} else {
|
|
//A script that does not call define(), so just simulate
|
|
//the call for it.
|
|
callGetModule([moduleName, (shim.deps || []), shim.exportsFn]);
|
|
}
|
|
}
|
|
|
|
checkLoaded();
|
|
},
|
|
|
|
/**
|
|
* Converts a module name to a file path. Supports cases where
|
|
* moduleName may actually be just an URL.
|
|
* Note that it **does not** call normalize on the moduleName,
|
|
* it is assumed to have already been normalized. This is an
|
|
* internal API, not a public one. Use toUrl for the public API.
|
|
*/
|
|
nameToUrl: function (moduleName, ext, skipExt) {
|
|
var paths, syms, i, parentModule, url,
|
|
parentPath, bundleId,
|
|
pkgMain = getOwn(config.pkgs, moduleName);
|
|
|
|
if (pkgMain) {
|
|
moduleName = pkgMain;
|
|
}
|
|
|
|
bundleId = getOwn(bundlesMap, moduleName);
|
|
|
|
if (bundleId) {
|
|
return context.nameToUrl(bundleId, ext, skipExt);
|
|
}
|
|
|
|
//If a colon is in the URL, it indicates a protocol is used and it is just
|
|
//an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
|
|
//or ends with .js, then assume the user meant to use an url and not a module id.
|
|
//The slash is important for protocol-less URLs as well as full paths.
|
|
if (req.jsExtRegExp.test(moduleName)) {
|
|
//Just a plain path, not module name lookup, so just return it.
|
|
//Add extension if it is included. This is a bit wonky, only non-.js things pass
|
|
//an extension, this method probably needs to be reworked.
|
|
url = moduleName + (ext || '');
|
|
} else {
|
|
//A module that needs to be converted to a path.
|
|
paths = config.paths;
|
|
|
|
syms = moduleName.split('/');
|
|
//For each module name segment, see if there is a path
|
|
//registered for it. Start with most specific name
|
|
//and work up from it.
|
|
for (i = syms.length; i > 0; i -= 1) {
|
|
parentModule = syms.slice(0, i).join('/');
|
|
|
|
parentPath = getOwn(paths, parentModule);
|
|
if (parentPath) {
|
|
//If an array, it means there are a few choices,
|
|
//Choose the one that is desired
|
|
if (isArray(parentPath)) {
|
|
parentPath = parentPath[0];
|
|
}
|
|
syms.splice(0, i, parentPath);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Join the path parts together, then figure out if baseUrl is needed.
|
|
url = syms.join('/');
|
|
url += (ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js'));
|
|
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
|
|
}
|
|
|
|
return config.urlArgs ? url +
|
|
((url.indexOf('?') === -1 ? '?' : '&') +
|
|
config.urlArgs) : url;
|
|
},
|
|
|
|
//Delegates to req.load. Broken out as a separate function to
|
|
//allow overriding in the optimizer.
|
|
load: function (id, url) {
|
|
req.load(context, id, url);
|
|
},
|
|
|
|
/**
|
|
* Executes a module callback function. Broken out as a separate function
|
|
* solely to allow the build system to sequence the files in the built
|
|
* layer in the right sequence.
|
|
*
|
|
* @private
|
|
*/
|
|
execCb: function (name, callback, args, exports) {
|
|
return callback.apply(exports, args);
|
|
},
|
|
|
|
/**
|
|
* callback for script loads, used to check status of loading.
|
|
*
|
|
* @param {Event} evt the event from the browser for the script
|
|
* that was loaded.
|
|
*/
|
|
onScriptLoad: function (evt) {
|
|
//Using currentTarget instead of target for Firefox 2.0's sake. Not
|
|
//all old browsers will be supported, but this one was easy enough
|
|
//to support and still makes sense.
|
|
if (evt.type === 'load' ||
|
|
(readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) {
|
|
//Reset interactive script so a script node is not held onto for
|
|
//to long.
|
|
interactiveScript = null;
|
|
|
|
//Pull out the name of the module and the context.
|
|
var data = getScriptData(evt);
|
|
context.completeLoad(data.id);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Callback for script errors.
|
|
*/
|
|
onScriptError: function (evt) {
|
|
var data = getScriptData(evt);
|
|
if (!hasPathFallback(data.id)) {
|
|
return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
|
|
}
|
|
}
|
|
};
|
|
|
|
context.require = context.makeRequire();
|
|
return context;
|
|
}
|
|
|
|
/**
|
|
* Main entry point.
|
|
*
|
|
* If the only argument to require is a string, then the module that
|
|
* is represented by that string is fetched for the appropriate context.
|
|
*
|
|
* If the first argument is an array, then it will be treated as an array
|
|
* of dependency string names to fetch. An optional function callback can
|
|
* be specified to execute when all of those dependencies are available.
|
|
*
|
|
* Make a local req variable to help Caja compliance (it assumes things
|
|
* on a require that are not standardized), and to give a short
|
|
* name for minification/local scope use.
|
|
*/
|
|
req = requirejs = function (deps, callback, errback, optional) {
|
|
|
|
//Find the right context, use default
|
|
var context, config,
|
|
contextName = defContextName;
|
|
|
|
// Determine if have config object in the call.
|
|
if (!isArray(deps) && typeof deps !== 'string') {
|
|
// deps is a config object
|
|
config = deps;
|
|
if (isArray(callback)) {
|
|
// Adjust args if there are dependencies
|
|
deps = callback;
|
|
callback = errback;
|
|
errback = optional;
|
|
} else {
|
|
deps = [];
|
|
}
|
|
}
|
|
|
|
if (config && config.context) {
|
|
contextName = config.context;
|
|
}
|
|
|
|
context = getOwn(contexts, contextName);
|
|
if (!context) {
|
|
context = contexts[contextName] = req.s.newContext(contextName);
|
|
}
|
|
|
|
if (config) {
|
|
context.configure(config);
|
|
}
|
|
|
|
return context.require(deps, callback, errback);
|
|
};
|
|
|
|
/**
|
|
* Support require.config() to make it easier to cooperate with other
|
|
* AMD loaders on globally agreed names.
|
|
*/
|
|
req.config = function (config) {
|
|
return req(config);
|
|
};
|
|
|
|
/**
|
|
* Execute something after the current tick
|
|
* of the event loop. Override for other envs
|
|
* that have a better solution than setTimeout.
|
|
* @param {Function} fn function to execute later.
|
|
*/
|
|
req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) {
|
|
setTimeout(fn, 4);
|
|
} : function (fn) { fn(); };
|
|
|
|
/**
|
|
* Export require as a global, but only if it does not already exist.
|
|
*/
|
|
if (!require) {
|
|
require = req;
|
|
}
|
|
|
|
req.version = version;
|
|
|
|
//Used to filter out dependencies that are already paths.
|
|
req.jsExtRegExp = /^\/|:|\?|\.js$/;
|
|
req.isBrowser = isBrowser;
|
|
s = req.s = {
|
|
contexts: contexts,
|
|
newContext: newContext
|
|
};
|
|
|
|
//Create default context.
|
|
req({});
|
|
|
|
//Exports some context-sensitive methods on global require.
|
|
each([
|
|
'toUrl',
|
|
'undef',
|
|
'defined',
|
|
'specified'
|
|
], function (prop) {
|
|
//Reference from contexts instead of early binding to default context,
|
|
//so that during builds, the latest instance of the default context
|
|
//with its config gets used.
|
|
req[prop] = function () {
|
|
var ctx = contexts[defContextName];
|
|
return ctx.require[prop].apply(ctx, arguments);
|
|
};
|
|
});
|
|
|
|
if (isBrowser) {
|
|
head = s.head = document.getElementsByTagName('head')[0];
|
|
//If BASE tag is in play, using appendChild is a problem for IE6.
|
|
//When that browser dies, this can be removed. Details in this jQuery bug:
|
|
//http://dev.jquery.com/ticket/2709
|
|
baseElement = document.getElementsByTagName('base')[0];
|
|
if (baseElement) {
|
|
head = s.head = baseElement.parentNode;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Any errors that require explicitly generates will be passed to this
|
|
* function. Intercept/override it if you want custom error handling.
|
|
* @param {Error} err the error object.
|
|
*/
|
|
req.onError = defaultOnError;
|
|
|
|
/**
|
|
* Creates the node for the load command. Only used in browser envs.
|
|
*/
|
|
req.createNode = function (config, moduleName, url) {
|
|
var node = config.xhtml ?
|
|
document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') :
|
|
document.createElement('script');
|
|
node.type = config.scriptType || 'text/javascript';
|
|
node.charset = 'utf-8';
|
|
node.async = true;
|
|
return node;
|
|
};
|
|
|
|
/**
|
|
* Does the request to load a module for the browser case.
|
|
* Make this a separate function to allow other environments
|
|
* to override it.
|
|
*
|
|
* @param {Object} context the require context to find state.
|
|
* @param {String} moduleName the name of the module.
|
|
* @param {Object} url the URL to the module.
|
|
*/
|
|
req.load = function (context, moduleName, url) {
|
|
var config = (context && context.config) || {},
|
|
node;
|
|
if (isBrowser) {
|
|
//In the browser so use a script tag
|
|
node = req.createNode(config, moduleName, url);
|
|
|
|
node.setAttribute('data-requirecontext', context.contextName);
|
|
node.setAttribute('data-requiremodule', moduleName);
|
|
|
|
//Set up load listener. Test attachEvent first because IE9 has
|
|
//a subtle issue in its addEventListener and script onload firings
|
|
//that do not match the behavior of all other browsers with
|
|
//addEventListener support, which fire the onload event for a
|
|
//script right after the script execution. See:
|
|
//https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution
|
|
//UNFORTUNATELY Opera implements attachEvent but does not follow the script
|
|
//script execution mode.
|
|
if (node.attachEvent &&
|
|
//Check if node.attachEvent is artificially added by custom script or
|
|
//natively supported by browser
|
|
//read https://github.com/jrburke/requirejs/issues/187
|
|
//if we can NOT find [native code] then it must NOT natively supported.
|
|
//in IE8, node.attachEvent does not have toString()
|
|
//Note the test for "[native code" with no closing brace, see:
|
|
//https://github.com/jrburke/requirejs/issues/273
|
|
!(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) &&
|
|
!isOpera) {
|
|
//Probably IE. IE (at least 6-8) do not fire
|
|
//script onload right after executing the script, so
|
|
//we cannot tie the anonymous define call to a name.
|
|
//However, IE reports the script as being in 'interactive'
|
|
//readyState at the time of the define call.
|
|
useInteractive = true;
|
|
|
|
node.attachEvent('onreadystatechange', context.onScriptLoad);
|
|
//It would be great to add an error handler here to catch
|
|
//404s in IE9+. However, onreadystatechange will fire before
|
|
//the error handler, so that does not help. If addEventListener
|
|
//is used, then IE will fire error before load, but we cannot
|
|
//use that pathway given the connect.microsoft.com issue
|
|
//mentioned above about not doing the 'script execute,
|
|
//then fire the script load event listener before execute
|
|
//next script' that other browsers do.
|
|
//Best hope: IE10 fixes the issues,
|
|
//and then destroys all installs of IE 6-9.
|
|
//node.attachEvent('onerror', context.onScriptError);
|
|
} else {
|
|
node.addEventListener('load', context.onScriptLoad, false);
|
|
node.addEventListener('error', context.onScriptError, false);
|
|
}
|
|
node.src = url;
|
|
|
|
//For some cache cases in IE 6-8, the script executes before the end
|
|
//of the appendChild execution, so to tie an anonymous define
|
|
//call to the module name (which is stored on the node), hold on
|
|
//to a reference to this node, but clear after the DOM insertion.
|
|
currentlyAddingScript = node;
|
|
if (baseElement) {
|
|
head.insertBefore(node, baseElement);
|
|
} else {
|
|
head.appendChild(node);
|
|
}
|
|
currentlyAddingScript = null;
|
|
|
|
return node;
|
|
} else if (isWebWorker) {
|
|
try {
|
|
//In a web worker, use importScripts. This is not a very
|
|
//efficient use of importScripts, importScripts will block until
|
|
//its script is downloaded and evaluated. However, if web workers
|
|
//are in play, the expectation that a build has been done so that
|
|
//only one script needs to be loaded anyway. This may need to be
|
|
//reevaluated if other use cases become common.
|
|
importScripts(url);
|
|
|
|
//Account for anonymous modules
|
|
context.completeLoad(moduleName);
|
|
} catch (e) {
|
|
context.onError(makeError('importscripts',
|
|
'importScripts failed for ' +
|
|
moduleName + ' at ' + url,
|
|
e,
|
|
[moduleName]));
|
|
}
|
|
}
|
|
};
|
|
|
|
function getInteractiveScript() {
|
|
if (interactiveScript && interactiveScript.readyState === 'interactive') {
|
|
return interactiveScript;
|
|
}
|
|
|
|
eachReverse(scripts(), function (script) {
|
|
if (script.readyState === 'interactive') {
|
|
return (interactiveScript = script);
|
|
}
|
|
});
|
|
return interactiveScript;
|
|
}
|
|
|
|
//Look for a data-main script attribute, which could also adjust the baseUrl.
|
|
if (isBrowser && !cfg.skipDataMain) {
|
|
//Figure out baseUrl. Get it from the script tag with require.js in it.
|
|
eachReverse(scripts(), function (script) {
|
|
//Set the 'head' where we can append children by
|
|
//using the script's parent.
|
|
if (!head) {
|
|
head = script.parentNode;
|
|
}
|
|
|
|
//Look for a data-main attribute to set main script for the page
|
|
//to load. If it is there, the path to data main becomes the
|
|
//baseUrl, if it is not already set.
|
|
dataMain = script.getAttribute('data-main');
|
|
if (dataMain) {
|
|
//Preserve dataMain in case it is a path (i.e. contains '?')
|
|
mainScript = dataMain;
|
|
|
|
//Set final baseUrl if there is not already an explicit one.
|
|
if (!cfg.baseUrl) {
|
|
//Pull off the directory of data-main for use as the
|
|
//baseUrl.
|
|
src = mainScript.split('/');
|
|
mainScript = src.pop();
|
|
subPath = src.length ? src.join('/') + '/' : './';
|
|
|
|
cfg.baseUrl = subPath;
|
|
}
|
|
|
|
//Strip off any trailing .js since mainScript is now
|
|
//like a module name.
|
|
mainScript = mainScript.replace(jsSuffixRegExp, '');
|
|
|
|
//If mainScript is still a path, fall back to dataMain
|
|
if (req.jsExtRegExp.test(mainScript)) {
|
|
mainScript = dataMain;
|
|
}
|
|
|
|
//Put the data-main script in the files to load.
|
|
cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
|
|
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* The function that handles definitions of modules. Differs from
|
|
* require() in that a string for the module should be the first argument,
|
|
* and the function to execute after dependencies are loaded should
|
|
* return a value to define the module corresponding to the first argument's
|
|
* name.
|
|
*/
|
|
define = function (name, deps, callback) {
|
|
var node, context;
|
|
|
|
//Allow for anonymous modules
|
|
if (typeof name !== 'string') {
|
|
//Adjust args appropriately
|
|
callback = deps;
|
|
deps = name;
|
|
name = null;
|
|
}
|
|
|
|
//This module may not have dependencies
|
|
if (!isArray(deps)) {
|
|
callback = deps;
|
|
deps = null;
|
|
}
|
|
|
|
//If no name, and callback is a function, then figure out if it a
|
|
//CommonJS thing with dependencies.
|
|
if (!deps && isFunction(callback)) {
|
|
deps = [];
|
|
//Remove comments from the callback string,
|
|
//look for require calls, and pull them into the dependencies,
|
|
//but only if there are function args.
|
|
if (callback.length) {
|
|
callback
|
|
.toString()
|
|
.replace(commentRegExp, '')
|
|
.replace(cjsRequireRegExp, function (match, dep) {
|
|
deps.push(dep);
|
|
});
|
|
|
|
//May be a CommonJS thing even without require calls, but still
|
|
//could use exports, and module. Avoid doing exports and module
|
|
//work though if it just needs require.
|
|
//REQUIRES the function to expect the CommonJS variables in the
|
|
//order listed below.
|
|
deps = (callback.length === 1 ? ['require'] : ['require', 'exports', 'module']).concat(deps);
|
|
}
|
|
}
|
|
|
|
//If in IE 6-8 and hit an anonymous define() call, do the interactive
|
|
//work.
|
|
if (useInteractive) {
|
|
node = currentlyAddingScript || getInteractiveScript();
|
|
if (node) {
|
|
if (!name) {
|
|
name = node.getAttribute('data-requiremodule');
|
|
}
|
|
context = contexts[node.getAttribute('data-requirecontext')];
|
|
}
|
|
}
|
|
|
|
//Always save off evaluating the def call until the script onload handler.
|
|
//This allows multiple modules to be in a file without prematurely
|
|
//tracing dependencies, and allows for anonymous module support,
|
|
//where the module name is not known until the script onload event
|
|
//occurs. If no context, use the global queue, and get it processed
|
|
//in the onscript load callback.
|
|
(context ? context.defQueue : globalDefQueue).push([name, deps, callback]);
|
|
};
|
|
|
|
define.amd = {
|
|
jQuery: true
|
|
};
|
|
|
|
|
|
/**
|
|
* Executes the text. Normally just uses eval, but can be modified
|
|
* to use a better, environment-specific call. Only used for transpiling
|
|
* loader plugins, not for plain JS modules.
|
|
* @param {String} text the text to execute/evaluate.
|
|
*/
|
|
req.exec = function (text) {
|
|
/*jslint evil: true */
|
|
return eval(text);
|
|
};
|
|
|
|
//Set up with config info.
|
|
req(cfg);
|
|
}(this));
|
|
define("requireLib", function(){});
|
|
|
|
/*!
|
|
* jQuery JavaScript Library v2.1.0
|
|
* http://jquery.com/
|
|
*
|
|
* Includes Sizzle.js
|
|
* http://sizzlejs.com/
|
|
*
|
|
* Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
|
|
* Released under the MIT license
|
|
* http://jquery.org/license
|
|
*
|
|
* Date: 2014-01-23T21:10Z
|
|
*/
|
|
|
|
(function( global, factory ) {
|
|
|
|
if ( typeof module === "object" && typeof module.exports === "object" ) {
|
|
// For CommonJS and CommonJS-like environments where a proper window is present,
|
|
// execute the factory and get jQuery
|
|
// For environments that do not inherently posses a window with a document
|
|
// (such as Node.js), expose a jQuery-making factory as module.exports
|
|
// This accentuates the need for the creation of a real window
|
|
// e.g. var jQuery = require("jquery")(window);
|
|
// See ticket #14549 for more info
|
|
module.exports = global.document ?
|
|
factory( global, true ) :
|
|
function( w ) {
|
|
if ( !w.document ) {
|
|
throw new Error( "jQuery requires a window with a document" );
|
|
}
|
|
return factory( w );
|
|
};
|
|
} else {
|
|
factory( global );
|
|
}
|
|
|
|
// Pass this if window is not defined yet
|
|
}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
|
|
|
|
// Can't do this because several apps including ASP.NET trace
|
|
// the stack via arguments.caller.callee and Firefox dies if
|
|
// you try to trace through "use strict" call chains. (#13335)
|
|
// Support: Firefox 18+
|
|
//
|
|
|
|
var arr = [];
|
|
|
|
var slice = arr.slice;
|
|
|
|
var concat = arr.concat;
|
|
|
|
var push = arr.push;
|
|
|
|
var indexOf = arr.indexOf;
|
|
|
|
var class2type = {};
|
|
|
|
var toString = class2type.toString;
|
|
|
|
var hasOwn = class2type.hasOwnProperty;
|
|
|
|
var trim = "".trim;
|
|
|
|
var support = {};
|
|
|
|
|
|
|
|
var
|
|
// Use the correct document accordingly with window argument (sandbox)
|
|
document = window.document,
|
|
|
|
version = "2.1.0",
|
|
|
|
// Define a local copy of jQuery
|
|
jQuery = function( selector, context ) {
|
|
// The jQuery object is actually just the init constructor 'enhanced'
|
|
// Need init if jQuery is called (just allow error to be thrown if not included)
|
|
return new jQuery.fn.init( selector, context );
|
|
},
|
|
|
|
// Matches dashed string for camelizing
|
|
rmsPrefix = /^-ms-/,
|
|
rdashAlpha = /-([\da-z])/gi,
|
|
|
|
// Used by jQuery.camelCase as callback to replace()
|
|
fcamelCase = function( all, letter ) {
|
|
return letter.toUpperCase();
|
|
};
|
|
|
|
jQuery.fn = jQuery.prototype = {
|
|
// The current version of jQuery being used
|
|
jquery: version,
|
|
|
|
constructor: jQuery,
|
|
|
|
// Start with an empty selector
|
|
selector: "",
|
|
|
|
// The default length of a jQuery object is 0
|
|
length: 0,
|
|
|
|
toArray: function() {
|
|
return slice.call( this );
|
|
},
|
|
|
|
// Get the Nth element in the matched element set OR
|
|
// Get the whole matched element set as a clean array
|
|
get: function( num ) {
|
|
return num != null ?
|
|
|
|
// Return a 'clean' array
|
|
( num < 0 ? this[ num + this.length ] : this[ num ] ) :
|
|
|
|
// Return just the object
|
|
slice.call( this );
|
|
},
|
|
|
|
// Take an array of elements and push it onto the stack
|
|
// (returning the new matched element set)
|
|
pushStack: function( elems ) {
|
|
|
|
// Build a new jQuery matched element set
|
|
var ret = jQuery.merge( this.constructor(), elems );
|
|
|
|
// Add the old object onto the stack (as a reference)
|
|
ret.prevObject = this;
|
|
ret.context = this.context;
|
|
|
|
// Return the newly-formed element set
|
|
return ret;
|
|
},
|
|
|
|
// Execute a callback for every element in the matched set.
|
|
// (You can seed the arguments with an array of args, but this is
|
|
// only used internally.)
|
|
each: function( callback, args ) {
|
|
return jQuery.each( this, callback, args );
|
|
},
|
|
|
|
map: function( callback ) {
|
|
return this.pushStack( jQuery.map(this, function( elem, i ) {
|
|
return callback.call( elem, i, elem );
|
|
}));
|
|
},
|
|
|
|
slice: function() {
|
|
return this.pushStack( slice.apply( this, arguments ) );
|
|
},
|
|
|
|
first: function() {
|
|
return this.eq( 0 );
|
|
},
|
|
|
|
last: function() {
|
|
return this.eq( -1 );
|
|
},
|
|
|
|
eq: function( i ) {
|
|
var len = this.length,
|
|
j = +i + ( i < 0 ? len : 0 );
|
|
return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
|
|
},
|
|
|
|
end: function() {
|
|
return this.prevObject || this.constructor(null);
|
|
},
|
|
|
|
// For internal use only.
|
|
// Behaves like an Array's method, not like a jQuery method.
|
|
push: push,
|
|
sort: arr.sort,
|
|
splice: arr.splice
|
|
};
|
|
|
|
jQuery.extend = jQuery.fn.extend = function() {
|
|
var options, name, src, copy, copyIsArray, clone,
|
|
target = arguments[0] || {},
|
|
i = 1,
|
|
length = arguments.length,
|
|
deep = false;
|
|
|
|
// Handle a deep copy situation
|
|
if ( typeof target === "boolean" ) {
|
|
deep = target;
|
|
|
|
// skip the boolean and the target
|
|
target = arguments[ i ] || {};
|
|
i++;
|
|
}
|
|
|
|
// Handle case when target is a string or something (possible in deep copy)
|
|
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
|
|
target = {};
|
|
}
|
|
|
|
// extend jQuery itself if only one argument is passed
|
|
if ( i === length ) {
|
|
target = this;
|
|
i--;
|
|
}
|
|
|
|
for ( ; i < length; i++ ) {
|
|
// Only deal with non-null/undefined values
|
|
if ( (options = arguments[ i ]) != null ) {
|
|
// Extend the base object
|
|
for ( name in options ) {
|
|
src = target[ name ];
|
|
copy = options[ name ];
|
|
|
|
// Prevent never-ending loop
|
|
if ( target === copy ) {
|
|
continue;
|
|
}
|
|
|
|
// Recurse if we're merging plain objects or arrays
|
|
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
|
|
if ( copyIsArray ) {
|
|
copyIsArray = false;
|
|
clone = src && jQuery.isArray(src) ? src : [];
|
|
|
|
} else {
|
|
clone = src && jQuery.isPlainObject(src) ? src : {};
|
|
}
|
|
|
|
// Never move original objects, clone them
|
|
target[ name ] = jQuery.extend( deep, clone, copy );
|
|
|
|
// Don't bring in undefined values
|
|
} else if ( copy !== undefined ) {
|
|
target[ name ] = copy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return the modified object
|
|
return target;
|
|
};
|
|
|
|
jQuery.extend({
|
|
// Unique for each copy of jQuery on the page
|
|
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
|
|
|
|
// Assume jQuery is ready without the ready module
|
|
isReady: true,
|
|
|
|
error: function( msg ) {
|
|
throw new Error( msg );
|
|
},
|
|
|
|
noop: function() {},
|
|
|
|
// See test/unit/core.js for details concerning isFunction.
|
|
// Since version 1.3, DOM methods and functions like alert
|
|
// aren't supported. They return false on IE (#2968).
|
|
isFunction: function( obj ) {
|
|
return jQuery.type(obj) === "function";
|
|
},
|
|
|
|
isArray: Array.isArray,
|
|
|
|
isWindow: function( obj ) {
|
|
return obj != null && obj === obj.window;
|
|
},
|
|
|
|
isNumeric: function( obj ) {
|
|
// parseFloat NaNs numeric-cast false positives (null|true|false|"")
|
|
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
|
|
// subtraction forces infinities to NaN
|
|
return obj - parseFloat( obj ) >= 0;
|
|
},
|
|
|
|
isPlainObject: function( obj ) {
|
|
// Not plain objects:
|
|
// - Any object or value whose internal [[Class]] property is not "[object Object]"
|
|
// - DOM nodes
|
|
// - window
|
|
if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
// Support: Firefox <20
|
|
// The try/catch suppresses exceptions thrown when attempting to access
|
|
// the "constructor" property of certain host objects, ie. |window.location|
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=814622
|
|
try {
|
|
if ( obj.constructor &&
|
|
!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
|
|
return false;
|
|
}
|
|
} catch ( e ) {
|
|
return false;
|
|
}
|
|
|
|
// If the function hasn't returned already, we're confident that
|
|
// |obj| is a plain object, created by {} or constructed with new Object
|
|
return true;
|
|
},
|
|
|
|
isEmptyObject: function( obj ) {
|
|
var name;
|
|
for ( name in obj ) {
|
|
return false;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
type: function( obj ) {
|
|
if ( obj == null ) {
|
|
return obj + "";
|
|
}
|
|
// Support: Android < 4.0, iOS < 6 (functionish RegExp)
|
|
return typeof obj === "object" || typeof obj === "function" ?
|
|
class2type[ toString.call(obj) ] || "object" :
|
|
typeof obj;
|
|
},
|
|
|
|
// Evaluates a script in a global context
|
|
globalEval: function( code ) {
|
|
var script,
|
|
indirect = eval;
|
|
|
|
code = jQuery.trim( code );
|
|
|
|
if ( code ) {
|
|
// If the code includes a valid, prologue position
|
|
// strict mode pragma, execute code by injecting a
|
|
// script tag into the document.
|
|
if ( code.indexOf("use strict") === 1 ) {
|
|
script = document.createElement("script");
|
|
script.text = code;
|
|
document.head.appendChild( script ).parentNode.removeChild( script );
|
|
} else {
|
|
// Otherwise, avoid the DOM node creation, insertion
|
|
// and removal by using an indirect global eval
|
|
indirect( code );
|
|
}
|
|
}
|
|
},
|
|
|
|
// Convert dashed to camelCase; used by the css and data modules
|
|
// Microsoft forgot to hump their vendor prefix (#9572)
|
|
camelCase: function( string ) {
|
|
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
|
|
},
|
|
|
|
nodeName: function( elem, name ) {
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
|
},
|
|
|
|
// args is for internal usage only
|
|
each: function( obj, callback, args ) {
|
|
var value,
|
|
i = 0,
|
|
length = obj.length,
|
|
isArray = isArraylike( obj );
|
|
|
|
if ( args ) {
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback.apply( obj[ i ], args );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for ( i in obj ) {
|
|
value = callback.apply( obj[ i ], args );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// A special, fast, case for the most common use of each
|
|
} else {
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback.call( obj[ i ], i, obj[ i ] );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
for ( i in obj ) {
|
|
value = callback.call( obj[ i ], i, obj[ i ] );
|
|
|
|
if ( value === false ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
},
|
|
|
|
trim: function( text ) {
|
|
return text == null ? "" : trim.call( text );
|
|
},
|
|
|
|
// results is for internal usage only
|
|
makeArray: function( arr, results ) {
|
|
var ret = results || [];
|
|
|
|
if ( arr != null ) {
|
|
if ( isArraylike( Object(arr) ) ) {
|
|
jQuery.merge( ret,
|
|
typeof arr === "string" ?
|
|
[ arr ] : arr
|
|
);
|
|
} else {
|
|
push.call( ret, arr );
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
},
|
|
|
|
inArray: function( elem, arr, i ) {
|
|
return arr == null ? -1 : indexOf.call( arr, elem, i );
|
|
},
|
|
|
|
merge: function( first, second ) {
|
|
var len = +second.length,
|
|
j = 0,
|
|
i = first.length;
|
|
|
|
for ( ; j < len; j++ ) {
|
|
first[ i++ ] = second[ j ];
|
|
}
|
|
|
|
first.length = i;
|
|
|
|
return first;
|
|
},
|
|
|
|
grep: function( elems, callback, invert ) {
|
|
var callbackInverse,
|
|
matches = [],
|
|
i = 0,
|
|
length = elems.length,
|
|
callbackExpect = !invert;
|
|
|
|
// Go through the array, only saving the items
|
|
// that pass the validator function
|
|
for ( ; i < length; i++ ) {
|
|
callbackInverse = !callback( elems[ i ], i );
|
|
if ( callbackInverse !== callbackExpect ) {
|
|
matches.push( elems[ i ] );
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
},
|
|
|
|
// arg is for internal usage only
|
|
map: function( elems, callback, arg ) {
|
|
var value,
|
|
i = 0,
|
|
length = elems.length,
|
|
isArray = isArraylike( elems ),
|
|
ret = [];
|
|
|
|
// Go through the array, translating each of the items to their new values
|
|
if ( isArray ) {
|
|
for ( ; i < length; i++ ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
|
|
// Go through every key on the object,
|
|
} else {
|
|
for ( i in elems ) {
|
|
value = callback( elems[ i ], i, arg );
|
|
|
|
if ( value != null ) {
|
|
ret.push( value );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flatten any nested arrays
|
|
return concat.apply( [], ret );
|
|
},
|
|
|
|
// A global GUID counter for objects
|
|
guid: 1,
|
|
|
|
// Bind a function to a context, optionally partially applying any
|
|
// arguments.
|
|
proxy: function( fn, context ) {
|
|
var tmp, args, proxy;
|
|
|
|
if ( typeof context === "string" ) {
|
|
tmp = fn[ context ];
|
|
context = fn;
|
|
fn = tmp;
|
|
}
|
|
|
|
// Quick check to determine if target is callable, in the spec
|
|
// this throws a TypeError, but we will just return undefined.
|
|
if ( !jQuery.isFunction( fn ) ) {
|
|
return undefined;
|
|
}
|
|
|
|
// Simulated bind
|
|
args = slice.call( arguments, 2 );
|
|
proxy = function() {
|
|
return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
|
|
};
|
|
|
|
// Set the guid of unique handler to the same of original handler, so it can be removed
|
|
proxy.guid = fn.guid = fn.guid || jQuery.guid++;
|
|
|
|
return proxy;
|
|
},
|
|
|
|
now: Date.now,
|
|
|
|
// jQuery.support is not used in Core but other projects attach their
|
|
// properties to it so it needs to exist.
|
|
support: support
|
|
});
|
|
|
|
// Populate the class2type map
|
|
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
|
|
class2type[ "[object " + name + "]" ] = name.toLowerCase();
|
|
});
|
|
|
|
function isArraylike( obj ) {
|
|
var length = obj.length,
|
|
type = jQuery.type( obj );
|
|
|
|
if ( type === "function" || jQuery.isWindow( obj ) ) {
|
|
return false;
|
|
}
|
|
|
|
if ( obj.nodeType === 1 && length ) {
|
|
return true;
|
|
}
|
|
|
|
return type === "array" || length === 0 ||
|
|
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
|
}
|
|
var Sizzle =
|
|
/*!
|
|
* Sizzle CSS Selector Engine v1.10.16
|
|
* http://sizzlejs.com/
|
|
*
|
|
* Copyright 2013 jQuery Foundation, Inc. and other contributors
|
|
* Released under the MIT license
|
|
* http://jquery.org/license
|
|
*
|
|
* Date: 2014-01-13
|
|
*/
|
|
(function( window ) {
|
|
|
|
var i,
|
|
support,
|
|
Expr,
|
|
getText,
|
|
isXML,
|
|
compile,
|
|
outermostContext,
|
|
sortInput,
|
|
hasDuplicate,
|
|
|
|
// Local document vars
|
|
setDocument,
|
|
document,
|
|
docElem,
|
|
documentIsHTML,
|
|
rbuggyQSA,
|
|
rbuggyMatches,
|
|
matches,
|
|
contains,
|
|
|
|
// Instance-specific data
|
|
expando = "sizzle" + -(new Date()),
|
|
preferredDoc = window.document,
|
|
dirruns = 0,
|
|
done = 0,
|
|
classCache = createCache(),
|
|
tokenCache = createCache(),
|
|
compilerCache = createCache(),
|
|
sortOrder = function( a, b ) {
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
// General-purpose constants
|
|
strundefined = typeof undefined,
|
|
MAX_NEGATIVE = 1 << 31,
|
|
|
|
// Instance methods
|
|
hasOwn = ({}).hasOwnProperty,
|
|
arr = [],
|
|
pop = arr.pop,
|
|
push_native = arr.push,
|
|
push = arr.push,
|
|
slice = arr.slice,
|
|
// Use a stripped-down indexOf if we can't use a native one
|
|
indexOf = arr.indexOf || function( elem ) {
|
|
var i = 0,
|
|
len = this.length;
|
|
for ( ; i < len; i++ ) {
|
|
if ( this[i] === elem ) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
},
|
|
|
|
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
|
|
|
|
// Regular expressions
|
|
|
|
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
|
|
whitespace = "[\\x20\\t\\r\\n\\f]",
|
|
// http://www.w3.org/TR/css3-syntax/#characters
|
|
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
|
|
|
|
// Loosely modeled on CSS identifier characters
|
|
// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
|
|
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
|
|
identifier = characterEncoding.replace( "w", "w#" ),
|
|
|
|
// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
|
|
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
|
|
"*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
|
|
|
|
// Prefer arguments quoted,
|
|
// then not containing pseudos/brackets,
|
|
// then attribute selectors/non-parenthetical expressions,
|
|
// then anything else
|
|
// These preferences are here to reduce the number of selectors
|
|
// needing tokenize in the PSEUDO preFilter
|
|
pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
|
|
|
|
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
|
|
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
|
|
|
|
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
|
|
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
|
|
|
|
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
|
|
|
|
rpseudo = new RegExp( pseudos ),
|
|
ridentifier = new RegExp( "^" + identifier + "$" ),
|
|
|
|
matchExpr = {
|
|
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
|
|
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
|
|
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
|
|
"ATTR": new RegExp( "^" + attributes ),
|
|
"PSEUDO": new RegExp( "^" + pseudos ),
|
|
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
|
|
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
|
|
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
|
|
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
|
|
// For use in libraries implementing .is()
|
|
// We use this for POS matching in `select`
|
|
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
|
|
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
|
|
},
|
|
|
|
rinputs = /^(?:input|select|textarea|button)$/i,
|
|
rheader = /^h\d$/i,
|
|
|
|
rnative = /^[^{]+\{\s*\[native \w/,
|
|
|
|
// Easily-parseable/retrievable ID or TAG or CLASS selectors
|
|
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
|
|
|
|
rsibling = /[+~]/,
|
|
rescape = /'|\\/g,
|
|
|
|
// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
|
|
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
|
|
funescape = function( _, escaped, escapedWhitespace ) {
|
|
var high = "0x" + escaped - 0x10000;
|
|
// NaN means non-codepoint
|
|
// Support: Firefox
|
|
// Workaround erroneous numeric interpretation of +"0x"
|
|
return high !== high || escapedWhitespace ?
|
|
escaped :
|
|
high < 0 ?
|
|
// BMP codepoint
|
|
String.fromCharCode( high + 0x10000 ) :
|
|
// Supplemental Plane codepoint (surrogate pair)
|
|
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
|
|
};
|
|
|
|
// Optimize for push.apply( _, NodeList )
|
|
try {
|
|
push.apply(
|
|
(arr = slice.call( preferredDoc.childNodes )),
|
|
preferredDoc.childNodes
|
|
);
|
|
// Support: Android<4.0
|
|
// Detect silently failing push.apply
|
|
arr[ preferredDoc.childNodes.length ].nodeType;
|
|
} catch ( e ) {
|
|
push = { apply: arr.length ?
|
|
|
|
// Leverage slice if possible
|
|
function( target, els ) {
|
|
push_native.apply( target, slice.call(els) );
|
|
} :
|
|
|
|
// Support: IE<9
|
|
// Otherwise append directly
|
|
function( target, els ) {
|
|
var j = target.length,
|
|
i = 0;
|
|
// Can't trust NodeList.length
|
|
while ( (target[j++] = els[i++]) ) {}
|
|
target.length = j - 1;
|
|
}
|
|
};
|
|
}
|
|
|
|
function Sizzle( selector, context, results, seed ) {
|
|
var match, elem, m, nodeType,
|
|
// QSA vars
|
|
i, groups, old, nid, newContext, newSelector;
|
|
|
|
if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
|
|
setDocument( context );
|
|
}
|
|
|
|
context = context || document;
|
|
results = results || [];
|
|
|
|
if ( !selector || typeof selector !== "string" ) {
|
|
return results;
|
|
}
|
|
|
|
if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
|
|
return [];
|
|
}
|
|
|
|
if ( documentIsHTML && !seed ) {
|
|
|
|
// Shortcuts
|
|
if ( (match = rquickExpr.exec( selector )) ) {
|
|
// Speed-up: Sizzle("#ID")
|
|
if ( (m = match[1]) ) {
|
|
if ( nodeType === 9 ) {
|
|
elem = context.getElementById( m );
|
|
// Check parentNode to catch when Blackberry 4.6 returns
|
|
// nodes that are no longer in the document (jQuery #6963)
|
|
if ( elem && elem.parentNode ) {
|
|
// Handle the case where IE, Opera, and Webkit return items
|
|
// by name instead of ID
|
|
if ( elem.id === m ) {
|
|
results.push( elem );
|
|
return results;
|
|
}
|
|
} else {
|
|
return results;
|
|
}
|
|
} else {
|
|
// Context is not a document
|
|
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
|
|
contains( context, elem ) && elem.id === m ) {
|
|
results.push( elem );
|
|
return results;
|
|
}
|
|
}
|
|
|
|
// Speed-up: Sizzle("TAG")
|
|
} else if ( match[2] ) {
|
|
push.apply( results, context.getElementsByTagName( selector ) );
|
|
return results;
|
|
|
|
// Speed-up: Sizzle(".CLASS")
|
|
} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
|
|
push.apply( results, context.getElementsByClassName( m ) );
|
|
return results;
|
|
}
|
|
}
|
|
|
|
// QSA path
|
|
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
|
|
nid = old = expando;
|
|
newContext = context;
|
|
newSelector = nodeType === 9 && selector;
|
|
|
|
// qSA works strangely on Element-rooted queries
|
|
// We can work around this by specifying an extra ID on the root
|
|
// and working up from there (Thanks to Andrew Dupont for the technique)
|
|
// IE 8 doesn't work on object elements
|
|
if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
|
|
groups = tokenize( selector );
|
|
|
|
if ( (old = context.getAttribute("id")) ) {
|
|
nid = old.replace( rescape, "\\$&" );
|
|
} else {
|
|
context.setAttribute( "id", nid );
|
|
}
|
|
nid = "[id='" + nid + "'] ";
|
|
|
|
i = groups.length;
|
|
while ( i-- ) {
|
|
groups[i] = nid + toSelector( groups[i] );
|
|
}
|
|
newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
|
|
newSelector = groups.join(",");
|
|
}
|
|
|
|
if ( newSelector ) {
|
|
try {
|
|
push.apply( results,
|
|
newContext.querySelectorAll( newSelector )
|
|
);
|
|
return results;
|
|
} catch(qsaError) {
|
|
} finally {
|
|
if ( !old ) {
|
|
context.removeAttribute("id");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// All others
|
|
return select( selector.replace( rtrim, "$1" ), context, results, seed );
|
|
}
|
|
|
|
/**
|
|
* Create key-value caches of limited size
|
|
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
|
|
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
|
|
* deleting the oldest entry
|
|
*/
|
|
function createCache() {
|
|
var keys = [];
|
|
|
|
function cache( key, value ) {
|
|
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
|
|
if ( keys.push( key + " " ) > Expr.cacheLength ) {
|
|
// Only keep the most recent entries
|
|
delete cache[ keys.shift() ];
|
|
}
|
|
return (cache[ key + " " ] = value);
|
|
}
|
|
return cache;
|
|
}
|
|
|
|
/**
|
|
* Mark a function for special use by Sizzle
|
|
* @param {Function} fn The function to mark
|
|
*/
|
|
function markFunction( fn ) {
|
|
fn[ expando ] = true;
|
|
return fn;
|
|
}
|
|
|
|
/**
|
|
* Support testing using an element
|
|
* @param {Function} fn Passed the created div and expects a boolean result
|
|
*/
|
|
function assert( fn ) {
|
|
var div = document.createElement("div");
|
|
|
|
try {
|
|
return !!fn( div );
|
|
} catch (e) {
|
|
return false;
|
|
} finally {
|
|
// Remove from its parent by default
|
|
if ( div.parentNode ) {
|
|
div.parentNode.removeChild( div );
|
|
}
|
|
// release memory in IE
|
|
div = null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the same handler for all of the specified attrs
|
|
* @param {String} attrs Pipe-separated list of attributes
|
|
* @param {Function} handler The method that will be applied
|
|
*/
|
|
function addHandle( attrs, handler ) {
|
|
var arr = attrs.split("|"),
|
|
i = attrs.length;
|
|
|
|
while ( i-- ) {
|
|
Expr.attrHandle[ arr[i] ] = handler;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks document order of two siblings
|
|
* @param {Element} a
|
|
* @param {Element} b
|
|
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
|
|
*/
|
|
function siblingCheck( a, b ) {
|
|
var cur = b && a,
|
|
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
|
|
( ~b.sourceIndex || MAX_NEGATIVE ) -
|
|
( ~a.sourceIndex || MAX_NEGATIVE );
|
|
|
|
// Use IE sourceIndex if available on both nodes
|
|
if ( diff ) {
|
|
return diff;
|
|
}
|
|
|
|
// Check if b follows a
|
|
if ( cur ) {
|
|
while ( (cur = cur.nextSibling) ) {
|
|
if ( cur === b ) {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return a ? 1 : -1;
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for input types
|
|
* @param {String} type
|
|
*/
|
|
function createInputPseudo( type ) {
|
|
return function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return name === "input" && elem.type === type;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for buttons
|
|
* @param {String} type
|
|
*/
|
|
function createButtonPseudo( type ) {
|
|
return function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return (name === "input" || name === "button") && elem.type === type;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Returns a function to use in pseudos for positionals
|
|
* @param {Function} fn
|
|
*/
|
|
function createPositionalPseudo( fn ) {
|
|
return markFunction(function( argument ) {
|
|
argument = +argument;
|
|
return markFunction(function( seed, matches ) {
|
|
var j,
|
|
matchIndexes = fn( [], seed.length, argument ),
|
|
i = matchIndexes.length;
|
|
|
|
// Match elements found at the specified indexes
|
|
while ( i-- ) {
|
|
if ( seed[ (j = matchIndexes[i]) ] ) {
|
|
seed[j] = !(matches[j] = seed[j]);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks a node for validity as a Sizzle context
|
|
* @param {Element|Object=} context
|
|
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
|
|
*/
|
|
function testContext( context ) {
|
|
return context && typeof context.getElementsByTagName !== strundefined && context;
|
|
}
|
|
|
|
// Expose support vars for convenience
|
|
support = Sizzle.support = {};
|
|
|
|
/**
|
|
* Detects XML nodes
|
|
* @param {Element|Object} elem An element or a document
|
|
* @returns {Boolean} True iff elem is a non-HTML XML node
|
|
*/
|
|
isXML = Sizzle.isXML = function( elem ) {
|
|
// documentElement is verified for cases where it doesn't yet exist
|
|
// (such as loading iframes in IE - #4833)
|
|
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
|
|
return documentElement ? documentElement.nodeName !== "HTML" : false;
|
|
};
|
|
|
|
/**
|
|
* Sets document-related variables once based on the current document
|
|
* @param {Element|Object} [doc] An element or document object to use to set the document
|
|
* @returns {Object} Returns the current document
|
|
*/
|
|
setDocument = Sizzle.setDocument = function( node ) {
|
|
var hasCompare,
|
|
doc = node ? node.ownerDocument || node : preferredDoc,
|
|
parent = doc.defaultView;
|
|
|
|
// If no document and documentElement is available, return
|
|
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
|
|
return document;
|
|
}
|
|
|
|
// Set our document
|
|
document = doc;
|
|
docElem = doc.documentElement;
|
|
|
|
// Support tests
|
|
documentIsHTML = !isXML( doc );
|
|
|
|
// Support: IE>8
|
|
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
|
|
// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
|
|
// IE6-8 do not support the defaultView property so parent will be undefined
|
|
if ( parent && parent !== parent.top ) {
|
|
// IE11 does not have attachEvent, so all must suffer
|
|
if ( parent.addEventListener ) {
|
|
parent.addEventListener( "unload", function() {
|
|
setDocument();
|
|
}, false );
|
|
} else if ( parent.attachEvent ) {
|
|
parent.attachEvent( "onunload", function() {
|
|
setDocument();
|
|
});
|
|
}
|
|
}
|
|
|
|
/* Attributes
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Support: IE<8
|
|
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
|
|
support.attributes = assert(function( div ) {
|
|
div.className = "i";
|
|
return !div.getAttribute("className");
|
|
});
|
|
|
|
/* getElement(s)By*
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Check if getElementsByTagName("*") returns only elements
|
|
support.getElementsByTagName = assert(function( div ) {
|
|
div.appendChild( doc.createComment("") );
|
|
return !div.getElementsByTagName("*").length;
|
|
});
|
|
|
|
// Check if getElementsByClassName can be trusted
|
|
support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
|
|
div.innerHTML = "<div class='a'></div><div class='a i'></div>";
|
|
|
|
// Support: Safari<4
|
|
// Catch class over-caching
|
|
div.firstChild.className = "i";
|
|
// Support: Opera<10
|
|
// Catch gEBCN failure to find non-leading classes
|
|
return div.getElementsByClassName("i").length === 2;
|
|
});
|
|
|
|
// Support: IE<10
|
|
// Check if getElementById returns elements by name
|
|
// The broken getElementById methods don't pick up programatically-set names,
|
|
// so use a roundabout getElementsByName test
|
|
support.getById = assert(function( div ) {
|
|
docElem.appendChild( div ).id = expando;
|
|
return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
|
|
});
|
|
|
|
// ID find and filter
|
|
if ( support.getById ) {
|
|
Expr.find["ID"] = function( id, context ) {
|
|
if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
|
|
var m = context.getElementById( id );
|
|
// Check parentNode to catch when Blackberry 4.6 returns
|
|
// nodes that are no longer in the document #6963
|
|
return m && m.parentNode ? [m] : [];
|
|
}
|
|
};
|
|
Expr.filter["ID"] = function( id ) {
|
|
var attrId = id.replace( runescape, funescape );
|
|
return function( elem ) {
|
|
return elem.getAttribute("id") === attrId;
|
|
};
|
|
};
|
|
} else {
|
|
// Support: IE6/7
|
|
// getElementById is not reliable as a find shortcut
|
|
delete Expr.find["ID"];
|
|
|
|
Expr.filter["ID"] = function( id ) {
|
|
var attrId = id.replace( runescape, funescape );
|
|
return function( elem ) {
|
|
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
|
|
return node && node.value === attrId;
|
|
};
|
|
};
|
|
}
|
|
|
|
// Tag
|
|
Expr.find["TAG"] = support.getElementsByTagName ?
|
|
function( tag, context ) {
|
|
if ( typeof context.getElementsByTagName !== strundefined ) {
|
|
return context.getElementsByTagName( tag );
|
|
}
|
|
} :
|
|
function( tag, context ) {
|
|
var elem,
|
|
tmp = [],
|
|
i = 0,
|
|
results = context.getElementsByTagName( tag );
|
|
|
|
// Filter out possible comments
|
|
if ( tag === "*" ) {
|
|
while ( (elem = results[i++]) ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
tmp.push( elem );
|
|
}
|
|
}
|
|
|
|
return tmp;
|
|
}
|
|
return results;
|
|
};
|
|
|
|
// Class
|
|
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
|
|
if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
|
|
return context.getElementsByClassName( className );
|
|
}
|
|
};
|
|
|
|
/* QSA/matchesSelector
|
|
---------------------------------------------------------------------- */
|
|
|
|
// QSA and matchesSelector support
|
|
|
|
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
|
|
rbuggyMatches = [];
|
|
|
|
// qSa(:focus) reports false when true (Chrome 21)
|
|
// We allow this because of a bug in IE8/9 that throws an error
|
|
// whenever `document.activeElement` is accessed on an iframe
|
|
// So, we allow :focus to pass through QSA all the time to avoid the IE error
|
|
// See http://bugs.jquery.com/ticket/13378
|
|
rbuggyQSA = [];
|
|
|
|
if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
|
|
// Build QSA regex
|
|
// Regex strategy adopted from Diego Perini
|
|
assert(function( div ) {
|
|
// Select is set to empty string on purpose
|
|
// This is to test IE's treatment of not explicitly
|
|
// setting a boolean content attribute,
|
|
// since its presence should be enough
|
|
// http://bugs.jquery.com/ticket/12359
|
|
div.innerHTML = "<select t=''><option selected=''></option></select>";
|
|
|
|
// Support: IE8, Opera 10-12
|
|
// Nothing should be selected when empty strings follow ^= or $= or *=
|
|
if ( div.querySelectorAll("[t^='']").length ) {
|
|
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
|
|
}
|
|
|
|
// Support: IE8
|
|
// Boolean attributes and "value" are not treated correctly
|
|
if ( !div.querySelectorAll("[selected]").length ) {
|
|
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
|
|
}
|
|
|
|
// Webkit/Opera - :checked should return selected option elements
|
|
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
|
|
// IE8 throws error here and will not see later tests
|
|
if ( !div.querySelectorAll(":checked").length ) {
|
|
rbuggyQSA.push(":checked");
|
|
}
|
|
});
|
|
|
|
assert(function( div ) {
|
|
// Support: Windows 8 Native Apps
|
|
// The type and name attributes are restricted during .innerHTML assignment
|
|
var input = doc.createElement("input");
|
|
input.setAttribute( "type", "hidden" );
|
|
div.appendChild( input ).setAttribute( "name", "D" );
|
|
|
|
// Support: IE8
|
|
// Enforce case-sensitivity of name attribute
|
|
if ( div.querySelectorAll("[name=d]").length ) {
|
|
rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
|
|
}
|
|
|
|
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
|
|
// IE8 throws error here and will not see later tests
|
|
if ( !div.querySelectorAll(":enabled").length ) {
|
|
rbuggyQSA.push( ":enabled", ":disabled" );
|
|
}
|
|
|
|
// Opera 10-11 does not throw on post-comma invalid pseudos
|
|
div.querySelectorAll("*,:x");
|
|
rbuggyQSA.push(",.*:");
|
|
});
|
|
}
|
|
|
|
if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector ||
|
|
docElem.mozMatchesSelector ||
|
|
docElem.oMatchesSelector ||
|
|
docElem.msMatchesSelector) )) ) {
|
|
|
|
assert(function( div ) {
|
|
// Check to see if it's possible to do matchesSelector
|
|
// on a disconnected node (IE 9)
|
|
support.disconnectedMatch = matches.call( div, "div" );
|
|
|
|
// This should fail with an exception
|
|
// Gecko does not error, returns false instead
|
|
matches.call( div, "[s!='']:x" );
|
|
rbuggyMatches.push( "!=", pseudos );
|
|
});
|
|
}
|
|
|
|
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
|
|
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
|
|
|
|
/* Contains
|
|
---------------------------------------------------------------------- */
|
|
hasCompare = rnative.test( docElem.compareDocumentPosition );
|
|
|
|
// Element contains another
|
|
// Purposefully does not implement inclusive descendent
|
|
// As in, an element does not contain itself
|
|
contains = hasCompare || rnative.test( docElem.contains ) ?
|
|
function( a, b ) {
|
|
var adown = a.nodeType === 9 ? a.documentElement : a,
|
|
bup = b && b.parentNode;
|
|
return a === bup || !!( bup && bup.nodeType === 1 && (
|
|
adown.contains ?
|
|
adown.contains( bup ) :
|
|
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
|
|
));
|
|
} :
|
|
function( a, b ) {
|
|
if ( b ) {
|
|
while ( (b = b.parentNode) ) {
|
|
if ( b === a ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
/* Sorting
|
|
---------------------------------------------------------------------- */
|
|
|
|
// Document order sorting
|
|
sortOrder = hasCompare ?
|
|
function( a, b ) {
|
|
|
|
// Flag for duplicate removal
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
return 0;
|
|
}
|
|
|
|
// Sort on method existence if only one input has compareDocumentPosition
|
|
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
|
|
if ( compare ) {
|
|
return compare;
|
|
}
|
|
|
|
// Calculate position if both inputs belong to the same document
|
|
compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
|
|
a.compareDocumentPosition( b ) :
|
|
|
|
// Otherwise we know they are disconnected
|
|
1;
|
|
|
|
// Disconnected nodes
|
|
if ( compare & 1 ||
|
|
(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
|
|
|
|
// Choose the first element that is related to our preferred document
|
|
if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
|
|
return -1;
|
|
}
|
|
if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
|
|
return 1;
|
|
}
|
|
|
|
// Maintain original order
|
|
return sortInput ?
|
|
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
|
|
0;
|
|
}
|
|
|
|
return compare & 4 ? -1 : 1;
|
|
} :
|
|
function( a, b ) {
|
|
// Exit early if the nodes are identical
|
|
if ( a === b ) {
|
|
hasDuplicate = true;
|
|
return 0;
|
|
}
|
|
|
|
var cur,
|
|
i = 0,
|
|
aup = a.parentNode,
|
|
bup = b.parentNode,
|
|
ap = [ a ],
|
|
bp = [ b ];
|
|
|
|
// Parentless nodes are either documents or disconnected
|
|
if ( !aup || !bup ) {
|
|
return a === doc ? -1 :
|
|
b === doc ? 1 :
|
|
aup ? -1 :
|
|
bup ? 1 :
|
|
sortInput ?
|
|
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
|
|
0;
|
|
|
|
// If the nodes are siblings, we can do a quick check
|
|
} else if ( aup === bup ) {
|
|
return siblingCheck( a, b );
|
|
}
|
|
|
|
// Otherwise we need full lists of their ancestors for comparison
|
|
cur = a;
|
|
while ( (cur = cur.parentNode) ) {
|
|
ap.unshift( cur );
|
|
}
|
|
cur = b;
|
|
while ( (cur = cur.parentNode) ) {
|
|
bp.unshift( cur );
|
|
}
|
|
|
|
// Walk down the tree looking for a discrepancy
|
|
while ( ap[i] === bp[i] ) {
|
|
i++;
|
|
}
|
|
|
|
return i ?
|
|
// Do a sibling check if the nodes have a common ancestor
|
|
siblingCheck( ap[i], bp[i] ) :
|
|
|
|
// Otherwise nodes in our document sort first
|
|
ap[i] === preferredDoc ? -1 :
|
|
bp[i] === preferredDoc ? 1 :
|
|
0;
|
|
};
|
|
|
|
return doc;
|
|
};
|
|
|
|
Sizzle.matches = function( expr, elements ) {
|
|
return Sizzle( expr, null, null, elements );
|
|
};
|
|
|
|
Sizzle.matchesSelector = function( elem, expr ) {
|
|
// Set document vars if needed
|
|
if ( ( elem.ownerDocument || elem ) !== document ) {
|
|
setDocument( elem );
|
|
}
|
|
|
|
// Make sure that attribute selectors are quoted
|
|
expr = expr.replace( rattributeQuotes, "='$1']" );
|
|
|
|
if ( support.matchesSelector && documentIsHTML &&
|
|
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
|
|
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
|
|
|
|
try {
|
|
var ret = matches.call( elem, expr );
|
|
|
|
// IE 9's matchesSelector returns false on disconnected nodes
|
|
if ( ret || support.disconnectedMatch ||
|
|
// As well, disconnected nodes are said to be in a document
|
|
// fragment in IE 9
|
|
elem.document && elem.document.nodeType !== 11 ) {
|
|
return ret;
|
|
}
|
|
} catch(e) {}
|
|
}
|
|
|
|
return Sizzle( expr, document, null, [elem] ).length > 0;
|
|
};
|
|
|
|
Sizzle.contains = function( context, elem ) {
|
|
// Set document vars if needed
|
|
if ( ( context.ownerDocument || context ) !== document ) {
|
|
setDocument( context );
|
|
}
|
|
return contains( context, elem );
|
|
};
|
|
|
|
Sizzle.attr = function( elem, name ) {
|
|
// Set document vars if needed
|
|
if ( ( elem.ownerDocument || elem ) !== document ) {
|
|
setDocument( elem );
|
|
}
|
|
|
|
var fn = Expr.attrHandle[ name.toLowerCase() ],
|
|
// Don't get fooled by Object.prototype properties (jQuery #13807)
|
|
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
|
|
fn( elem, name, !documentIsHTML ) :
|
|
undefined;
|
|
|
|
return val !== undefined ?
|
|
val :
|
|
support.attributes || !documentIsHTML ?
|
|
elem.getAttribute( name ) :
|
|
(val = elem.getAttributeNode(name)) && val.specified ?
|
|
val.value :
|
|
null;
|
|
};
|
|
|
|
Sizzle.error = function( msg ) {
|
|
throw new Error( "Syntax error, unrecognized expression: " + msg );
|
|
};
|
|
|
|
/**
|
|
* Document sorting and removing duplicates
|
|
* @param {ArrayLike} results
|
|
*/
|
|
Sizzle.uniqueSort = function( results ) {
|
|
var elem,
|
|
duplicates = [],
|
|
j = 0,
|
|
i = 0;
|
|
|
|
// Unless we *know* we can detect duplicates, assume their presence
|
|
hasDuplicate = !support.detectDuplicates;
|
|
sortInput = !support.sortStable && results.slice( 0 );
|
|
results.sort( sortOrder );
|
|
|
|
if ( hasDuplicate ) {
|
|
while ( (elem = results[i++]) ) {
|
|
if ( elem === results[ i ] ) {
|
|
j = duplicates.push( i );
|
|
}
|
|
}
|
|
while ( j-- ) {
|
|
results.splice( duplicates[ j ], 1 );
|
|
}
|
|
}
|
|
|
|
// Clear input after sorting to release objects
|
|
// See https://github.com/jquery/sizzle/pull/225
|
|
sortInput = null;
|
|
|
|
return results;
|
|
};
|
|
|
|
/**
|
|
* Utility function for retrieving the text value of an array of DOM nodes
|
|
* @param {Array|Element} elem
|
|
*/
|
|
getText = Sizzle.getText = function( elem ) {
|
|
var node,
|
|
ret = "",
|
|
i = 0,
|
|
nodeType = elem.nodeType;
|
|
|
|
if ( !nodeType ) {
|
|
// If no nodeType, this is expected to be an array
|
|
while ( (node = elem[i++]) ) {
|
|
// Do not traverse comment nodes
|
|
ret += getText( node );
|
|
}
|
|
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
|
|
// Use textContent for elements
|
|
// innerText usage removed for consistency of new lines (jQuery #11153)
|
|
if ( typeof elem.textContent === "string" ) {
|
|
return elem.textContent;
|
|
} else {
|
|
// Traverse its children
|
|
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
|
|
ret += getText( elem );
|
|
}
|
|
}
|
|
} else if ( nodeType === 3 || nodeType === 4 ) {
|
|
return elem.nodeValue;
|
|
}
|
|
// Do not include comment or processing instruction nodes
|
|
|
|
return ret;
|
|
};
|
|
|
|
Expr = Sizzle.selectors = {
|
|
|
|
// Can be adjusted by the user
|
|
cacheLength: 50,
|
|
|
|
createPseudo: markFunction,
|
|
|
|
match: matchExpr,
|
|
|
|
attrHandle: {},
|
|
|
|
find: {},
|
|
|
|
relative: {
|
|
">": { dir: "parentNode", first: true },
|
|
" ": { dir: "parentNode" },
|
|
"+": { dir: "previousSibling", first: true },
|
|
"~": { dir: "previousSibling" }
|
|
},
|
|
|
|
preFilter: {
|
|
"ATTR": function( match ) {
|
|
match[1] = match[1].replace( runescape, funescape );
|
|
|
|
// Move the given value to match[3] whether quoted or unquoted
|
|
match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
|
|
|
|
if ( match[2] === "~=" ) {
|
|
match[3] = " " + match[3] + " ";
|
|
}
|
|
|
|
return match.slice( 0, 4 );
|
|
},
|
|
|
|
"CHILD": function( match ) {
|
|
/* matches from matchExpr["CHILD"]
|
|
1 type (only|nth|...)
|
|
2 what (child|of-type)
|
|
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
|
|
4 xn-component of xn+y argument ([+-]?\d*n|)
|
|
5 sign of xn-component
|
|
6 x of xn-component
|
|
7 sign of y-component
|
|
8 y of y-component
|
|
*/
|
|
match[1] = match[1].toLowerCase();
|
|
|
|
if ( match[1].slice( 0, 3 ) === "nth" ) {
|
|
// nth-* requires argument
|
|
if ( !match[3] ) {
|
|
Sizzle.error( match[0] );
|
|
}
|
|
|
|
// numeric x and y parameters for Expr.filter.CHILD
|
|
// remember that false/true cast respectively to 0/1
|
|
match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
|
|
match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
|
|
|
|
// other types prohibit arguments
|
|
} else if ( match[3] ) {
|
|
Sizzle.error( match[0] );
|
|
}
|
|
|
|
return match;
|
|
},
|
|
|
|
"PSEUDO": function( match ) {
|
|
var excess,
|
|
unquoted = !match[5] && match[2];
|
|
|
|
if ( matchExpr["CHILD"].test( match[0] ) ) {
|
|
return null;
|
|
}
|
|
|
|
// Accept quoted arguments as-is
|
|
if ( match[3] && match[4] !== undefined ) {
|
|
match[2] = match[4];
|
|
|
|
// Strip excess characters from unquoted arguments
|
|
} else if ( unquoted && rpseudo.test( unquoted ) &&
|
|
// Get excess from tokenize (recursively)
|
|
(excess = tokenize( unquoted, true )) &&
|
|
// advance to the next closing parenthesis
|
|
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
|
|
|
|
// excess is a negative index
|
|
match[0] = match[0].slice( 0, excess );
|
|
match[2] = unquoted.slice( 0, excess );
|
|
}
|
|
|
|
// Return only captures needed by the pseudo filter method (type and argument)
|
|
return match.slice( 0, 3 );
|
|
}
|
|
},
|
|
|
|
filter: {
|
|
|
|
"TAG": function( nodeNameSelector ) {
|
|
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
|
|
return nodeNameSelector === "*" ?
|
|
function() { return true; } :
|
|
function( elem ) {
|
|
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
|
|
};
|
|
},
|
|
|
|
"CLASS": function( className ) {
|
|
var pattern = classCache[ className + " " ];
|
|
|
|
return pattern ||
|
|
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
|
|
classCache( className, function( elem ) {
|
|
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
|
|
});
|
|
},
|
|
|
|
"ATTR": function( name, operator, check ) {
|
|
return function( elem ) {
|
|
var result = Sizzle.attr( elem, name );
|
|
|
|
if ( result == null ) {
|
|
return operator === "!=";
|
|
}
|
|
if ( !operator ) {
|
|
return true;
|
|
}
|
|
|
|
result += "";
|
|
|
|
return operator === "=" ? result === check :
|
|
operator === "!=" ? result !== check :
|
|
operator === "^=" ? check && result.indexOf( check ) === 0 :
|
|
operator === "*=" ? check && result.indexOf( check ) > -1 :
|
|
operator === "$=" ? check && result.slice( -check.length ) === check :
|
|
operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
|
|
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
|
|
false;
|
|
};
|
|
},
|
|
|
|
"CHILD": function( type, what, argument, first, last ) {
|
|
var simple = type.slice( 0, 3 ) !== "nth",
|
|
forward = type.slice( -4 ) !== "last",
|
|
ofType = what === "of-type";
|
|
|
|
return first === 1 && last === 0 ?
|
|
|
|
// Shortcut for :nth-*(n)
|
|
function( elem ) {
|
|
return !!elem.parentNode;
|
|
} :
|
|
|
|
function( elem, context, xml ) {
|
|
var cache, outerCache, node, diff, nodeIndex, start,
|
|
dir = simple !== forward ? "nextSibling" : "previousSibling",
|
|
parent = elem.parentNode,
|
|
name = ofType && elem.nodeName.toLowerCase(),
|
|
useCache = !xml && !ofType;
|
|
|
|
if ( parent ) {
|
|
|
|
// :(first|last|only)-(child|of-type)
|
|
if ( simple ) {
|
|
while ( dir ) {
|
|
node = elem;
|
|
while ( (node = node[ dir ]) ) {
|
|
if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
|
|
return false;
|
|
}
|
|
}
|
|
// Reverse direction for :only-* (if we haven't yet done so)
|
|
start = dir = type === "only" && !start && "nextSibling";
|
|
}
|
|
return true;
|
|
}
|
|
|
|
start = [ forward ? parent.firstChild : parent.lastChild ];
|
|
|
|
// non-xml :nth-child(...) stores cache data on `parent`
|
|
if ( forward && useCache ) {
|
|
// Seek `elem` from a previously-cached index
|
|
outerCache = parent[ expando ] || (parent[ expando ] = {});
|
|
cache = outerCache[ type ] || [];
|
|
nodeIndex = cache[0] === dirruns && cache[1];
|
|
diff = cache[0] === dirruns && cache[2];
|
|
node = nodeIndex && parent.childNodes[ nodeIndex ];
|
|
|
|
while ( (node = ++nodeIndex && node && node[ dir ] ||
|
|
|
|
// Fallback to seeking `elem` from the start
|
|
(diff = nodeIndex = 0) || start.pop()) ) {
|
|
|
|
// When found, cache indexes on `parent` and break
|
|
if ( node.nodeType === 1 && ++diff && node === elem ) {
|
|
outerCache[ type ] = [ dirruns, nodeIndex, diff ];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Use previously-cached element index if available
|
|
} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
|
|
diff = cache[1];
|
|
|
|
// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
|
|
} else {
|
|
// Use the same loop as above to seek `elem` from the start
|
|
while ( (node = ++nodeIndex && node && node[ dir ] ||
|
|
(diff = nodeIndex = 0) || start.pop()) ) {
|
|
|
|
if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
|
|
// Cache the index of each encountered element
|
|
if ( useCache ) {
|
|
(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
|
|
}
|
|
|
|
if ( node === elem ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Incorporate the offset, then check against cycle size
|
|
diff -= last;
|
|
return diff === first || ( diff % first === 0 && diff / first >= 0 );
|
|
}
|
|
};
|
|
},
|
|
|
|
"PSEUDO": function( pseudo, argument ) {
|
|
// pseudo-class names are case-insensitive
|
|
// http://www.w3.org/TR/selectors/#pseudo-classes
|
|
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
|
|
// Remember that setFilters inherits from pseudos
|
|
var args,
|
|
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
|
|
Sizzle.error( "unsupported pseudo: " + pseudo );
|
|
|
|
// The user may use createPseudo to indicate that
|
|
// arguments are needed to create the filter function
|
|
// just as Sizzle does
|
|
if ( fn[ expando ] ) {
|
|
return fn( argument );
|
|
}
|
|
|
|
// But maintain support for old signatures
|
|
if ( fn.length > 1 ) {
|
|
args = [ pseudo, pseudo, "", argument ];
|
|
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
|
|
markFunction(function( seed, matches ) {
|
|
var idx,
|
|
matched = fn( seed, argument ),
|
|
i = matched.length;
|
|
while ( i-- ) {
|
|
idx = indexOf.call( seed, matched[i] );
|
|
seed[ idx ] = !( matches[ idx ] = matched[i] );
|
|
}
|
|
}) :
|
|
function( elem ) {
|
|
return fn( elem, 0, args );
|
|
};
|
|
}
|
|
|
|
return fn;
|
|
}
|
|
},
|
|
|
|
pseudos: {
|
|
// Potentially complex pseudos
|
|
"not": markFunction(function( selector ) {
|
|
// Trim the selector passed to compile
|
|
// to avoid treating leading and trailing
|
|
// spaces as combinators
|
|
var input = [],
|
|
results = [],
|
|
matcher = compile( selector.replace( rtrim, "$1" ) );
|
|
|
|
return matcher[ expando ] ?
|
|
markFunction(function( seed, matches, context, xml ) {
|
|
var elem,
|
|
unmatched = matcher( seed, null, xml, [] ),
|
|
i = seed.length;
|
|
|
|
// Match elements unmatched by `matcher`
|
|
while ( i-- ) {
|
|
if ( (elem = unmatched[i]) ) {
|
|
seed[i] = !(matches[i] = elem);
|
|
}
|
|
}
|
|
}) :
|
|
function( elem, context, xml ) {
|
|
input[0] = elem;
|
|
matcher( input, null, xml, results );
|
|
return !results.pop();
|
|
};
|
|
}),
|
|
|
|
"has": markFunction(function( selector ) {
|
|
return function( elem ) {
|
|
return Sizzle( selector, elem ).length > 0;
|
|
};
|
|
}),
|
|
|
|
"contains": markFunction(function( text ) {
|
|
return function( elem ) {
|
|
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
|
|
};
|
|
}),
|
|
|
|
// "Whether an element is represented by a :lang() selector
|
|
// is based solely on the element's language value
|
|
// being equal to the identifier C,
|
|
// or beginning with the identifier C immediately followed by "-".
|
|
// The matching of C against the element's language value is performed case-insensitively.
|
|
// The identifier C does not have to be a valid language name."
|
|
// http://www.w3.org/TR/selectors/#lang-pseudo
|
|
"lang": markFunction( function( lang ) {
|
|
// lang value must be a valid identifier
|
|
if ( !ridentifier.test(lang || "") ) {
|
|
Sizzle.error( "unsupported lang: " + lang );
|
|
}
|
|
lang = lang.replace( runescape, funescape ).toLowerCase();
|
|
return function( elem ) {
|
|
var elemLang;
|
|
do {
|
|
if ( (elemLang = documentIsHTML ?
|
|
elem.lang :
|
|
elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
|
|
|
|
elemLang = elemLang.toLowerCase();
|
|
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
|
|
}
|
|
} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
|
|
return false;
|
|
};
|
|
}),
|
|
|
|
// Miscellaneous
|
|
"target": function( elem ) {
|
|
var hash = window.location && window.location.hash;
|
|
return hash && hash.slice( 1 ) === elem.id;
|
|
},
|
|
|
|
"root": function( elem ) {
|
|
return elem === docElem;
|
|
},
|
|
|
|
"focus": function( elem ) {
|
|
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
|
|
},
|
|
|
|
// Boolean properties
|
|
"enabled": function( elem ) {
|
|
return elem.disabled === false;
|
|
},
|
|
|
|
"disabled": function( elem ) {
|
|
return elem.disabled === true;
|
|
},
|
|
|
|
"checked": function( elem ) {
|
|
// In CSS3, :checked should return both checked and selected elements
|
|
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
|
|
var nodeName = elem.nodeName.toLowerCase();
|
|
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
|
|
},
|
|
|
|
"selected": function( elem ) {
|
|
// Accessing this property makes selected-by-default
|
|
// options in Safari work properly
|
|
if ( elem.parentNode ) {
|
|
elem.parentNode.selectedIndex;
|
|
}
|
|
|
|
return elem.selected === true;
|
|
},
|
|
|
|
// Contents
|
|
"empty": function( elem ) {
|
|
// http://www.w3.org/TR/selectors/#empty-pseudo
|
|
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
|
|
// but not by others (comment: 8; processing instruction: 7; etc.)
|
|
// nodeType < 6 works because attributes (2) do not appear as children
|
|
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
|
|
if ( elem.nodeType < 6 ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
},
|
|
|
|
"parent": function( elem ) {
|
|
return !Expr.pseudos["empty"]( elem );
|
|
},
|
|
|
|
// Element/input types
|
|
"header": function( elem ) {
|
|
return rheader.test( elem.nodeName );
|
|
},
|
|
|
|
"input": function( elem ) {
|
|
return rinputs.test( elem.nodeName );
|
|
},
|
|
|
|
"button": function( elem ) {
|
|
var name = elem.nodeName.toLowerCase();
|
|
return name === "input" && elem.type === "button" || name === "button";
|
|
},
|
|
|
|
"text": function( elem ) {
|
|
var attr;
|
|
return elem.nodeName.toLowerCase() === "input" &&
|
|
elem.type === "text" &&
|
|
|
|
// Support: IE<8
|
|
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
|
|
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
|
|
},
|
|
|
|
// Position-in-collection
|
|
"first": createPositionalPseudo(function() {
|
|
return [ 0 ];
|
|
}),
|
|
|
|
"last": createPositionalPseudo(function( matchIndexes, length ) {
|
|
return [ length - 1 ];
|
|
}),
|
|
|
|
"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
return [ argument < 0 ? argument + length : argument ];
|
|
}),
|
|
|
|
"even": createPositionalPseudo(function( matchIndexes, length ) {
|
|
var i = 0;
|
|
for ( ; i < length; i += 2 ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"odd": createPositionalPseudo(function( matchIndexes, length ) {
|
|
var i = 1;
|
|
for ( ; i < length; i += 2 ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
var i = argument < 0 ? argument + length : argument;
|
|
for ( ; --i >= 0; ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
}),
|
|
|
|
"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
|
|
var i = argument < 0 ? argument + length : argument;
|
|
for ( ; ++i < length; ) {
|
|
matchIndexes.push( i );
|
|
}
|
|
return matchIndexes;
|
|
})
|
|
}
|
|
};
|
|
|
|
Expr.pseudos["nth"] = Expr.pseudos["eq"];
|
|
|
|
// Add button/input type pseudos
|
|
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
|
|
Expr.pseudos[ i ] = createInputPseudo( i );
|
|
}
|
|
for ( i in { submit: true, reset: true } ) {
|
|
Expr.pseudos[ i ] = createButtonPseudo( i );
|
|
}
|
|
|
|
// Easy API for creating new setFilters
|
|
function setFilters() {}
|
|
setFilters.prototype = Expr.filters = Expr.pseudos;
|
|
Expr.setFilters = new setFilters();
|
|
|
|
function tokenize( selector, parseOnly ) {
|
|
var matched, match, tokens, type,
|
|
soFar, groups, preFilters,
|
|
cached = tokenCache[ selector + " " ];
|
|
|
|
if ( cached ) {
|
|
return parseOnly ? 0 : cached.slice( 0 );
|
|
}
|
|
|
|
soFar = selector;
|
|
groups = [];
|
|
preFilters = Expr.preFilter;
|
|
|
|
while ( soFar ) {
|
|
|
|
// Comma and first run
|
|
if ( !matched || (match = rcomma.exec( soFar )) ) {
|
|
if ( match ) {
|
|
// Don't consume trailing commas as valid
|
|
soFar = soFar.slice( match[0].length ) || soFar;
|
|
}
|
|
groups.push( (tokens = []) );
|
|
}
|
|
|
|
matched = false;
|
|
|
|
// Combinators
|
|
if ( (match = rcombinators.exec( soFar )) ) {
|
|
matched = match.shift();
|
|
tokens.push({
|
|
value: matched,
|
|
// Cast descendant combinators to space
|
|
type: match[0].replace( rtrim, " " )
|
|
});
|
|
soFar = soFar.slice( matched.length );
|
|
}
|
|
|
|
// Filters
|
|
for ( type in Expr.filter ) {
|
|
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
|
|
(match = preFilters[ type ]( match ))) ) {
|
|
matched = match.shift();
|
|
tokens.push({
|
|
value: matched,
|
|
type: type,
|
|
matches: match
|
|
});
|
|
soFar = soFar.slice( matched.length );
|
|
}
|
|
}
|
|
|
|
if ( !matched ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return the length of the invalid excess
|
|
// if we're just parsing
|
|
// Otherwise, throw an error or return tokens
|
|
return parseOnly ?
|
|
soFar.length :
|
|
soFar ?
|
|
Sizzle.error( selector ) :
|
|
// Cache the tokens
|
|
tokenCache( selector, groups ).slice( 0 );
|
|
}
|
|
|
|
function toSelector( tokens ) {
|
|
var i = 0,
|
|
len = tokens.length,
|
|
selector = "";
|
|
for ( ; i < len; i++ ) {
|
|
selector += tokens[i].value;
|
|
}
|
|
return selector;
|
|
}
|
|
|
|
function addCombinator( matcher, combinator, base ) {
|
|
var dir = combinator.dir,
|
|
checkNonElements = base && dir === "parentNode",
|
|
doneName = done++;
|
|
|
|
return combinator.first ?
|
|
// Check against closest ancestor/preceding element
|
|
function( elem, context, xml ) {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
return matcher( elem, context, xml );
|
|
}
|
|
}
|
|
} :
|
|
|
|
// Check against all ancestor/preceding elements
|
|
function( elem, context, xml ) {
|
|
var oldCache, outerCache,
|
|
newCache = [ dirruns, doneName ];
|
|
|
|
// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
|
|
if ( xml ) {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
if ( matcher( elem, context, xml ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
while ( (elem = elem[ dir ]) ) {
|
|
if ( elem.nodeType === 1 || checkNonElements ) {
|
|
outerCache = elem[ expando ] || (elem[ expando ] = {});
|
|
if ( (oldCache = outerCache[ dir ]) &&
|
|
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
|
|
|
|
// Assign to newCache so results back-propagate to previous elements
|
|
return (newCache[ 2 ] = oldCache[ 2 ]);
|
|
} else {
|
|
// Reuse newcache so results back-propagate to previous elements
|
|
outerCache[ dir ] = newCache;
|
|
|
|
// A match means we're done; a fail means we have to keep checking
|
|
if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function elementMatcher( matchers ) {
|
|
return matchers.length > 1 ?
|
|
function( elem, context, xml ) {
|
|
var i = matchers.length;
|
|
while ( i-- ) {
|
|
if ( !matchers[i]( elem, context, xml ) ) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} :
|
|
matchers[0];
|
|
}
|
|
|
|
function condense( unmatched, map, filter, context, xml ) {
|
|
var elem,
|
|
newUnmatched = [],
|
|
i = 0,
|
|
len = unmatched.length,
|
|
mapped = map != null;
|
|
|
|
for ( ; i < len; i++ ) {
|
|
if ( (elem = unmatched[i]) ) {
|
|
if ( !filter || filter( elem, context, xml ) ) {
|
|
newUnmatched.push( elem );
|
|
if ( mapped ) {
|
|
map.push( i );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return newUnmatched;
|
|
}
|
|
|
|
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
|
|
if ( postFilter && !postFilter[ expando ] ) {
|
|
postFilter = setMatcher( postFilter );
|
|
}
|
|
if ( postFinder && !postFinder[ expando ] ) {
|
|
postFinder = setMatcher( postFinder, postSelector );
|
|
}
|
|
return markFunction(function( seed, results, context, xml ) {
|
|
var temp, i, elem,
|
|
preMap = [],
|
|
postMap = [],
|
|
preexisting = results.length,
|
|
|
|
// Get initial elements from seed or context
|
|
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
|
|
|
|
// Prefilter to get matcher input, preserving a map for seed-results synchronization
|
|
matcherIn = preFilter && ( seed || !selector ) ?
|
|
condense( elems, preMap, preFilter, context, xml ) :
|
|
elems,
|
|
|
|
matcherOut = matcher ?
|
|
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
|
|
postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
|
|
|
|
// ...intermediate processing is necessary
|
|
[] :
|
|
|
|
// ...otherwise use results directly
|
|
results :
|
|
matcherIn;
|
|
|
|
// Find primary matches
|
|
if ( matcher ) {
|
|
matcher( matcherIn, matcherOut, context, xml );
|
|
}
|
|
|
|
// Apply postFilter
|
|
if ( postFilter ) {
|
|
temp = condense( matcherOut, postMap );
|
|
postFilter( temp, [], context, xml );
|
|
|
|
// Un-match failing elements by moving them back to matcherIn
|
|
i = temp.length;
|
|
while ( i-- ) {
|
|
if ( (elem = temp[i]) ) {
|
|
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( seed ) {
|
|
if ( postFinder || preFilter ) {
|
|
if ( postFinder ) {
|
|
// Get the final matcherOut by condensing this intermediate into postFinder contexts
|
|
temp = [];
|
|
i = matcherOut.length;
|
|
while ( i-- ) {
|
|
if ( (elem = matcherOut[i]) ) {
|
|
// Restore matcherIn since elem is not yet a final match
|
|
temp.push( (matcherIn[i] = elem) );
|
|
}
|
|
}
|
|
postFinder( null, (matcherOut = []), temp, xml );
|
|
}
|
|
|
|
// Move matched elements from seed to results to keep them synchronized
|
|
i = matcherOut.length;
|
|
while ( i-- ) {
|
|
if ( (elem = matcherOut[i]) &&
|
|
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
|
|
|
|
seed[temp] = !(results[temp] = elem);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add elements to results, through postFinder if defined
|
|
} else {
|
|
matcherOut = condense(
|
|
matcherOut === results ?
|
|
matcherOut.splice( preexisting, matcherOut.length ) :
|
|
matcherOut
|
|
);
|
|
if ( postFinder ) {
|
|
postFinder( null, results, matcherOut, xml );
|
|
} else {
|
|
push.apply( results, matcherOut );
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function matcherFromTokens( tokens ) {
|
|
var checkContext, matcher, j,
|
|
len = tokens.length,
|
|
leadingRelative = Expr.relative[ tokens[0].type ],
|
|
implicitRelative = leadingRelative || Expr.relative[" "],
|
|
i = leadingRelative ? 1 : 0,
|
|
|
|
// The foundational matcher ensures that elements are reachable from top-level context(s)
|
|
matchContext = addCombinator( function( elem ) {
|
|
return elem === checkContext;
|
|
}, implicitRelative, true ),
|
|
matchAnyContext = addCombinator( function( elem ) {
|
|
return indexOf.call( checkContext, elem ) > -1;
|
|
}, implicitRelative, true ),
|
|
matchers = [ function( elem, context, xml ) {
|
|
return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
|
|
(checkContext = context).nodeType ?
|
|
matchContext( elem, context, xml ) :
|
|
matchAnyContext( elem, context, xml ) );
|
|
} ];
|
|
|
|
for ( ; i < len; i++ ) {
|
|
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
|
|
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
|
|
} else {
|
|
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
|
|
|
|
// Return special upon seeing a positional matcher
|
|
if ( matcher[ expando ] ) {
|
|
// Find the next relative operator (if any) for proper handling
|
|
j = ++i;
|
|
for ( ; j < len; j++ ) {
|
|
if ( Expr.relative[ tokens[j].type ] ) {
|
|
break;
|
|
}
|
|
}
|
|
return setMatcher(
|
|
i > 1 && elementMatcher( matchers ),
|
|
i > 1 && toSelector(
|
|
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
|
|
tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
|
|
).replace( rtrim, "$1" ),
|
|
matcher,
|
|
i < j && matcherFromTokens( tokens.slice( i, j ) ),
|
|
j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
|
|
j < len && toSelector( tokens )
|
|
);
|
|
}
|
|
matchers.push( matcher );
|
|
}
|
|
}
|
|
|
|
return elementMatcher( matchers );
|
|
}
|
|
|
|
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
|
|
var bySet = setMatchers.length > 0,
|
|
byElement = elementMatchers.length > 0,
|
|
superMatcher = function( seed, context, xml, results, outermost ) {
|
|
var elem, j, matcher,
|
|
matchedCount = 0,
|
|
i = "0",
|
|
unmatched = seed && [],
|
|
setMatched = [],
|
|
contextBackup = outermostContext,
|
|
// We must always have either seed elements or outermost context
|
|
elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
|
|
// Use integer dirruns iff this is the outermost matcher
|
|
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
|
|
len = elems.length;
|
|
|
|
if ( outermost ) {
|
|
outermostContext = context !== document && context;
|
|
}
|
|
|
|
// Add elements passing elementMatchers directly to results
|
|
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
|
|
// Support: IE<9, Safari
|
|
// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
|
|
for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
|
|
if ( byElement && elem ) {
|
|
j = 0;
|
|
while ( (matcher = elementMatchers[j++]) ) {
|
|
if ( matcher( elem, context, xml ) ) {
|
|
results.push( elem );
|
|
break;
|
|
}
|
|
}
|
|
if ( outermost ) {
|
|
dirruns = dirrunsUnique;
|
|
}
|
|
}
|
|
|
|
// Track unmatched elements for set filters
|
|
if ( bySet ) {
|
|
// They will have gone through all possible matchers
|
|
if ( (elem = !matcher && elem) ) {
|
|
matchedCount--;
|
|
}
|
|
|
|
// Lengthen the array for every element, matched or not
|
|
if ( seed ) {
|
|
unmatched.push( elem );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply set filters to unmatched elements
|
|
matchedCount += i;
|
|
if ( bySet && i !== matchedCount ) {
|
|
j = 0;
|
|
while ( (matcher = setMatchers[j++]) ) {
|
|
matcher( unmatched, setMatched, context, xml );
|
|
}
|
|
|
|
if ( seed ) {
|
|
// Reintegrate element matches to eliminate the need for sorting
|
|
if ( matchedCount > 0 ) {
|
|
while ( i-- ) {
|
|
if ( !(unmatched[i] || setMatched[i]) ) {
|
|
setMatched[i] = pop.call( results );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Discard index placeholder values to get only actual matches
|
|
setMatched = condense( setMatched );
|
|
}
|
|
|
|
// Add matches to results
|
|
push.apply( results, setMatched );
|
|
|
|
// Seedless set matches succeeding multiple successful matchers stipulate sorting
|
|
if ( outermost && !seed && setMatched.length > 0 &&
|
|
( matchedCount + setMatchers.length ) > 1 ) {
|
|
|
|
Sizzle.uniqueSort( results );
|
|
}
|
|
}
|
|
|
|
// Override manipulation of globals by nested matchers
|
|
if ( outermost ) {
|
|
dirruns = dirrunsUnique;
|
|
outermostContext = contextBackup;
|
|
}
|
|
|
|
return unmatched;
|
|
};
|
|
|
|
return bySet ?
|
|
markFunction( superMatcher ) :
|
|
superMatcher;
|
|
}
|
|
|
|
compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
|
|
var i,
|
|
setMatchers = [],
|
|
elementMatchers = [],
|
|
cached = compilerCache[ selector + " " ];
|
|
|
|
if ( !cached ) {
|
|
// Generate a function of recursive functions that can be used to check each element
|
|
if ( !group ) {
|
|
group = tokenize( selector );
|
|
}
|
|
i = group.length;
|
|
while ( i-- ) {
|
|
cached = matcherFromTokens( group[i] );
|
|
if ( cached[ expando ] ) {
|
|
setMatchers.push( cached );
|
|
} else {
|
|
elementMatchers.push( cached );
|
|
}
|
|
}
|
|
|
|
// Cache the compiled function
|
|
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
|
|
}
|
|
return cached;
|
|
};
|
|
|
|
function multipleContexts( selector, contexts, results ) {
|
|
var i = 0,
|
|
len = contexts.length;
|
|
for ( ; i < len; i++ ) {
|
|
Sizzle( selector, contexts[i], results );
|
|
}
|
|
return results;
|
|
}
|
|
|
|
function select( selector, context, results, seed ) {
|
|
var i, tokens, token, type, find,
|
|
match = tokenize( selector );
|
|
|
|
if ( !seed ) {
|
|
// Try to minimize operations if there is only one group
|
|
if ( match.length === 1 ) {
|
|
|
|
// Take a shortcut and set the context if the root selector is an ID
|
|
tokens = match[0] = match[0].slice( 0 );
|
|
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
|
|
support.getById && context.nodeType === 9 && documentIsHTML &&
|
|
Expr.relative[ tokens[1].type ] ) {
|
|
|
|
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
|
|
if ( !context ) {
|
|
return results;
|
|
}
|
|
selector = selector.slice( tokens.shift().value.length );
|
|
}
|
|
|
|
// Fetch a seed set for right-to-left matching
|
|
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
|
|
while ( i-- ) {
|
|
token = tokens[i];
|
|
|
|
// Abort if we hit a combinator
|
|
if ( Expr.relative[ (type = token.type) ] ) {
|
|
break;
|
|
}
|
|
if ( (find = Expr.find[ type ]) ) {
|
|
// Search, expanding context for leading sibling combinators
|
|
if ( (seed = find(
|
|
token.matches[0].replace( runescape, funescape ),
|
|
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
|
|
)) ) {
|
|
|
|
// If seed is empty or no tokens remain, we can return early
|
|
tokens.splice( i, 1 );
|
|
selector = seed.length && toSelector( tokens );
|
|
if ( !selector ) {
|
|
push.apply( results, seed );
|
|
return results;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compile and execute a filtering function
|
|
// Provide `match` to avoid retokenization if we modified the selector above
|
|
compile( selector, match )(
|
|
seed,
|
|
context,
|
|
!documentIsHTML,
|
|
results,
|
|
rsibling.test( selector ) && testContext( context.parentNode ) || context
|
|
);
|
|
return results;
|
|
}
|
|
|
|
// One-time assignments
|
|
|
|
// Sort stability
|
|
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
|
|
|
|
// Support: Chrome<14
|
|
// Always assume duplicates if they aren't passed to the comparison function
|
|
support.detectDuplicates = !!hasDuplicate;
|
|
|
|
// Initialize against the default document
|
|
setDocument();
|
|
|
|
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
|
|
// Detached nodes confoundingly follow *each other*
|
|
support.sortDetached = assert(function( div1 ) {
|
|
// Should return 1, but returns 4 (following)
|
|
return div1.compareDocumentPosition( document.createElement("div") ) & 1;
|
|
});
|
|
|
|
// Support: IE<8
|
|
// Prevent attribute/property "interpolation"
|
|
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
|
|
if ( !assert(function( div ) {
|
|
div.innerHTML = "<a href='#'></a>";
|
|
return div.firstChild.getAttribute("href") === "#" ;
|
|
}) ) {
|
|
addHandle( "type|href|height|width", function( elem, name, isXML ) {
|
|
if ( !isXML ) {
|
|
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
|
|
}
|
|
});
|
|
}
|
|
|
|
// Support: IE<9
|
|
// Use defaultValue in place of getAttribute("value")
|
|
if ( !support.attributes || !assert(function( div ) {
|
|
div.innerHTML = "<input/>";
|
|
div.firstChild.setAttribute( "value", "" );
|
|
return div.firstChild.getAttribute( "value" ) === "";
|
|
}) ) {
|
|
addHandle( "value", function( elem, name, isXML ) {
|
|
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
|
|
return elem.defaultValue;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Support: IE<9
|
|
// Use getAttributeNode to fetch booleans when getAttribute lies
|
|
if ( !assert(function( div ) {
|
|
return div.getAttribute("disabled") == null;
|
|
}) ) {
|
|
addHandle( booleans, function( elem, name, isXML ) {
|
|
var val;
|
|
if ( !isXML ) {
|
|
return elem[ name ] === true ? name.toLowerCase() :
|
|
(val = elem.getAttributeNode( name )) && val.specified ?
|
|
val.value :
|
|
null;
|
|
}
|
|
});
|
|
}
|
|
|
|
return Sizzle;
|
|
|
|
})( window );
|
|
|
|
|
|
|
|
jQuery.find = Sizzle;
|
|
jQuery.expr = Sizzle.selectors;
|
|
jQuery.expr[":"] = jQuery.expr.pseudos;
|
|
jQuery.unique = Sizzle.uniqueSort;
|
|
jQuery.text = Sizzle.getText;
|
|
jQuery.isXMLDoc = Sizzle.isXML;
|
|
jQuery.contains = Sizzle.contains;
|
|
|
|
|
|
|
|
var rneedsContext = jQuery.expr.match.needsContext;
|
|
|
|
var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
|
|
|
|
|
|
|
|
var risSimple = /^.[^:#\[\.,]*$/;
|
|
|
|
// Implement the identical functionality for filter and not
|
|
function winnow( elements, qualifier, not ) {
|
|
if ( jQuery.isFunction( qualifier ) ) {
|
|
return jQuery.grep( elements, function( elem, i ) {
|
|
/* jshint -W018 */
|
|
return !!qualifier.call( elem, i, elem ) !== not;
|
|
});
|
|
|
|
}
|
|
|
|
if ( qualifier.nodeType ) {
|
|
return jQuery.grep( elements, function( elem ) {
|
|
return ( elem === qualifier ) !== not;
|
|
});
|
|
|
|
}
|
|
|
|
if ( typeof qualifier === "string" ) {
|
|
if ( risSimple.test( qualifier ) ) {
|
|
return jQuery.filter( qualifier, elements, not );
|
|
}
|
|
|
|
qualifier = jQuery.filter( qualifier, elements );
|
|
}
|
|
|
|
return jQuery.grep( elements, function( elem ) {
|
|
return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
|
|
});
|
|
}
|
|
|
|
jQuery.filter = function( expr, elems, not ) {
|
|
var elem = elems[ 0 ];
|
|
|
|
if ( not ) {
|
|
expr = ":not(" + expr + ")";
|
|
}
|
|
|
|
return elems.length === 1 && elem.nodeType === 1 ?
|
|
jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
|
|
jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
|
|
return elem.nodeType === 1;
|
|
}));
|
|
};
|
|
|
|
jQuery.fn.extend({
|
|
find: function( selector ) {
|
|
var i,
|
|
len = this.length,
|
|
ret = [],
|
|
self = this;
|
|
|
|
if ( typeof selector !== "string" ) {
|
|
return this.pushStack( jQuery( selector ).filter(function() {
|
|
for ( i = 0; i < len; i++ ) {
|
|
if ( jQuery.contains( self[ i ], this ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
}) );
|
|
}
|
|
|
|
for ( i = 0; i < len; i++ ) {
|
|
jQuery.find( selector, self[ i ], ret );
|
|
}
|
|
|
|
// Needed because $( selector, context ) becomes $( context ).find( selector )
|
|
ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
|
|
ret.selector = this.selector ? this.selector + " " + selector : selector;
|
|
return ret;
|
|
},
|
|
filter: function( selector ) {
|
|
return this.pushStack( winnow(this, selector || [], false) );
|
|
},
|
|
not: function( selector ) {
|
|
return this.pushStack( winnow(this, selector || [], true) );
|
|
},
|
|
is: function( selector ) {
|
|
return !!winnow(
|
|
this,
|
|
|
|
// If this is a positional/relative selector, check membership in the returned set
|
|
// so $("p:first").is("p:last") won't return true for a doc with two "p".
|
|
typeof selector === "string" && rneedsContext.test( selector ) ?
|
|
jQuery( selector ) :
|
|
selector || [],
|
|
false
|
|
).length;
|
|
}
|
|
});
|
|
|
|
|
|
// Initialize a jQuery object
|
|
|
|
|
|
// A central reference to the root jQuery(document)
|
|
var rootjQuery,
|
|
|
|
// A simple way to check for HTML strings
|
|
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
|
|
// Strict HTML recognition (#11290: must start with <)
|
|
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
|
|
|
|
init = jQuery.fn.init = function( selector, context ) {
|
|
var match, elem;
|
|
|
|
// HANDLE: $(""), $(null), $(undefined), $(false)
|
|
if ( !selector ) {
|
|
return this;
|
|
}
|
|
|
|
// Handle HTML strings
|
|
if ( typeof selector === "string" ) {
|
|
if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
|
|
// Assume that strings that start and end with <> are HTML and skip the regex check
|
|
match = [ null, selector, null ];
|
|
|
|
} else {
|
|
match = rquickExpr.exec( selector );
|
|
}
|
|
|
|
// Match html or make sure no context is specified for #id
|
|
if ( match && (match[1] || !context) ) {
|
|
|
|
// HANDLE: $(html) -> $(array)
|
|
if ( match[1] ) {
|
|
context = context instanceof jQuery ? context[0] : context;
|
|
|
|
// scripts is true for back-compat
|
|
// Intentionally let the error be thrown if parseHTML is not present
|
|
jQuery.merge( this, jQuery.parseHTML(
|
|
match[1],
|
|
context && context.nodeType ? context.ownerDocument || context : document,
|
|
true
|
|
) );
|
|
|
|
// HANDLE: $(html, props)
|
|
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
|
|
for ( match in context ) {
|
|
// Properties of context are called as methods if possible
|
|
if ( jQuery.isFunction( this[ match ] ) ) {
|
|
this[ match ]( context[ match ] );
|
|
|
|
// ...and otherwise set as attributes
|
|
} else {
|
|
this.attr( match, context[ match ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
|
|
// HANDLE: $(#id)
|
|
} else {
|
|
elem = document.getElementById( match[2] );
|
|
|
|
// Check parentNode to catch when Blackberry 4.6 returns
|
|
// nodes that are no longer in the document #6963
|
|
if ( elem && elem.parentNode ) {
|
|
// Inject the element directly into the jQuery object
|
|
this.length = 1;
|
|
this[0] = elem;
|
|
}
|
|
|
|
this.context = document;
|
|
this.selector = selector;
|
|
return this;
|
|
}
|
|
|
|
// HANDLE: $(expr, $(...))
|
|
} else if ( !context || context.jquery ) {
|
|
return ( context || rootjQuery ).find( selector );
|
|
|
|
// HANDLE: $(expr, context)
|
|
// (which is just equivalent to: $(context).find(expr)
|
|
} else {
|
|
return this.constructor( context ).find( selector );
|
|
}
|
|
|
|
// HANDLE: $(DOMElement)
|
|
} else if ( selector.nodeType ) {
|
|
this.context = this[0] = selector;
|
|
this.length = 1;
|
|
return this;
|
|
|
|
// HANDLE: $(function)
|
|
// Shortcut for document ready
|
|
} else if ( jQuery.isFunction( selector ) ) {
|
|
return typeof rootjQuery.ready !== "undefined" ?
|
|
rootjQuery.ready( selector ) :
|
|
// Execute immediately if ready is not present
|
|
selector( jQuery );
|
|
}
|
|
|
|
if ( selector.selector !== undefined ) {
|
|
this.selector = selector.selector;
|
|
this.context = selector.context;
|
|
}
|
|
|
|
return jQuery.makeArray( selector, this );
|
|
};
|
|
|
|
// Give the init function the jQuery prototype for later instantiation
|
|
init.prototype = jQuery.fn;
|
|
|
|
// Initialize central reference
|
|
rootjQuery = jQuery( document );
|
|
|
|
|
|
var rparentsprev = /^(?:parents|prev(?:Until|All))/,
|
|
// methods guaranteed to produce a unique set when starting from a unique set
|
|
guaranteedUnique = {
|
|
children: true,
|
|
contents: true,
|
|
next: true,
|
|
prev: true
|
|
};
|
|
|
|
jQuery.extend({
|
|
dir: function( elem, dir, until ) {
|
|
var matched = [],
|
|
truncate = until !== undefined;
|
|
|
|
while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
if ( truncate && jQuery( elem ).is( until ) ) {
|
|
break;
|
|
}
|
|
matched.push( elem );
|
|
}
|
|
}
|
|
return matched;
|
|
},
|
|
|
|
sibling: function( n, elem ) {
|
|
var matched = [];
|
|
|
|
for ( ; n; n = n.nextSibling ) {
|
|
if ( n.nodeType === 1 && n !== elem ) {
|
|
matched.push( n );
|
|
}
|
|
}
|
|
|
|
return matched;
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
has: function( target ) {
|
|
var targets = jQuery( target, this ),
|
|
l = targets.length;
|
|
|
|
return this.filter(function() {
|
|
var i = 0;
|
|
for ( ; i < l; i++ ) {
|
|
if ( jQuery.contains( this, targets[i] ) ) {
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
},
|
|
|
|
closest: function( selectors, context ) {
|
|
var cur,
|
|
i = 0,
|
|
l = this.length,
|
|
matched = [],
|
|
pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
|
|
jQuery( selectors, context || this.context ) :
|
|
0;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
|
|
// Always skip document fragments
|
|
if ( cur.nodeType < 11 && (pos ?
|
|
pos.index(cur) > -1 :
|
|
|
|
// Don't pass non-elements to Sizzle
|
|
cur.nodeType === 1 &&
|
|
jQuery.find.matchesSelector(cur, selectors)) ) {
|
|
|
|
matched.push( cur );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
|
|
},
|
|
|
|
// Determine the position of an element within
|
|
// the matched set of elements
|
|
index: function( elem ) {
|
|
|
|
// No argument, return index in parent
|
|
if ( !elem ) {
|
|
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
|
|
}
|
|
|
|
// index in selector
|
|
if ( typeof elem === "string" ) {
|
|
return indexOf.call( jQuery( elem ), this[ 0 ] );
|
|
}
|
|
|
|
// Locate the position of the desired element
|
|
return indexOf.call( this,
|
|
|
|
// If it receives a jQuery object, the first element is used
|
|
elem.jquery ? elem[ 0 ] : elem
|
|
);
|
|
},
|
|
|
|
add: function( selector, context ) {
|
|
return this.pushStack(
|
|
jQuery.unique(
|
|
jQuery.merge( this.get(), jQuery( selector, context ) )
|
|
)
|
|
);
|
|
},
|
|
|
|
addBack: function( selector ) {
|
|
return this.add( selector == null ?
|
|
this.prevObject : this.prevObject.filter(selector)
|
|
);
|
|
}
|
|
});
|
|
|
|
function sibling( cur, dir ) {
|
|
while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
|
|
return cur;
|
|
}
|
|
|
|
jQuery.each({
|
|
parent: function( elem ) {
|
|
var parent = elem.parentNode;
|
|
return parent && parent.nodeType !== 11 ? parent : null;
|
|
},
|
|
parents: function( elem ) {
|
|
return jQuery.dir( elem, "parentNode" );
|
|
},
|
|
parentsUntil: function( elem, i, until ) {
|
|
return jQuery.dir( elem, "parentNode", until );
|
|
},
|
|
next: function( elem ) {
|
|
return sibling( elem, "nextSibling" );
|
|
},
|
|
prev: function( elem ) {
|
|
return sibling( elem, "previousSibling" );
|
|
},
|
|
nextAll: function( elem ) {
|
|
return jQuery.dir( elem, "nextSibling" );
|
|
},
|
|
prevAll: function( elem ) {
|
|
return jQuery.dir( elem, "previousSibling" );
|
|
},
|
|
nextUntil: function( elem, i, until ) {
|
|
return jQuery.dir( elem, "nextSibling", until );
|
|
},
|
|
prevUntil: function( elem, i, until ) {
|
|
return jQuery.dir( elem, "previousSibling", until );
|
|
},
|
|
siblings: function( elem ) {
|
|
return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
|
|
},
|
|
children: function( elem ) {
|
|
return jQuery.sibling( elem.firstChild );
|
|
},
|
|
contents: function( elem ) {
|
|
return elem.contentDocument || jQuery.merge( [], elem.childNodes );
|
|
}
|
|
}, function( name, fn ) {
|
|
jQuery.fn[ name ] = function( until, selector ) {
|
|
var matched = jQuery.map( this, fn, until );
|
|
|
|
if ( name.slice( -5 ) !== "Until" ) {
|
|
selector = until;
|
|
}
|
|
|
|
if ( selector && typeof selector === "string" ) {
|
|
matched = jQuery.filter( selector, matched );
|
|
}
|
|
|
|
if ( this.length > 1 ) {
|
|
// Remove duplicates
|
|
if ( !guaranteedUnique[ name ] ) {
|
|
jQuery.unique( matched );
|
|
}
|
|
|
|
// Reverse order for parents* and prev-derivatives
|
|
if ( rparentsprev.test( name ) ) {
|
|
matched.reverse();
|
|
}
|
|
}
|
|
|
|
return this.pushStack( matched );
|
|
};
|
|
});
|
|
var rnotwhite = (/\S+/g);
|
|
|
|
|
|
|
|
// String to Object options format cache
|
|
var optionsCache = {};
|
|
|
|
// Convert String-formatted options into Object-formatted ones and store in cache
|
|
function createOptions( options ) {
|
|
var object = optionsCache[ options ] = {};
|
|
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
|
|
object[ flag ] = true;
|
|
});
|
|
return object;
|
|
}
|
|
|
|
/*
|
|
* Create a callback list using the following parameters:
|
|
*
|
|
* options: an optional list of space-separated options that will change how
|
|
* the callback list behaves or a more traditional option object
|
|
*
|
|
* By default a callback list will act like an event callback list and can be
|
|
* "fired" multiple times.
|
|
*
|
|
* Possible options:
|
|
*
|
|
* once: will ensure the callback list can only be fired once (like a Deferred)
|
|
*
|
|
* memory: will keep track of previous values and will call any callback added
|
|
* after the list has been fired right away with the latest "memorized"
|
|
* values (like a Deferred)
|
|
*
|
|
* unique: will ensure a callback can only be added once (no duplicate in the list)
|
|
*
|
|
* stopOnFalse: interrupt callings when a callback returns false
|
|
*
|
|
*/
|
|
jQuery.Callbacks = function( options ) {
|
|
|
|
// Convert options from String-formatted to Object-formatted if needed
|
|
// (we check in cache first)
|
|
options = typeof options === "string" ?
|
|
( optionsCache[ options ] || createOptions( options ) ) :
|
|
jQuery.extend( {}, options );
|
|
|
|
var // Last fire value (for non-forgettable lists)
|
|
memory,
|
|
// Flag to know if list was already fired
|
|
fired,
|
|
// Flag to know if list is currently firing
|
|
firing,
|
|
// First callback to fire (used internally by add and fireWith)
|
|
firingStart,
|
|
// End of the loop when firing
|
|
firingLength,
|
|
// Index of currently firing callback (modified by remove if needed)
|
|
firingIndex,
|
|
// Actual callback list
|
|
list = [],
|
|
// Stack of fire calls for repeatable lists
|
|
stack = !options.once && [],
|
|
// Fire callbacks
|
|
fire = function( data ) {
|
|
memory = options.memory && data;
|
|
fired = true;
|
|
firingIndex = firingStart || 0;
|
|
firingStart = 0;
|
|
firingLength = list.length;
|
|
firing = true;
|
|
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
|
|
if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
|
|
memory = false; // To prevent further calls using add
|
|
break;
|
|
}
|
|
}
|
|
firing = false;
|
|
if ( list ) {
|
|
if ( stack ) {
|
|
if ( stack.length ) {
|
|
fire( stack.shift() );
|
|
}
|
|
} else if ( memory ) {
|
|
list = [];
|
|
} else {
|
|
self.disable();
|
|
}
|
|
}
|
|
},
|
|
// Actual Callbacks object
|
|
self = {
|
|
// Add a callback or a collection of callbacks to the list
|
|
add: function() {
|
|
if ( list ) {
|
|
// First, we save the current length
|
|
var start = list.length;
|
|
(function add( args ) {
|
|
jQuery.each( args, function( _, arg ) {
|
|
var type = jQuery.type( arg );
|
|
if ( type === "function" ) {
|
|
if ( !options.unique || !self.has( arg ) ) {
|
|
list.push( arg );
|
|
}
|
|
} else if ( arg && arg.length && type !== "string" ) {
|
|
// Inspect recursively
|
|
add( arg );
|
|
}
|
|
});
|
|
})( arguments );
|
|
// Do we need to add the callbacks to the
|
|
// current firing batch?
|
|
if ( firing ) {
|
|
firingLength = list.length;
|
|
// With memory, if we're not firing then
|
|
// we should call right away
|
|
} else if ( memory ) {
|
|
firingStart = start;
|
|
fire( memory );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
// Remove a callback from the list
|
|
remove: function() {
|
|
if ( list ) {
|
|
jQuery.each( arguments, function( _, arg ) {
|
|
var index;
|
|
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
|
|
list.splice( index, 1 );
|
|
// Handle firing indexes
|
|
if ( firing ) {
|
|
if ( index <= firingLength ) {
|
|
firingLength--;
|
|
}
|
|
if ( index <= firingIndex ) {
|
|
firingIndex--;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return this;
|
|
},
|
|
// Check if a given callback is in the list.
|
|
// If no argument is given, return whether or not list has callbacks attached.
|
|
has: function( fn ) {
|
|
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
|
|
},
|
|
// Remove all callbacks from the list
|
|
empty: function() {
|
|
list = [];
|
|
firingLength = 0;
|
|
return this;
|
|
},
|
|
// Have the list do nothing anymore
|
|
disable: function() {
|
|
list = stack = memory = undefined;
|
|
return this;
|
|
},
|
|
// Is it disabled?
|
|
disabled: function() {
|
|
return !list;
|
|
},
|
|
// Lock the list in its current state
|
|
lock: function() {
|
|
stack = undefined;
|
|
if ( !memory ) {
|
|
self.disable();
|
|
}
|
|
return this;
|
|
},
|
|
// Is it locked?
|
|
locked: function() {
|
|
return !stack;
|
|
},
|
|
// Call all callbacks with the given context and arguments
|
|
fireWith: function( context, args ) {
|
|
if ( list && ( !fired || stack ) ) {
|
|
args = args || [];
|
|
args = [ context, args.slice ? args.slice() : args ];
|
|
if ( firing ) {
|
|
stack.push( args );
|
|
} else {
|
|
fire( args );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
// Call all the callbacks with the given arguments
|
|
fire: function() {
|
|
self.fireWith( this, arguments );
|
|
return this;
|
|
},
|
|
// To know if the callbacks have already been called at least once
|
|
fired: function() {
|
|
return !!fired;
|
|
}
|
|
};
|
|
|
|
return self;
|
|
};
|
|
|
|
|
|
jQuery.extend({
|
|
|
|
Deferred: function( func ) {
|
|
var tuples = [
|
|
// action, add listener, listener list, final state
|
|
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
|
|
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
|
|
[ "notify", "progress", jQuery.Callbacks("memory") ]
|
|
],
|
|
state = "pending",
|
|
promise = {
|
|
state: function() {
|
|
return state;
|
|
},
|
|
always: function() {
|
|
deferred.done( arguments ).fail( arguments );
|
|
return this;
|
|
},
|
|
then: function( /* fnDone, fnFail, fnProgress */ ) {
|
|
var fns = arguments;
|
|
return jQuery.Deferred(function( newDefer ) {
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
|
|
// deferred[ done | fail | progress ] for forwarding actions to newDefer
|
|
deferred[ tuple[1] ](function() {
|
|
var returned = fn && fn.apply( this, arguments );
|
|
if ( returned && jQuery.isFunction( returned.promise ) ) {
|
|
returned.promise()
|
|
.done( newDefer.resolve )
|
|
.fail( newDefer.reject )
|
|
.progress( newDefer.notify );
|
|
} else {
|
|
newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
|
|
}
|
|
});
|
|
});
|
|
fns = null;
|
|
}).promise();
|
|
},
|
|
// Get a promise for this deferred
|
|
// If obj is provided, the promise aspect is added to the object
|
|
promise: function( obj ) {
|
|
return obj != null ? jQuery.extend( obj, promise ) : promise;
|
|
}
|
|
},
|
|
deferred = {};
|
|
|
|
// Keep pipe for back-compat
|
|
promise.pipe = promise.then;
|
|
|
|
// Add list-specific methods
|
|
jQuery.each( tuples, function( i, tuple ) {
|
|
var list = tuple[ 2 ],
|
|
stateString = tuple[ 3 ];
|
|
|
|
// promise[ done | fail | progress ] = list.add
|
|
promise[ tuple[1] ] = list.add;
|
|
|
|
// Handle state
|
|
if ( stateString ) {
|
|
list.add(function() {
|
|
// state = [ resolved | rejected ]
|
|
state = stateString;
|
|
|
|
// [ reject_list | resolve_list ].disable; progress_list.lock
|
|
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
|
|
}
|
|
|
|
// deferred[ resolve | reject | notify ]
|
|
deferred[ tuple[0] ] = function() {
|
|
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
|
|
return this;
|
|
};
|
|
deferred[ tuple[0] + "With" ] = list.fireWith;
|
|
});
|
|
|
|
// Make the deferred a promise
|
|
promise.promise( deferred );
|
|
|
|
// Call given func if any
|
|
if ( func ) {
|
|
func.call( deferred, deferred );
|
|
}
|
|
|
|
// All done!
|
|
return deferred;
|
|
},
|
|
|
|
// Deferred helper
|
|
when: function( subordinate /* , ..., subordinateN */ ) {
|
|
var i = 0,
|
|
resolveValues = slice.call( arguments ),
|
|
length = resolveValues.length,
|
|
|
|
// the count of uncompleted subordinates
|
|
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
|
|
|
|
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
|
|
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
|
|
|
|
// Update function for both resolve and progress values
|
|
updateFunc = function( i, contexts, values ) {
|
|
return function( value ) {
|
|
contexts[ i ] = this;
|
|
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
|
|
if ( values === progressValues ) {
|
|
deferred.notifyWith( contexts, values );
|
|
} else if ( !( --remaining ) ) {
|
|
deferred.resolveWith( contexts, values );
|
|
}
|
|
};
|
|
},
|
|
|
|
progressValues, progressContexts, resolveContexts;
|
|
|
|
// add listeners to Deferred subordinates; treat others as resolved
|
|
if ( length > 1 ) {
|
|
progressValues = new Array( length );
|
|
progressContexts = new Array( length );
|
|
resolveContexts = new Array( length );
|
|
for ( ; i < length; i++ ) {
|
|
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
|
|
resolveValues[ i ].promise()
|
|
.done( updateFunc( i, resolveContexts, resolveValues ) )
|
|
.fail( deferred.reject )
|
|
.progress( updateFunc( i, progressContexts, progressValues ) );
|
|
} else {
|
|
--remaining;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we're not waiting on anything, resolve the master
|
|
if ( !remaining ) {
|
|
deferred.resolveWith( resolveContexts, resolveValues );
|
|
}
|
|
|
|
return deferred.promise();
|
|
}
|
|
});
|
|
|
|
|
|
// The deferred used on DOM ready
|
|
var readyList;
|
|
|
|
jQuery.fn.ready = function( fn ) {
|
|
// Add the callback
|
|
jQuery.ready.promise().done( fn );
|
|
|
|
return this;
|
|
};
|
|
|
|
jQuery.extend({
|
|
// Is the DOM ready to be used? Set to true once it occurs.
|
|
isReady: false,
|
|
|
|
// A counter to track how many items to wait for before
|
|
// the ready event fires. See #6781
|
|
readyWait: 1,
|
|
|
|
// Hold (or release) the ready event
|
|
holdReady: function( hold ) {
|
|
if ( hold ) {
|
|
jQuery.readyWait++;
|
|
} else {
|
|
jQuery.ready( true );
|
|
}
|
|
},
|
|
|
|
// Handle when the DOM is ready
|
|
ready: function( wait ) {
|
|
|
|
// Abort if there are pending holds or we're already ready
|
|
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
|
|
return;
|
|
}
|
|
|
|
// Remember that the DOM is ready
|
|
jQuery.isReady = true;
|
|
|
|
// If a normal DOM Ready event fired, decrement, and wait if need be
|
|
if ( wait !== true && --jQuery.readyWait > 0 ) {
|
|
return;
|
|
}
|
|
|
|
// If there are functions bound, to execute
|
|
readyList.resolveWith( document, [ jQuery ] );
|
|
|
|
// Trigger any bound ready events
|
|
if ( jQuery.fn.trigger ) {
|
|
jQuery( document ).trigger("ready").off("ready");
|
|
}
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The ready event handler and self cleanup method
|
|
*/
|
|
function completed() {
|
|
document.removeEventListener( "DOMContentLoaded", completed, false );
|
|
window.removeEventListener( "load", completed, false );
|
|
jQuery.ready();
|
|
}
|
|
|
|
jQuery.ready.promise = function( obj ) {
|
|
if ( !readyList ) {
|
|
|
|
readyList = jQuery.Deferred();
|
|
|
|
// Catch cases where $(document).ready() is called after the browser event has already occurred.
|
|
// we once tried to use readyState "interactive" here, but it caused issues like the one
|
|
// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
|
|
if ( document.readyState === "complete" ) {
|
|
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
|
setTimeout( jQuery.ready );
|
|
|
|
} else {
|
|
|
|
// Use the handy event callback
|
|
document.addEventListener( "DOMContentLoaded", completed, false );
|
|
|
|
// A fallback to window.onload, that will always work
|
|
window.addEventListener( "load", completed, false );
|
|
}
|
|
}
|
|
return readyList.promise( obj );
|
|
};
|
|
|
|
// Kick off the DOM ready check even if the user does not
|
|
jQuery.ready.promise();
|
|
|
|
|
|
|
|
|
|
// Multifunctional method to get and set values of a collection
|
|
// The value/s can optionally be executed if it's a function
|
|
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
|
var i = 0,
|
|
len = elems.length,
|
|
bulk = key == null;
|
|
|
|
// Sets many values
|
|
if ( jQuery.type( key ) === "object" ) {
|
|
chainable = true;
|
|
for ( i in key ) {
|
|
jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
|
|
}
|
|
|
|
// Sets one value
|
|
} else if ( value !== undefined ) {
|
|
chainable = true;
|
|
|
|
if ( !jQuery.isFunction( value ) ) {
|
|
raw = true;
|
|
}
|
|
|
|
if ( bulk ) {
|
|
// Bulk operations run against the entire set
|
|
if ( raw ) {
|
|
fn.call( elems, value );
|
|
fn = null;
|
|
|
|
// ...except when executing function values
|
|
} else {
|
|
bulk = fn;
|
|
fn = function( elem, key, value ) {
|
|
return bulk.call( jQuery( elem ), value );
|
|
};
|
|
}
|
|
}
|
|
|
|
if ( fn ) {
|
|
for ( ; i < len; i++ ) {
|
|
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
return chainable ?
|
|
elems :
|
|
|
|
// Gets
|
|
bulk ?
|
|
fn.call( elems ) :
|
|
len ? fn( elems[0], key ) : emptyGet;
|
|
};
|
|
|
|
|
|
/**
|
|
* Determines whether an object can have data
|
|
*/
|
|
jQuery.acceptData = function( owner ) {
|
|
// Accepts only:
|
|
// - Node
|
|
// - Node.ELEMENT_NODE
|
|
// - Node.DOCUMENT_NODE
|
|
// - Object
|
|
// - Any
|
|
/* jshint -W018 */
|
|
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
|
};
|
|
|
|
|
|
function Data() {
|
|
// Support: Android < 4,
|
|
// Old WebKit does not have Object.preventExtensions/freeze method,
|
|
// return new empty object instead with no [[set]] accessor
|
|
Object.defineProperty( this.cache = {}, 0, {
|
|
get: function() {
|
|
return {};
|
|
}
|
|
});
|
|
|
|
this.expando = jQuery.expando + Math.random();
|
|
}
|
|
|
|
Data.uid = 1;
|
|
Data.accepts = jQuery.acceptData;
|
|
|
|
Data.prototype = {
|
|
key: function( owner ) {
|
|
// We can accept data for non-element nodes in modern browsers,
|
|
// but we should not, see #8335.
|
|
// Always return the key for a frozen object.
|
|
if ( !Data.accepts( owner ) ) {
|
|
return 0;
|
|
}
|
|
|
|
var descriptor = {},
|
|
// Check if the owner object already has a cache key
|
|
unlock = owner[ this.expando ];
|
|
|
|
// If not, create one
|
|
if ( !unlock ) {
|
|
unlock = Data.uid++;
|
|
|
|
// Secure it in a non-enumerable, non-writable property
|
|
try {
|
|
descriptor[ this.expando ] = { value: unlock };
|
|
Object.defineProperties( owner, descriptor );
|
|
|
|
// Support: Android < 4
|
|
// Fallback to a less secure definition
|
|
} catch ( e ) {
|
|
descriptor[ this.expando ] = unlock;
|
|
jQuery.extend( owner, descriptor );
|
|
}
|
|
}
|
|
|
|
// Ensure the cache object
|
|
if ( !this.cache[ unlock ] ) {
|
|
this.cache[ unlock ] = {};
|
|
}
|
|
|
|
return unlock;
|
|
},
|
|
set: function( owner, data, value ) {
|
|
var prop,
|
|
// There may be an unlock assigned to this node,
|
|
// if there is no entry for this "owner", create one inline
|
|
// and set the unlock as though an owner entry had always existed
|
|
unlock = this.key( owner ),
|
|
cache = this.cache[ unlock ];
|
|
|
|
// Handle: [ owner, key, value ] args
|
|
if ( typeof data === "string" ) {
|
|
cache[ data ] = value;
|
|
|
|
// Handle: [ owner, { properties } ] args
|
|
} else {
|
|
// Fresh assignments by object are shallow copied
|
|
if ( jQuery.isEmptyObject( cache ) ) {
|
|
jQuery.extend( this.cache[ unlock ], data );
|
|
// Otherwise, copy the properties one-by-one to the cache object
|
|
} else {
|
|
for ( prop in data ) {
|
|
cache[ prop ] = data[ prop ];
|
|
}
|
|
}
|
|
}
|
|
return cache;
|
|
},
|
|
get: function( owner, key ) {
|
|
// Either a valid cache is found, or will be created.
|
|
// New caches will be created and the unlock returned,
|
|
// allowing direct access to the newly created
|
|
// empty data object. A valid owner object must be provided.
|
|
var cache = this.cache[ this.key( owner ) ];
|
|
|
|
return key === undefined ?
|
|
cache : cache[ key ];
|
|
},
|
|
access: function( owner, key, value ) {
|
|
var stored;
|
|
// In cases where either:
|
|
//
|
|
// 1. No key was specified
|
|
// 2. A string key was specified, but no value provided
|
|
//
|
|
// Take the "read" path and allow the get method to determine
|
|
// which value to return, respectively either:
|
|
//
|
|
// 1. The entire cache object
|
|
// 2. The data stored at the key
|
|
//
|
|
if ( key === undefined ||
|
|
((key && typeof key === "string") && value === undefined) ) {
|
|
|
|
stored = this.get( owner, key );
|
|
|
|
return stored !== undefined ?
|
|
stored : this.get( owner, jQuery.camelCase(key) );
|
|
}
|
|
|
|
// [*]When the key is not a string, or both a key and value
|
|
// are specified, set or extend (existing objects) with either:
|
|
//
|
|
// 1. An object of properties
|
|
// 2. A key and value
|
|
//
|
|
this.set( owner, key, value );
|
|
|
|
// Since the "set" path can have two possible entry points
|
|
// return the expected data based on which path was taken[*]
|
|
return value !== undefined ? value : key;
|
|
},
|
|
remove: function( owner, key ) {
|
|
var i, name, camel,
|
|
unlock = this.key( owner ),
|
|
cache = this.cache[ unlock ];
|
|
|
|
if ( key === undefined ) {
|
|
this.cache[ unlock ] = {};
|
|
|
|
} else {
|
|
// Support array or space separated string of keys
|
|
if ( jQuery.isArray( key ) ) {
|
|
// If "name" is an array of keys...
|
|
// When data is initially created, via ("key", "val") signature,
|
|
// keys will be converted to camelCase.
|
|
// Since there is no way to tell _how_ a key was added, remove
|
|
// both plain key and camelCase key. #12786
|
|
// This will only penalize the array argument path.
|
|
name = key.concat( key.map( jQuery.camelCase ) );
|
|
} else {
|
|
camel = jQuery.camelCase( key );
|
|
// Try the string as a key before any manipulation
|
|
if ( key in cache ) {
|
|
name = [ key, camel ];
|
|
} else {
|
|
// If a key with the spaces exists, use it.
|
|
// Otherwise, create an array by matching non-whitespace
|
|
name = camel;
|
|
name = name in cache ?
|
|
[ name ] : ( name.match( rnotwhite ) || [] );
|
|
}
|
|
}
|
|
|
|
i = name.length;
|
|
while ( i-- ) {
|
|
delete cache[ name[ i ] ];
|
|
}
|
|
}
|
|
},
|
|
hasData: function( owner ) {
|
|
return !jQuery.isEmptyObject(
|
|
this.cache[ owner[ this.expando ] ] || {}
|
|
);
|
|
},
|
|
discard: function( owner ) {
|
|
if ( owner[ this.expando ] ) {
|
|
delete this.cache[ owner[ this.expando ] ];
|
|
}
|
|
}
|
|
};
|
|
var data_priv = new Data();
|
|
|
|
var data_user = new Data();
|
|
|
|
|
|
|
|
/*
|
|
Implementation Summary
|
|
|
|
1. Enforce API surface and semantic compatibility with 1.9.x branch
|
|
2. Improve the module's maintainability by reducing the storage
|
|
paths to a single mechanism.
|
|
3. Use the same single mechanism to support "private" and "user" data.
|
|
4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
|
|
5. Avoid exposing implementation details on user objects (eg. expando properties)
|
|
6. Provide a clear path for implementation upgrade to WeakMap in 2014
|
|
*/
|
|
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
|
|
rmultiDash = /([A-Z])/g;
|
|
|
|
function dataAttr( elem, key, data ) {
|
|
var name;
|
|
|
|
// If nothing was found internally, try to fetch any
|
|
// data from the HTML5 data-* attribute
|
|
if ( data === undefined && elem.nodeType === 1 ) {
|
|
name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
|
|
data = elem.getAttribute( name );
|
|
|
|
if ( typeof data === "string" ) {
|
|
try {
|
|
data = data === "true" ? true :
|
|
data === "false" ? false :
|
|
data === "null" ? null :
|
|
// Only convert to a number if it doesn't change the string
|
|
+data + "" === data ? +data :
|
|
rbrace.test( data ) ? jQuery.parseJSON( data ) :
|
|
data;
|
|
} catch( e ) {}
|
|
|
|
// Make sure we set the data so it isn't changed later
|
|
data_user.set( elem, key, data );
|
|
} else {
|
|
data = undefined;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
jQuery.extend({
|
|
hasData: function( elem ) {
|
|
return data_user.hasData( elem ) || data_priv.hasData( elem );
|
|
},
|
|
|
|
data: function( elem, name, data ) {
|
|
return data_user.access( elem, name, data );
|
|
},
|
|
|
|
removeData: function( elem, name ) {
|
|
data_user.remove( elem, name );
|
|
},
|
|
|
|
// TODO: Now that all calls to _data and _removeData have been replaced
|
|
// with direct calls to data_priv methods, these can be deprecated.
|
|
_data: function( elem, name, data ) {
|
|
return data_priv.access( elem, name, data );
|
|
},
|
|
|
|
_removeData: function( elem, name ) {
|
|
data_priv.remove( elem, name );
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
data: function( key, value ) {
|
|
var i, name, data,
|
|
elem = this[ 0 ],
|
|
attrs = elem && elem.attributes;
|
|
|
|
// Gets all values
|
|
if ( key === undefined ) {
|
|
if ( this.length ) {
|
|
data = data_user.get( elem );
|
|
|
|
if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
|
|
i = attrs.length;
|
|
while ( i-- ) {
|
|
name = attrs[ i ].name;
|
|
|
|
if ( name.indexOf( "data-" ) === 0 ) {
|
|
name = jQuery.camelCase( name.slice(5) );
|
|
dataAttr( elem, name, data[ name ] );
|
|
}
|
|
}
|
|
data_priv.set( elem, "hasDataAttrs", true );
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Sets multiple values
|
|
if ( typeof key === "object" ) {
|
|
return this.each(function() {
|
|
data_user.set( this, key );
|
|
});
|
|
}
|
|
|
|
return access( this, function( value ) {
|
|
var data,
|
|
camelKey = jQuery.camelCase( key );
|
|
|
|
// The calling jQuery object (element matches) is not empty
|
|
// (and therefore has an element appears at this[ 0 ]) and the
|
|
// `value` parameter was not undefined. An empty jQuery object
|
|
// will result in `undefined` for elem = this[ 0 ] which will
|
|
// throw an exception if an attempt to read a data cache is made.
|
|
if ( elem && value === undefined ) {
|
|
// Attempt to get data from the cache
|
|
// with the key as-is
|
|
data = data_user.get( elem, key );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// Attempt to get data from the cache
|
|
// with the key camelized
|
|
data = data_user.get( elem, camelKey );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// Attempt to "discover" the data in
|
|
// HTML5 custom data-* attrs
|
|
data = dataAttr( elem, camelKey, undefined );
|
|
if ( data !== undefined ) {
|
|
return data;
|
|
}
|
|
|
|
// We tried really hard, but the data doesn't exist.
|
|
return;
|
|
}
|
|
|
|
// Set the data...
|
|
this.each(function() {
|
|
// First, attempt to store a copy or reference of any
|
|
// data that might've been store with a camelCased key.
|
|
var data = data_user.get( this, camelKey );
|
|
|
|
// For HTML5 data-* attribute interop, we have to
|
|
// store property names with dashes in a camelCase form.
|
|
// This might not apply to all properties...*
|
|
data_user.set( this, camelKey, value );
|
|
|
|
// *... In the case of properties that might _actually_
|
|
// have dashes, we need to also store a copy of that
|
|
// unchanged property.
|
|
if ( key.indexOf("-") !== -1 && data !== undefined ) {
|
|
data_user.set( this, key, value );
|
|
}
|
|
});
|
|
}, null, value, arguments.length > 1, null, true );
|
|
},
|
|
|
|
removeData: function( key ) {
|
|
return this.each(function() {
|
|
data_user.remove( this, key );
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
jQuery.extend({
|
|
queue: function( elem, type, data ) {
|
|
var queue;
|
|
|
|
if ( elem ) {
|
|
type = ( type || "fx" ) + "queue";
|
|
queue = data_priv.get( elem, type );
|
|
|
|
// Speed up dequeue by getting out quickly if this is just a lookup
|
|
if ( data ) {
|
|
if ( !queue || jQuery.isArray( data ) ) {
|
|
queue = data_priv.access( elem, type, jQuery.makeArray(data) );
|
|
} else {
|
|
queue.push( data );
|
|
}
|
|
}
|
|
return queue || [];
|
|
}
|
|
},
|
|
|
|
dequeue: function( elem, type ) {
|
|
type = type || "fx";
|
|
|
|
var queue = jQuery.queue( elem, type ),
|
|
startLength = queue.length,
|
|
fn = queue.shift(),
|
|
hooks = jQuery._queueHooks( elem, type ),
|
|
next = function() {
|
|
jQuery.dequeue( elem, type );
|
|
};
|
|
|
|
// If the fx queue is dequeued, always remove the progress sentinel
|
|
if ( fn === "inprogress" ) {
|
|
fn = queue.shift();
|
|
startLength--;
|
|
}
|
|
|
|
if ( fn ) {
|
|
|
|
// Add a progress sentinel to prevent the fx queue from being
|
|
// automatically dequeued
|
|
if ( type === "fx" ) {
|
|
queue.unshift( "inprogress" );
|
|
}
|
|
|
|
// clear up the last queue stop function
|
|
delete hooks.stop;
|
|
fn.call( elem, next, hooks );
|
|
}
|
|
|
|
if ( !startLength && hooks ) {
|
|
hooks.empty.fire();
|
|
}
|
|
},
|
|
|
|
// not intended for public consumption - generates a queueHooks object, or returns the current one
|
|
_queueHooks: function( elem, type ) {
|
|
var key = type + "queueHooks";
|
|
return data_priv.get( elem, key ) || data_priv.access( elem, key, {
|
|
empty: jQuery.Callbacks("once memory").add(function() {
|
|
data_priv.remove( elem, [ type + "queue", key ] );
|
|
})
|
|
});
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
queue: function( type, data ) {
|
|
var setter = 2;
|
|
|
|
if ( typeof type !== "string" ) {
|
|
data = type;
|
|
type = "fx";
|
|
setter--;
|
|
}
|
|
|
|
if ( arguments.length < setter ) {
|
|
return jQuery.queue( this[0], type );
|
|
}
|
|
|
|
return data === undefined ?
|
|
this :
|
|
this.each(function() {
|
|
var queue = jQuery.queue( this, type, data );
|
|
|
|
// ensure a hooks for this queue
|
|
jQuery._queueHooks( this, type );
|
|
|
|
if ( type === "fx" && queue[0] !== "inprogress" ) {
|
|
jQuery.dequeue( this, type );
|
|
}
|
|
});
|
|
},
|
|
dequeue: function( type ) {
|
|
return this.each(function() {
|
|
jQuery.dequeue( this, type );
|
|
});
|
|
},
|
|
clearQueue: function( type ) {
|
|
return this.queue( type || "fx", [] );
|
|
},
|
|
// Get a promise resolved when queues of a certain type
|
|
// are emptied (fx is the type by default)
|
|
promise: function( type, obj ) {
|
|
var tmp,
|
|
count = 1,
|
|
defer = jQuery.Deferred(),
|
|
elements = this,
|
|
i = this.length,
|
|
resolve = function() {
|
|
if ( !( --count ) ) {
|
|
defer.resolveWith( elements, [ elements ] );
|
|
}
|
|
};
|
|
|
|
if ( typeof type !== "string" ) {
|
|
obj = type;
|
|
type = undefined;
|
|
}
|
|
type = type || "fx";
|
|
|
|
while ( i-- ) {
|
|
tmp = data_priv.get( elements[ i ], type + "queueHooks" );
|
|
if ( tmp && tmp.empty ) {
|
|
count++;
|
|
tmp.empty.add( resolve );
|
|
}
|
|
}
|
|
resolve();
|
|
return defer.promise( obj );
|
|
}
|
|
});
|
|
var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
|
|
|
|
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
|
|
|
|
var isHidden = function( elem, el ) {
|
|
// isHidden might be called from jQuery#filter function;
|
|
// in that case, element will be second argument
|
|
elem = el || elem;
|
|
return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
|
|
};
|
|
|
|
var rcheckableType = (/^(?:checkbox|radio)$/i);
|
|
|
|
|
|
|
|
(function() {
|
|
var fragment = document.createDocumentFragment(),
|
|
div = fragment.appendChild( document.createElement( "div" ) );
|
|
|
|
// #11217 - WebKit loses check when the name is after the checked attribute
|
|
div.innerHTML = "<input type='radio' checked='checked' name='t'/>";
|
|
|
|
// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
|
|
// old WebKit doesn't clone checked state correctly in fragments
|
|
support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
|
|
|
|
// Make sure textarea (and checkbox) defaultValue is properly cloned
|
|
// Support: IE9-IE11+
|
|
div.innerHTML = "<textarea>x</textarea>";
|
|
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
|
|
})();
|
|
var strundefined = typeof undefined;
|
|
|
|
|
|
|
|
support.focusinBubbles = "onfocusin" in window;
|
|
|
|
|
|
var
|
|
rkeyEvent = /^key/,
|
|
rmouseEvent = /^(?:mouse|contextmenu)|click/,
|
|
rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
|
|
rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
|
|
|
|
function returnTrue() {
|
|
return true;
|
|
}
|
|
|
|
function returnFalse() {
|
|
return false;
|
|
}
|
|
|
|
function safeActiveElement() {
|
|
try {
|
|
return document.activeElement;
|
|
} catch ( err ) { }
|
|
}
|
|
|
|
/*
|
|
* Helper functions for managing events -- not part of the public interface.
|
|
* Props to Dean Edwards' addEvent library for many of the ideas.
|
|
*/
|
|
jQuery.event = {
|
|
|
|
global: {},
|
|
|
|
add: function( elem, types, handler, data, selector ) {
|
|
|
|
var handleObjIn, eventHandle, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = data_priv.get( elem );
|
|
|
|
// Don't attach events to noData or text/comment nodes (but allow plain objects)
|
|
if ( !elemData ) {
|
|
return;
|
|
}
|
|
|
|
// Caller can pass in an object of custom data in lieu of the handler
|
|
if ( handler.handler ) {
|
|
handleObjIn = handler;
|
|
handler = handleObjIn.handler;
|
|
selector = handleObjIn.selector;
|
|
}
|
|
|
|
// Make sure that the handler has a unique ID, used to find/remove it later
|
|
if ( !handler.guid ) {
|
|
handler.guid = jQuery.guid++;
|
|
}
|
|
|
|
// Init the element's event structure and main handler, if this is the first
|
|
if ( !(events = elemData.events) ) {
|
|
events = elemData.events = {};
|
|
}
|
|
if ( !(eventHandle = elemData.handle) ) {
|
|
eventHandle = elemData.handle = function( e ) {
|
|
// Discard the second event of a jQuery.event.trigger() and
|
|
// when an event is called after a page has unloaded
|
|
return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
|
|
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
|
|
};
|
|
}
|
|
|
|
// Handle multiple events separated by a space
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[t] ) || [];
|
|
type = origType = tmp[1];
|
|
namespaces = ( tmp[2] || "" ).split( "." ).sort();
|
|
|
|
// There *must* be a type, no attaching namespace-only handlers
|
|
if ( !type ) {
|
|
continue;
|
|
}
|
|
|
|
// If event changes its type, use the special event handlers for the changed type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// If selector defined, determine special event api type, otherwise given type
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
|
|
// Update special based on newly reset type
|
|
special = jQuery.event.special[ type ] || {};
|
|
|
|
// handleObj is passed to all event handlers
|
|
handleObj = jQuery.extend({
|
|
type: type,
|
|
origType: origType,
|
|
data: data,
|
|
handler: handler,
|
|
guid: handler.guid,
|
|
selector: selector,
|
|
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
|
|
namespace: namespaces.join(".")
|
|
}, handleObjIn );
|
|
|
|
// Init the event handler queue if we're the first
|
|
if ( !(handlers = events[ type ]) ) {
|
|
handlers = events[ type ] = [];
|
|
handlers.delegateCount = 0;
|
|
|
|
// Only use addEventListener if the special events handler returns false
|
|
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
|
|
if ( elem.addEventListener ) {
|
|
elem.addEventListener( type, eventHandle, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( special.add ) {
|
|
special.add.call( elem, handleObj );
|
|
|
|
if ( !handleObj.handler.guid ) {
|
|
handleObj.handler.guid = handler.guid;
|
|
}
|
|
}
|
|
|
|
// Add to the element's handler list, delegates in front
|
|
if ( selector ) {
|
|
handlers.splice( handlers.delegateCount++, 0, handleObj );
|
|
} else {
|
|
handlers.push( handleObj );
|
|
}
|
|
|
|
// Keep track of which events have ever been used, for event optimization
|
|
jQuery.event.global[ type ] = true;
|
|
}
|
|
|
|
},
|
|
|
|
// Detach an event or set of events from an element
|
|
remove: function( elem, types, handler, selector, mappedTypes ) {
|
|
|
|
var j, origCount, tmp,
|
|
events, t, handleObj,
|
|
special, handlers, type, namespaces, origType,
|
|
elemData = data_priv.hasData( elem ) && data_priv.get( elem );
|
|
|
|
if ( !elemData || !(events = elemData.events) ) {
|
|
return;
|
|
}
|
|
|
|
// Once for each type.namespace in types; type may be omitted
|
|
types = ( types || "" ).match( rnotwhite ) || [ "" ];
|
|
t = types.length;
|
|
while ( t-- ) {
|
|
tmp = rtypenamespace.exec( types[t] ) || [];
|
|
type = origType = tmp[1];
|
|
namespaces = ( tmp[2] || "" ).split( "." ).sort();
|
|
|
|
// Unbind all events (on this namespace, if provided) for the element
|
|
if ( !type ) {
|
|
for ( type in events ) {
|
|
jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
special = jQuery.event.special[ type ] || {};
|
|
type = ( selector ? special.delegateType : special.bindType ) || type;
|
|
handlers = events[ type ] || [];
|
|
tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
|
|
|
|
// Remove matching events
|
|
origCount = j = handlers.length;
|
|
while ( j-- ) {
|
|
handleObj = handlers[ j ];
|
|
|
|
if ( ( mappedTypes || origType === handleObj.origType ) &&
|
|
( !handler || handler.guid === handleObj.guid ) &&
|
|
( !tmp || tmp.test( handleObj.namespace ) ) &&
|
|
( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
|
|
handlers.splice( j, 1 );
|
|
|
|
if ( handleObj.selector ) {
|
|
handlers.delegateCount--;
|
|
}
|
|
if ( special.remove ) {
|
|
special.remove.call( elem, handleObj );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove generic event handler if we removed something and no more handlers exist
|
|
// (avoids potential for endless recursion during removal of special event handlers)
|
|
if ( origCount && !handlers.length ) {
|
|
if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
|
|
jQuery.removeEvent( elem, type, elemData.handle );
|
|
}
|
|
|
|
delete events[ type ];
|
|
}
|
|
}
|
|
|
|
// Remove the expando if it's no longer used
|
|
if ( jQuery.isEmptyObject( events ) ) {
|
|
delete elemData.handle;
|
|
data_priv.remove( elem, "events" );
|
|
}
|
|
},
|
|
|
|
trigger: function( event, data, elem, onlyHandlers ) {
|
|
|
|
var i, cur, tmp, bubbleType, ontype, handle, special,
|
|
eventPath = [ elem || document ],
|
|
type = hasOwn.call( event, "type" ) ? event.type : event,
|
|
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
|
|
|
|
cur = tmp = elem = elem || document;
|
|
|
|
// Don't do events on text and comment nodes
|
|
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
|
|
return;
|
|
}
|
|
|
|
// focus/blur morphs to focusin/out; ensure we're not firing them right now
|
|
if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
|
|
return;
|
|
}
|
|
|
|
if ( type.indexOf(".") >= 0 ) {
|
|
// Namespaced trigger; create a regexp to match event type in handle()
|
|
namespaces = type.split(".");
|
|
type = namespaces.shift();
|
|
namespaces.sort();
|
|
}
|
|
ontype = type.indexOf(":") < 0 && "on" + type;
|
|
|
|
// Caller can pass in a jQuery.Event object, Object, or just an event type string
|
|
event = event[ jQuery.expando ] ?
|
|
event :
|
|
new jQuery.Event( type, typeof event === "object" && event );
|
|
|
|
// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
|
|
event.isTrigger = onlyHandlers ? 2 : 3;
|
|
event.namespace = namespaces.join(".");
|
|
event.namespace_re = event.namespace ?
|
|
new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
|
|
null;
|
|
|
|
// Clean up the event in case it is being reused
|
|
event.result = undefined;
|
|
if ( !event.target ) {
|
|
event.target = elem;
|
|
}
|
|
|
|
// Clone any incoming data and prepend the event, creating the handler arg list
|
|
data = data == null ?
|
|
[ event ] :
|
|
jQuery.makeArray( data, [ event ] );
|
|
|
|
// Allow special events to draw outside the lines
|
|
special = jQuery.event.special[ type ] || {};
|
|
if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine event propagation path in advance, per W3C events spec (#9951)
|
|
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
|
|
if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
|
|
|
|
bubbleType = special.delegateType || type;
|
|
if ( !rfocusMorph.test( bubbleType + type ) ) {
|
|
cur = cur.parentNode;
|
|
}
|
|
for ( ; cur; cur = cur.parentNode ) {
|
|
eventPath.push( cur );
|
|
tmp = cur;
|
|
}
|
|
|
|
// Only add window if we got to document (e.g., not plain obj or detached DOM)
|
|
if ( tmp === (elem.ownerDocument || document) ) {
|
|
eventPath.push( tmp.defaultView || tmp.parentWindow || window );
|
|
}
|
|
}
|
|
|
|
// Fire handlers on the event path
|
|
i = 0;
|
|
while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
|
|
|
|
event.type = i > 1 ?
|
|
bubbleType :
|
|
special.bindType || type;
|
|
|
|
// jQuery handler
|
|
handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
|
|
if ( handle ) {
|
|
handle.apply( cur, data );
|
|
}
|
|
|
|
// Native handler
|
|
handle = ontype && cur[ ontype ];
|
|
if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
|
|
event.result = handle.apply( cur, data );
|
|
if ( event.result === false ) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
event.type = type;
|
|
|
|
// If nobody prevented the default action, do it now
|
|
if ( !onlyHandlers && !event.isDefaultPrevented() ) {
|
|
|
|
if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
|
|
jQuery.acceptData( elem ) ) {
|
|
|
|
// Call a native DOM method on the target with the same name name as the event.
|
|
// Don't do default actions on window, that's where global variables be (#6170)
|
|
if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
|
|
|
|
// Don't re-trigger an onFOO event when we call its FOO() method
|
|
tmp = elem[ ontype ];
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = null;
|
|
}
|
|
|
|
// Prevent re-triggering of the same event, since we already bubbled it above
|
|
jQuery.event.triggered = type;
|
|
elem[ type ]();
|
|
jQuery.event.triggered = undefined;
|
|
|
|
if ( tmp ) {
|
|
elem[ ontype ] = tmp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
dispatch: function( event ) {
|
|
|
|
// Make a writable jQuery.Event from the native event object
|
|
event = jQuery.event.fix( event );
|
|
|
|
var i, j, ret, matched, handleObj,
|
|
handlerQueue = [],
|
|
args = slice.call( arguments ),
|
|
handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
|
|
special = jQuery.event.special[ event.type ] || {};
|
|
|
|
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
|
args[0] = event;
|
|
event.delegateTarget = this;
|
|
|
|
// Call the preDispatch hook for the mapped type, and let it bail if desired
|
|
if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
|
|
return;
|
|
}
|
|
|
|
// Determine handlers
|
|
handlerQueue = jQuery.event.handlers.call( this, event, handlers );
|
|
|
|
// Run delegates first; they may want to stop propagation beneath us
|
|
i = 0;
|
|
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
|
|
event.currentTarget = matched.elem;
|
|
|
|
j = 0;
|
|
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
|
|
|
|
// Triggered event must either 1) have no namespace, or
|
|
// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
|
|
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
|
|
|
|
event.handleObj = handleObj;
|
|
event.data = handleObj.data;
|
|
|
|
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
|
|
.apply( matched.elem, args );
|
|
|
|
if ( ret !== undefined ) {
|
|
if ( (event.result = ret) === false ) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call the postDispatch hook for the mapped type
|
|
if ( special.postDispatch ) {
|
|
special.postDispatch.call( this, event );
|
|
}
|
|
|
|
return event.result;
|
|
},
|
|
|
|
handlers: function( event, handlers ) {
|
|
var i, matches, sel, handleObj,
|
|
handlerQueue = [],
|
|
delegateCount = handlers.delegateCount,
|
|
cur = event.target;
|
|
|
|
// Find delegate handlers
|
|
// Black-hole SVG <use> instance trees (#13180)
|
|
// Avoid non-left-click bubbling in Firefox (#3861)
|
|
if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
|
|
|
|
for ( ; cur !== this; cur = cur.parentNode || this ) {
|
|
|
|
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
|
|
if ( cur.disabled !== true || event.type !== "click" ) {
|
|
matches = [];
|
|
for ( i = 0; i < delegateCount; i++ ) {
|
|
handleObj = handlers[ i ];
|
|
|
|
// Don't conflict with Object.prototype properties (#13203)
|
|
sel = handleObj.selector + " ";
|
|
|
|
if ( matches[ sel ] === undefined ) {
|
|
matches[ sel ] = handleObj.needsContext ?
|
|
jQuery( sel, this ).index( cur ) >= 0 :
|
|
jQuery.find( sel, this, null, [ cur ] ).length;
|
|
}
|
|
if ( matches[ sel ] ) {
|
|
matches.push( handleObj );
|
|
}
|
|
}
|
|
if ( matches.length ) {
|
|
handlerQueue.push({ elem: cur, handlers: matches });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add the remaining (directly-bound) handlers
|
|
if ( delegateCount < handlers.length ) {
|
|
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
|
|
}
|
|
|
|
return handlerQueue;
|
|
},
|
|
|
|
// Includes some event props shared by KeyEvent and MouseEvent
|
|
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
|
|
|
|
fixHooks: {},
|
|
|
|
keyHooks: {
|
|
props: "char charCode key keyCode".split(" "),
|
|
filter: function( event, original ) {
|
|
|
|
// Add which for key events
|
|
if ( event.which == null ) {
|
|
event.which = original.charCode != null ? original.charCode : original.keyCode;
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
mouseHooks: {
|
|
props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
|
|
filter: function( event, original ) {
|
|
var eventDoc, doc, body,
|
|
button = original.button;
|
|
|
|
// Calculate pageX/Y if missing and clientX/Y available
|
|
if ( event.pageX == null && original.clientX != null ) {
|
|
eventDoc = event.target.ownerDocument || document;
|
|
doc = eventDoc.documentElement;
|
|
body = eventDoc.body;
|
|
|
|
event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
|
|
event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
|
|
}
|
|
|
|
// Add which for click: 1 === left; 2 === middle; 3 === right
|
|
// Note: button is not normalized, so don't use it
|
|
if ( !event.which && button !== undefined ) {
|
|
event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
|
|
}
|
|
|
|
return event;
|
|
}
|
|
},
|
|
|
|
fix: function( event ) {
|
|
if ( event[ jQuery.expando ] ) {
|
|
return event;
|
|
}
|
|
|
|
// Create a writable copy of the event object and normalize some properties
|
|
var i, prop, copy,
|
|
type = event.type,
|
|
originalEvent = event,
|
|
fixHook = this.fixHooks[ type ];
|
|
|
|
if ( !fixHook ) {
|
|
this.fixHooks[ type ] = fixHook =
|
|
rmouseEvent.test( type ) ? this.mouseHooks :
|
|
rkeyEvent.test( type ) ? this.keyHooks :
|
|
{};
|
|
}
|
|
copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
|
|
|
|
event = new jQuery.Event( originalEvent );
|
|
|
|
i = copy.length;
|
|
while ( i-- ) {
|
|
prop = copy[ i ];
|
|
event[ prop ] = originalEvent[ prop ];
|
|
}
|
|
|
|
// Support: Cordova 2.5 (WebKit) (#13255)
|
|
// All events should have a target; Cordova deviceready doesn't
|
|
if ( !event.target ) {
|
|
event.target = document;
|
|
}
|
|
|
|
// Support: Safari 6.0+, Chrome < 28
|
|
// Target should not be a text node (#504, #13143)
|
|
if ( event.target.nodeType === 3 ) {
|
|
event.target = event.target.parentNode;
|
|
}
|
|
|
|
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
|
|
},
|
|
|
|
special: {
|
|
load: {
|
|
// Prevent triggered image.load events from bubbling to window.load
|
|
noBubble: true
|
|
},
|
|
focus: {
|
|
// Fire native event if possible so blur/focus sequence is correct
|
|
trigger: function() {
|
|
if ( this !== safeActiveElement() && this.focus ) {
|
|
this.focus();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusin"
|
|
},
|
|
blur: {
|
|
trigger: function() {
|
|
if ( this === safeActiveElement() && this.blur ) {
|
|
this.blur();
|
|
return false;
|
|
}
|
|
},
|
|
delegateType: "focusout"
|
|
},
|
|
click: {
|
|
// For checkbox, fire native event so checked state will be right
|
|
trigger: function() {
|
|
if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
|
|
this.click();
|
|
return false;
|
|
}
|
|
},
|
|
|
|
// For cross-browser consistency, don't fire native .click() on links
|
|
_default: function( event ) {
|
|
return jQuery.nodeName( event.target, "a" );
|
|
}
|
|
},
|
|
|
|
beforeunload: {
|
|
postDispatch: function( event ) {
|
|
|
|
// Support: Firefox 20+
|
|
// Firefox doesn't alert if the returnValue field is not set.
|
|
if ( event.result !== undefined ) {
|
|
event.originalEvent.returnValue = event.result;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
simulate: function( type, elem, event, bubble ) {
|
|
// Piggyback on a donor event to simulate a different one.
|
|
// Fake originalEvent to avoid donor's stopPropagation, but if the
|
|
// simulated event prevents default then we do the same on the donor.
|
|
var e = jQuery.extend(
|
|
new jQuery.Event(),
|
|
event,
|
|
{
|
|
type: type,
|
|
isSimulated: true,
|
|
originalEvent: {}
|
|
}
|
|
);
|
|
if ( bubble ) {
|
|
jQuery.event.trigger( e, null, elem );
|
|
} else {
|
|
jQuery.event.dispatch.call( elem, e );
|
|
}
|
|
if ( e.isDefaultPrevented() ) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.removeEvent = function( elem, type, handle ) {
|
|
if ( elem.removeEventListener ) {
|
|
elem.removeEventListener( type, handle, false );
|
|
}
|
|
};
|
|
|
|
jQuery.Event = function( src, props ) {
|
|
// Allow instantiation without the 'new' keyword
|
|
if ( !(this instanceof jQuery.Event) ) {
|
|
return new jQuery.Event( src, props );
|
|
}
|
|
|
|
// Event object
|
|
if ( src && src.type ) {
|
|
this.originalEvent = src;
|
|
this.type = src.type;
|
|
|
|
// Events bubbling up the document may have been marked as prevented
|
|
// by a handler lower down the tree; reflect the correct value.
|
|
this.isDefaultPrevented = src.defaultPrevented ||
|
|
// Support: Android < 4.0
|
|
src.defaultPrevented === undefined &&
|
|
src.getPreventDefault && src.getPreventDefault() ?
|
|
returnTrue :
|
|
returnFalse;
|
|
|
|
// Event type
|
|
} else {
|
|
this.type = src;
|
|
}
|
|
|
|
// Put explicitly provided properties onto the event object
|
|
if ( props ) {
|
|
jQuery.extend( this, props );
|
|
}
|
|
|
|
// Create a timestamp if incoming event doesn't have one
|
|
this.timeStamp = src && src.timeStamp || jQuery.now();
|
|
|
|
// Mark it as fixed
|
|
this[ jQuery.expando ] = true;
|
|
};
|
|
|
|
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
|
|
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
|
jQuery.Event.prototype = {
|
|
isDefaultPrevented: returnFalse,
|
|
isPropagationStopped: returnFalse,
|
|
isImmediatePropagationStopped: returnFalse,
|
|
|
|
preventDefault: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isDefaultPrevented = returnTrue;
|
|
|
|
if ( e && e.preventDefault ) {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
stopPropagation: function() {
|
|
var e = this.originalEvent;
|
|
|
|
this.isPropagationStopped = returnTrue;
|
|
|
|
if ( e && e.stopPropagation ) {
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
stopImmediatePropagation: function() {
|
|
this.isImmediatePropagationStopped = returnTrue;
|
|
this.stopPropagation();
|
|
}
|
|
};
|
|
|
|
// Create mouseenter/leave events using mouseover/out and event-time checks
|
|
// Support: Chrome 15+
|
|
jQuery.each({
|
|
mouseenter: "mouseover",
|
|
mouseleave: "mouseout"
|
|
}, function( orig, fix ) {
|
|
jQuery.event.special[ orig ] = {
|
|
delegateType: fix,
|
|
bindType: fix,
|
|
|
|
handle: function( event ) {
|
|
var ret,
|
|
target = this,
|
|
related = event.relatedTarget,
|
|
handleObj = event.handleObj;
|
|
|
|
// For mousenter/leave call the handler if related is outside the target.
|
|
// NB: No relatedTarget if the mouse left/entered the browser window
|
|
if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
|
|
event.type = handleObj.origType;
|
|
ret = handleObj.handler.apply( this, arguments );
|
|
event.type = fix;
|
|
}
|
|
return ret;
|
|
}
|
|
};
|
|
});
|
|
|
|
// Create "bubbling" focus and blur events
|
|
// Support: Firefox, Chrome, Safari
|
|
if ( !support.focusinBubbles ) {
|
|
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
|
|
|
|
// Attach a single capturing handler on the document while someone wants focusin/focusout
|
|
var handler = function( event ) {
|
|
jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
|
|
};
|
|
|
|
jQuery.event.special[ fix ] = {
|
|
setup: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = data_priv.access( doc, fix );
|
|
|
|
if ( !attaches ) {
|
|
doc.addEventListener( orig, handler, true );
|
|
}
|
|
data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
|
|
},
|
|
teardown: function() {
|
|
var doc = this.ownerDocument || this,
|
|
attaches = data_priv.access( doc, fix ) - 1;
|
|
|
|
if ( !attaches ) {
|
|
doc.removeEventListener( orig, handler, true );
|
|
data_priv.remove( doc, fix );
|
|
|
|
} else {
|
|
data_priv.access( doc, fix, attaches );
|
|
}
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
jQuery.fn.extend({
|
|
|
|
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
|
|
var origFn, type;
|
|
|
|
// Types can be a map of types/handlers
|
|
if ( typeof types === "object" ) {
|
|
// ( types-Object, selector, data )
|
|
if ( typeof selector !== "string" ) {
|
|
// ( types-Object, data )
|
|
data = data || selector;
|
|
selector = undefined;
|
|
}
|
|
for ( type in types ) {
|
|
this.on( type, selector, data, types[ type ], one );
|
|
}
|
|
return this;
|
|
}
|
|
|
|
if ( data == null && fn == null ) {
|
|
// ( types, fn )
|
|
fn = selector;
|
|
data = selector = undefined;
|
|
} else if ( fn == null ) {
|
|
if ( typeof selector === "string" ) {
|
|
// ( types, selector, fn )
|
|
fn = data;
|
|
data = undefined;
|
|
} else {
|
|
// ( types, data, fn )
|
|
fn = data;
|
|
data = selector;
|
|
selector = undefined;
|
|
}
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
} else if ( !fn ) {
|
|
return this;
|
|
}
|
|
|
|
if ( one === 1 ) {
|
|
origFn = fn;
|
|
fn = function( event ) {
|
|
// Can use an empty set, since event contains the info
|
|
jQuery().off( event );
|
|
return origFn.apply( this, arguments );
|
|
};
|
|
// Use same guid so caller can remove using origFn
|
|
fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
|
|
}
|
|
return this.each( function() {
|
|
jQuery.event.add( this, types, fn, data, selector );
|
|
});
|
|
},
|
|
one: function( types, selector, data, fn ) {
|
|
return this.on( types, selector, data, fn, 1 );
|
|
},
|
|
off: function( types, selector, fn ) {
|
|
var handleObj, type;
|
|
if ( types && types.preventDefault && types.handleObj ) {
|
|
// ( event ) dispatched jQuery.Event
|
|
handleObj = types.handleObj;
|
|
jQuery( types.delegateTarget ).off(
|
|
handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
|
|
handleObj.selector,
|
|
handleObj.handler
|
|
);
|
|
return this;
|
|
}
|
|
if ( typeof types === "object" ) {
|
|
// ( types-object [, selector] )
|
|
for ( type in types ) {
|
|
this.off( type, selector, types[ type ] );
|
|
}
|
|
return this;
|
|
}
|
|
if ( selector === false || typeof selector === "function" ) {
|
|
// ( types [, fn] )
|
|
fn = selector;
|
|
selector = undefined;
|
|
}
|
|
if ( fn === false ) {
|
|
fn = returnFalse;
|
|
}
|
|
return this.each(function() {
|
|
jQuery.event.remove( this, types, fn, selector );
|
|
});
|
|
},
|
|
|
|
trigger: function( type, data ) {
|
|
return this.each(function() {
|
|
jQuery.event.trigger( type, data, this );
|
|
});
|
|
},
|
|
triggerHandler: function( type, data ) {
|
|
var elem = this[0];
|
|
if ( elem ) {
|
|
return jQuery.event.trigger( type, data, elem, true );
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
var
|
|
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
|
|
rtagName = /<([\w:]+)/,
|
|
rhtml = /<|&#?\w+;/,
|
|
rnoInnerhtml = /<(?:script|style|link)/i,
|
|
// checked="checked" or checked
|
|
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
|
|
rscriptType = /^$|\/(?:java|ecma)script/i,
|
|
rscriptTypeMasked = /^true\/(.*)/,
|
|
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
|
|
|
|
// We have to close these tags to support XHTML (#13200)
|
|
wrapMap = {
|
|
|
|
// Support: IE 9
|
|
option: [ 1, "<select multiple='multiple'>", "</select>" ],
|
|
|
|
thead: [ 1, "<table>", "</table>" ],
|
|
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
|
|
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
|
|
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
|
|
|
_default: [ 0, "", "" ]
|
|
};
|
|
|
|
// Support: IE 9
|
|
wrapMap.optgroup = wrapMap.option;
|
|
|
|
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
|
|
wrapMap.th = wrapMap.td;
|
|
|
|
// Support: 1.x compatibility
|
|
// Manipulating tables requires a tbody
|
|
function manipulationTarget( elem, content ) {
|
|
return jQuery.nodeName( elem, "table" ) &&
|
|
jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
|
|
|
|
elem.getElementsByTagName("tbody")[0] ||
|
|
elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
|
|
elem;
|
|
}
|
|
|
|
// Replace/restore the type attribute of script elements for safe DOM manipulation
|
|
function disableScript( elem ) {
|
|
elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
|
|
return elem;
|
|
}
|
|
function restoreScript( elem ) {
|
|
var match = rscriptTypeMasked.exec( elem.type );
|
|
|
|
if ( match ) {
|
|
elem.type = match[ 1 ];
|
|
} else {
|
|
elem.removeAttribute("type");
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
// Mark scripts as having already been evaluated
|
|
function setGlobalEval( elems, refElements ) {
|
|
var i = 0,
|
|
l = elems.length;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
data_priv.set(
|
|
elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
|
|
);
|
|
}
|
|
}
|
|
|
|
function cloneCopyEvent( src, dest ) {
|
|
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
|
|
|
|
if ( dest.nodeType !== 1 ) {
|
|
return;
|
|
}
|
|
|
|
// 1. Copy private data: events, handlers, etc.
|
|
if ( data_priv.hasData( src ) ) {
|
|
pdataOld = data_priv.access( src );
|
|
pdataCur = data_priv.set( dest, pdataOld );
|
|
events = pdataOld.events;
|
|
|
|
if ( events ) {
|
|
delete pdataCur.handle;
|
|
pdataCur.events = {};
|
|
|
|
for ( type in events ) {
|
|
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
|
|
jQuery.event.add( dest, type, events[ type ][ i ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Copy user data
|
|
if ( data_user.hasData( src ) ) {
|
|
udataOld = data_user.access( src );
|
|
udataCur = jQuery.extend( {}, udataOld );
|
|
|
|
data_user.set( dest, udataCur );
|
|
}
|
|
}
|
|
|
|
function getAll( context, tag ) {
|
|
var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
|
|
context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
|
|
[];
|
|
|
|
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
|
|
jQuery.merge( [ context ], ret ) :
|
|
ret;
|
|
}
|
|
|
|
// Support: IE >= 9
|
|
function fixInput( src, dest ) {
|
|
var nodeName = dest.nodeName.toLowerCase();
|
|
|
|
// Fails to persist the checked state of a cloned checkbox or radio button.
|
|
if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
|
|
dest.checked = src.checked;
|
|
|
|
// Fails to return the selected option to the default selected state when cloning options
|
|
} else if ( nodeName === "input" || nodeName === "textarea" ) {
|
|
dest.defaultValue = src.defaultValue;
|
|
}
|
|
}
|
|
|
|
jQuery.extend({
|
|
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
|
|
var i, l, srcElements, destElements,
|
|
clone = elem.cloneNode( true ),
|
|
inPage = jQuery.contains( elem.ownerDocument, elem );
|
|
|
|
// Support: IE >= 9
|
|
// Fix Cloning issues
|
|
if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
|
|
!jQuery.isXMLDoc( elem ) ) {
|
|
|
|
// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
|
|
destElements = getAll( clone );
|
|
srcElements = getAll( elem );
|
|
|
|
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
|
fixInput( srcElements[ i ], destElements[ i ] );
|
|
}
|
|
}
|
|
|
|
// Copy the events from the original to the clone
|
|
if ( dataAndEvents ) {
|
|
if ( deepDataAndEvents ) {
|
|
srcElements = srcElements || getAll( elem );
|
|
destElements = destElements || getAll( clone );
|
|
|
|
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
|
cloneCopyEvent( srcElements[ i ], destElements[ i ] );
|
|
}
|
|
} else {
|
|
cloneCopyEvent( elem, clone );
|
|
}
|
|
}
|
|
|
|
// Preserve script evaluation history
|
|
destElements = getAll( clone, "script" );
|
|
if ( destElements.length > 0 ) {
|
|
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
|
|
}
|
|
|
|
// Return the cloned set
|
|
return clone;
|
|
},
|
|
|
|
buildFragment: function( elems, context, scripts, selection ) {
|
|
var elem, tmp, tag, wrap, contains, j,
|
|
fragment = context.createDocumentFragment(),
|
|
nodes = [],
|
|
i = 0,
|
|
l = elems.length;
|
|
|
|
for ( ; i < l; i++ ) {
|
|
elem = elems[ i ];
|
|
|
|
if ( elem || elem === 0 ) {
|
|
|
|
// Add nodes directly
|
|
if ( jQuery.type( elem ) === "object" ) {
|
|
// Support: QtWebKit
|
|
// jQuery.merge because push.apply(_, arraylike) throws
|
|
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
|
|
|
|
// Convert non-html into a text node
|
|
} else if ( !rhtml.test( elem ) ) {
|
|
nodes.push( context.createTextNode( elem ) );
|
|
|
|
// Convert html into DOM nodes
|
|
} else {
|
|
tmp = tmp || fragment.appendChild( context.createElement("div") );
|
|
|
|
// Deserialize a standard representation
|
|
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
|
|
wrap = wrapMap[ tag ] || wrapMap._default;
|
|
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
|
|
|
|
// Descend through wrappers to the right content
|
|
j = wrap[ 0 ];
|
|
while ( j-- ) {
|
|
tmp = tmp.lastChild;
|
|
}
|
|
|
|
// Support: QtWebKit
|
|
// jQuery.merge because push.apply(_, arraylike) throws
|
|
jQuery.merge( nodes, tmp.childNodes );
|
|
|
|
// Remember the top-level container
|
|
tmp = fragment.firstChild;
|
|
|
|
// Fixes #12346
|
|
// Support: Webkit, IE
|
|
tmp.textContent = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove wrapper from fragment
|
|
fragment.textContent = "";
|
|
|
|
i = 0;
|
|
while ( (elem = nodes[ i++ ]) ) {
|
|
|
|
// #4087 - If origin and destination elements are the same, and this is
|
|
// that element, do not do anything
|
|
if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
|
|
continue;
|
|
}
|
|
|
|
contains = jQuery.contains( elem.ownerDocument, elem );
|
|
|
|
// Append to fragment
|
|
tmp = getAll( fragment.appendChild( elem ), "script" );
|
|
|
|
// Preserve script evaluation history
|
|
if ( contains ) {
|
|
setGlobalEval( tmp );
|
|
}
|
|
|
|
// Capture executables
|
|
if ( scripts ) {
|
|
j = 0;
|
|
while ( (elem = tmp[ j++ ]) ) {
|
|
if ( rscriptType.test( elem.type || "" ) ) {
|
|
scripts.push( elem );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return fragment;
|
|
},
|
|
|
|
cleanData: function( elems ) {
|
|
var data, elem, events, type, key, j,
|
|
special = jQuery.event.special,
|
|
i = 0;
|
|
|
|
for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
|
|
if ( jQuery.acceptData( elem ) ) {
|
|
key = elem[ data_priv.expando ];
|
|
|
|
if ( key && (data = data_priv.cache[ key ]) ) {
|
|
events = Object.keys( data.events || {} );
|
|
if ( events.length ) {
|
|
for ( j = 0; (type = events[j]) !== undefined; j++ ) {
|
|
if ( special[ type ] ) {
|
|
jQuery.event.remove( elem, type );
|
|
|
|
// This is a shortcut to avoid jQuery.event.remove's overhead
|
|
} else {
|
|
jQuery.removeEvent( elem, type, data.handle );
|
|
}
|
|
}
|
|
}
|
|
if ( data_priv.cache[ key ] ) {
|
|
// Discard any remaining `private` data
|
|
delete data_priv.cache[ key ];
|
|
}
|
|
}
|
|
}
|
|
// Discard any remaining `user` data
|
|
delete data_user.cache[ elem[ data_user.expando ] ];
|
|
}
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
text: function( value ) {
|
|
return access( this, function( value ) {
|
|
return value === undefined ?
|
|
jQuery.text( this ) :
|
|
this.empty().each(function() {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
this.textContent = value;
|
|
}
|
|
});
|
|
}, null, value, arguments.length );
|
|
},
|
|
|
|
append: function() {
|
|
return this.domManip( arguments, function( elem ) {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
var target = manipulationTarget( this, elem );
|
|
target.appendChild( elem );
|
|
}
|
|
});
|
|
},
|
|
|
|
prepend: function() {
|
|
return this.domManip( arguments, function( elem ) {
|
|
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
|
var target = manipulationTarget( this, elem );
|
|
target.insertBefore( elem, target.firstChild );
|
|
}
|
|
});
|
|
},
|
|
|
|
before: function() {
|
|
return this.domManip( arguments, function( elem ) {
|
|
if ( this.parentNode ) {
|
|
this.parentNode.insertBefore( elem, this );
|
|
}
|
|
});
|
|
},
|
|
|
|
after: function() {
|
|
return this.domManip( arguments, function( elem ) {
|
|
if ( this.parentNode ) {
|
|
this.parentNode.insertBefore( elem, this.nextSibling );
|
|
}
|
|
});
|
|
},
|
|
|
|
remove: function( selector, keepData /* Internal Use Only */ ) {
|
|
var elem,
|
|
elems = selector ? jQuery.filter( selector, this ) : this,
|
|
i = 0;
|
|
|
|
for ( ; (elem = elems[i]) != null; i++ ) {
|
|
if ( !keepData && elem.nodeType === 1 ) {
|
|
jQuery.cleanData( getAll( elem ) );
|
|
}
|
|
|
|
if ( elem.parentNode ) {
|
|
if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
|
|
setGlobalEval( getAll( elem, "script" ) );
|
|
}
|
|
elem.parentNode.removeChild( elem );
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
empty: function() {
|
|
var elem,
|
|
i = 0;
|
|
|
|
for ( ; (elem = this[i]) != null; i++ ) {
|
|
if ( elem.nodeType === 1 ) {
|
|
|
|
// Prevent memory leaks
|
|
jQuery.cleanData( getAll( elem, false ) );
|
|
|
|
// Remove any remaining nodes
|
|
elem.textContent = "";
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
clone: function( dataAndEvents, deepDataAndEvents ) {
|
|
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
|
|
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
|
|
|
|
return this.map(function() {
|
|
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
|
|
});
|
|
},
|
|
|
|
html: function( value ) {
|
|
return access( this, function( value ) {
|
|
var elem = this[ 0 ] || {},
|
|
i = 0,
|
|
l = this.length;
|
|
|
|
if ( value === undefined && elem.nodeType === 1 ) {
|
|
return elem.innerHTML;
|
|
}
|
|
|
|
// See if we can take a shortcut and just use innerHTML
|
|
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
|
|
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
|
|
|
|
value = value.replace( rxhtmlTag, "<$1></$2>" );
|
|
|
|
try {
|
|
for ( ; i < l; i++ ) {
|
|
elem = this[ i ] || {};
|
|
|
|
// Remove element nodes and prevent memory leaks
|
|
if ( elem.nodeType === 1 ) {
|
|
jQuery.cleanData( getAll( elem, false ) );
|
|
elem.innerHTML = value;
|
|
}
|
|
}
|
|
|
|
elem = 0;
|
|
|
|
// If using innerHTML throws an exception, use the fallback method
|
|
} catch( e ) {}
|
|
}
|
|
|
|
if ( elem ) {
|
|
this.empty().append( value );
|
|
}
|
|
}, null, value, arguments.length );
|
|
},
|
|
|
|
replaceWith: function() {
|
|
var arg = arguments[ 0 ];
|
|
|
|
// Make the changes, replacing each context element with the new content
|
|
this.domManip( arguments, function( elem ) {
|
|
arg = this.parentNode;
|
|
|
|
jQuery.cleanData( getAll( this ) );
|
|
|
|
if ( arg ) {
|
|
arg.replaceChild( elem, this );
|
|
}
|
|
});
|
|
|
|
// Force removal if there was no new content (e.g., from empty arguments)
|
|
return arg && (arg.length || arg.nodeType) ? this : this.remove();
|
|
},
|
|
|
|
detach: function( selector ) {
|
|
return this.remove( selector, true );
|
|
},
|
|
|
|
domManip: function( args, callback ) {
|
|
|
|
// Flatten any nested arrays
|
|
args = concat.apply( [], args );
|
|
|
|
var fragment, first, scripts, hasScripts, node, doc,
|
|
i = 0,
|
|
l = this.length,
|
|
set = this,
|
|
iNoClone = l - 1,
|
|
value = args[ 0 ],
|
|
isFunction = jQuery.isFunction( value );
|
|
|
|
// We can't cloneNode fragments that contain checked, in WebKit
|
|
if ( isFunction ||
|
|
( l > 1 && typeof value === "string" &&
|
|
!support.checkClone && rchecked.test( value ) ) ) {
|
|
return this.each(function( index ) {
|
|
var self = set.eq( index );
|
|
if ( isFunction ) {
|
|
args[ 0 ] = value.call( this, index, self.html() );
|
|
}
|
|
self.domManip( args, callback );
|
|
});
|
|
}
|
|
|
|
if ( l ) {
|
|
fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
|
|
first = fragment.firstChild;
|
|
|
|
if ( fragment.childNodes.length === 1 ) {
|
|
fragment = first;
|
|
}
|
|
|
|
if ( first ) {
|
|
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
|
|
hasScripts = scripts.length;
|
|
|
|
// Use the original fragment for the last item instead of the first because it can end up
|
|
// being emptied incorrectly in certain situations (#8070).
|
|
for ( ; i < l; i++ ) {
|
|
node = fragment;
|
|
|
|
if ( i !== iNoClone ) {
|
|
node = jQuery.clone( node, true, true );
|
|
|
|
// Keep references to cloned scripts for later restoration
|
|
if ( hasScripts ) {
|
|
// Support: QtWebKit
|
|
// jQuery.merge because push.apply(_, arraylike) throws
|
|
jQuery.merge( scripts, getAll( node, "script" ) );
|
|
}
|
|
}
|
|
|
|
callback.call( this[ i ], node, i );
|
|
}
|
|
|
|
if ( hasScripts ) {
|
|
doc = scripts[ scripts.length - 1 ].ownerDocument;
|
|
|
|
// Reenable scripts
|
|
jQuery.map( scripts, restoreScript );
|
|
|
|
// Evaluate executable scripts on first document insertion
|
|
for ( i = 0; i < hasScripts; i++ ) {
|
|
node = scripts[ i ];
|
|
if ( rscriptType.test( node.type || "" ) &&
|
|
!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
|
|
|
|
if ( node.src ) {
|
|
// Optional AJAX dependency, but won't run scripts if not present
|
|
if ( jQuery._evalUrl ) {
|
|
jQuery._evalUrl( node.src );
|
|
}
|
|
} else {
|
|
jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
}
|
|
});
|
|
|
|
jQuery.each({
|
|
appendTo: "append",
|
|
prependTo: "prepend",
|
|
insertBefore: "before",
|
|
insertAfter: "after",
|
|
replaceAll: "replaceWith"
|
|
}, function( name, original ) {
|
|
jQuery.fn[ name ] = function( selector ) {
|
|
var elems,
|
|
ret = [],
|
|
insert = jQuery( selector ),
|
|
last = insert.length - 1,
|
|
i = 0;
|
|
|
|
for ( ; i <= last; i++ ) {
|
|
elems = i === last ? this : this.clone( true );
|
|
jQuery( insert[ i ] )[ original ]( elems );
|
|
|
|
// Support: QtWebKit
|
|
// .get() because push.apply(_, arraylike) throws
|
|
push.apply( ret, elems.get() );
|
|
}
|
|
|
|
return this.pushStack( ret );
|
|
};
|
|
});
|
|
|
|
|
|
var iframe,
|
|
elemdisplay = {};
|
|
|
|
/**
|
|
* Retrieve the actual display of a element
|
|
* @param {String} name nodeName of the element
|
|
* @param {Object} doc Document object
|
|
*/
|
|
// Called only from within defaultDisplay
|
|
function actualDisplay( name, doc ) {
|
|
var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
|
|
|
|
// getDefaultComputedStyle might be reliably used only on attached element
|
|
display = window.getDefaultComputedStyle ?
|
|
|
|
// Use of this method is a temporary fix (more like optmization) until something better comes along,
|
|
// since it was removed from specification and supported only in FF
|
|
window.getDefaultComputedStyle( elem[ 0 ] ).display : jQuery.css( elem[ 0 ], "display" );
|
|
|
|
// We don't have any data stored on the element,
|
|
// so use "detach" method as fast way to get rid of the element
|
|
elem.detach();
|
|
|
|
return display;
|
|
}
|
|
|
|
/**
|
|
* Try to determine the default display value of an element
|
|
* @param {String} nodeName
|
|
*/
|
|
function defaultDisplay( nodeName ) {
|
|
var doc = document,
|
|
display = elemdisplay[ nodeName ];
|
|
|
|
if ( !display ) {
|
|
display = actualDisplay( nodeName, doc );
|
|
|
|
// If the simple way fails, read from inside an iframe
|
|
if ( display === "none" || !display ) {
|
|
|
|
// Use the already-created iframe if possible
|
|
iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
|
|
|
|
// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
|
|
doc = iframe[ 0 ].contentDocument;
|
|
|
|
// Support: IE
|
|
doc.write();
|
|
doc.close();
|
|
|
|
display = actualDisplay( nodeName, doc );
|
|
iframe.detach();
|
|
}
|
|
|
|
// Store the correct default display
|
|
elemdisplay[ nodeName ] = display;
|
|
}
|
|
|
|
return display;
|
|
}
|
|
var rmargin = (/^margin/);
|
|
|
|
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
|
|
|
|
var getStyles = function( elem ) {
|
|
return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
|
|
};
|
|
|
|
|
|
|
|
function curCSS( elem, name, computed ) {
|
|
var width, minWidth, maxWidth, ret,
|
|
style = elem.style;
|
|
|
|
computed = computed || getStyles( elem );
|
|
|
|
// Support: IE9
|
|
// getPropertyValue is only needed for .css('filter') in IE9, see #12537
|
|
if ( computed ) {
|
|
ret = computed.getPropertyValue( name ) || computed[ name ];
|
|
}
|
|
|
|
if ( computed ) {
|
|
|
|
if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
|
|
ret = jQuery.style( elem, name );
|
|
}
|
|
|
|
// Support: iOS < 6
|
|
// A tribute to the "awesome hack by Dean Edwards"
|
|
// iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
|
|
// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
|
|
if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
|
|
|
|
// Remember the original values
|
|
width = style.width;
|
|
minWidth = style.minWidth;
|
|
maxWidth = style.maxWidth;
|
|
|
|
// Put in the new values to get a computed value out
|
|
style.minWidth = style.maxWidth = style.width = ret;
|
|
ret = computed.width;
|
|
|
|
// Revert the changed values
|
|
style.width = width;
|
|
style.minWidth = minWidth;
|
|
style.maxWidth = maxWidth;
|
|
}
|
|
}
|
|
|
|
return ret !== undefined ?
|
|
// Support: IE
|
|
// IE returns zIndex value as an integer.
|
|
ret + "" :
|
|
ret;
|
|
}
|
|
|
|
|
|
function addGetHookIf( conditionFn, hookFn ) {
|
|
// Define the hook, we'll check on the first run if it's really needed.
|
|
return {
|
|
get: function() {
|
|
if ( conditionFn() ) {
|
|
// Hook not needed (or it's not possible to use it due to missing dependency),
|
|
// remove it.
|
|
// Since there are no other hooks for marginRight, remove the whole object.
|
|
delete this.get;
|
|
return;
|
|
}
|
|
|
|
// Hook needed; redefine it so that the support test is not executed again.
|
|
|
|
return (this.get = hookFn).apply( this, arguments );
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
(function() {
|
|
var pixelPositionVal, boxSizingReliableVal,
|
|
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
|
|
divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;" +
|
|
"-moz-box-sizing:content-box;box-sizing:content-box",
|
|
docElem = document.documentElement,
|
|
container = document.createElement( "div" ),
|
|
div = document.createElement( "div" );
|
|
|
|
div.style.backgroundClip = "content-box";
|
|
div.cloneNode( true ).style.backgroundClip = "";
|
|
support.clearCloneStyle = div.style.backgroundClip === "content-box";
|
|
|
|
container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;" +
|
|
"margin-top:1px";
|
|
container.appendChild( div );
|
|
|
|
// Executing both pixelPosition & boxSizingReliable tests require only one layout
|
|
// so they're executed at the same time to save the second computation.
|
|
function computePixelPositionAndBoxSizingReliable() {
|
|
// Support: Firefox, Android 2.3 (Prefixed box-sizing versions).
|
|
div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
|
|
"box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;" +
|
|
"position:absolute;top:1%";
|
|
docElem.appendChild( container );
|
|
|
|
var divStyle = window.getComputedStyle( div, null );
|
|
pixelPositionVal = divStyle.top !== "1%";
|
|
boxSizingReliableVal = divStyle.width === "4px";
|
|
|
|
docElem.removeChild( container );
|
|
}
|
|
|
|
// Use window.getComputedStyle because jsdom on node.js will break without it.
|
|
if ( window.getComputedStyle ) {
|
|
jQuery.extend(support, {
|
|
pixelPosition: function() {
|
|
// This test is executed only once but we still do memoizing
|
|
// since we can use the boxSizingReliable pre-computing.
|
|
// No need to check if the test was already performed, though.
|
|
computePixelPositionAndBoxSizingReliable();
|
|
return pixelPositionVal;
|
|
},
|
|
boxSizingReliable: function() {
|
|
if ( boxSizingReliableVal == null ) {
|
|
computePixelPositionAndBoxSizingReliable();
|
|
}
|
|
return boxSizingReliableVal;
|
|
},
|
|
reliableMarginRight: function() {
|
|
// Support: Android 2.3
|
|
// Check if div with explicit width and no margin-right incorrectly
|
|
// gets computed margin-right based on width of container. (#3333)
|
|
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
|
|
// This support function is only executed once so no memoizing is needed.
|
|
var ret,
|
|
marginDiv = div.appendChild( document.createElement( "div" ) );
|
|
marginDiv.style.cssText = div.style.cssText = divReset;
|
|
marginDiv.style.marginRight = marginDiv.style.width = "0";
|
|
div.style.width = "1px";
|
|
docElem.appendChild( container );
|
|
|
|
ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
|
|
|
|
docElem.removeChild( container );
|
|
|
|
// Clean up the div for other support tests.
|
|
div.innerHTML = "";
|
|
|
|
return ret;
|
|
}
|
|
});
|
|
}
|
|
})();
|
|
|
|
|
|
// A method for quickly swapping in/out CSS properties to get correct calculations.
|
|
jQuery.swap = function( elem, options, callback, args ) {
|
|
var ret, name,
|
|
old = {};
|
|
|
|
// Remember the old values, and insert the new ones
|
|
for ( name in options ) {
|
|
old[ name ] = elem.style[ name ];
|
|
elem.style[ name ] = options[ name ];
|
|
}
|
|
|
|
ret = callback.apply( elem, args || [] );
|
|
|
|
// Revert the old values
|
|
for ( name in options ) {
|
|
elem.style[ name ] = old[ name ];
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
|
|
var
|
|
// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
|
|
// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
|
|
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
|
|
rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
|
|
rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
|
|
|
|
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
|
|
cssNormalTransform = {
|
|
letterSpacing: 0,
|
|
fontWeight: 400
|
|
},
|
|
|
|
cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
|
|
|
|
// return a css property mapped to a potentially vendor prefixed property
|
|
function vendorPropName( style, name ) {
|
|
|
|
// shortcut for names that are not vendor prefixed
|
|
if ( name in style ) {
|
|
return name;
|
|
}
|
|
|
|
// check for vendor prefixed names
|
|
var capName = name[0].toUpperCase() + name.slice(1),
|
|
origName = name,
|
|
i = cssPrefixes.length;
|
|
|
|
while ( i-- ) {
|
|
name = cssPrefixes[ i ] + capName;
|
|
if ( name in style ) {
|
|
return name;
|
|
}
|
|
}
|
|
|
|
return origName;
|
|
}
|
|
|
|
function setPositiveNumber( elem, value, subtract ) {
|
|
var matches = rnumsplit.exec( value );
|
|
return matches ?
|
|
// Guard against undefined "subtract", e.g., when used as in cssHooks
|
|
Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
|
|
value;
|
|
}
|
|
|
|
function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
|
|
var i = extra === ( isBorderBox ? "border" : "content" ) ?
|
|
// If we already have the right measurement, avoid augmentation
|
|
4 :
|
|
// Otherwise initialize for horizontal or vertical properties
|
|
name === "width" ? 1 : 0,
|
|
|
|
val = 0;
|
|
|
|
for ( ; i < 4; i += 2 ) {
|
|
// both box models exclude margin, so add it if we want it
|
|
if ( extra === "margin" ) {
|
|
val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
|
|
}
|
|
|
|
if ( isBorderBox ) {
|
|
// border-box includes padding, so remove it if we want content
|
|
if ( extra === "content" ) {
|
|
val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
|
}
|
|
|
|
// at this point, extra isn't border nor margin, so remove border
|
|
if ( extra !== "margin" ) {
|
|
val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
|
}
|
|
} else {
|
|
// at this point, extra isn't content, so add padding
|
|
val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
|
|
|
// at this point, extra isn't content nor padding, so add border
|
|
if ( extra !== "padding" ) {
|
|
val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
|
}
|
|
}
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
function getWidthOrHeight( elem, name, extra ) {
|
|
|
|
// Start with offset property, which is equivalent to the border-box value
|
|
var valueIsBorderBox = true,
|
|
val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
|
|
styles = getStyles( elem ),
|
|
isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
|
|
|
|
// some non-html elements return undefined for offsetWidth, so check for null/undefined
|
|
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
|
|
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
|
|
if ( val <= 0 || val == null ) {
|
|
// Fall back to computed then uncomputed css if necessary
|
|
val = curCSS( elem, name, styles );
|
|
if ( val < 0 || val == null ) {
|
|
val = elem.style[ name ];
|
|
}
|
|
|
|
// Computed unit is not pixels. Stop here and return.
|
|
if ( rnumnonpx.test(val) ) {
|
|
return val;
|
|
}
|
|
|
|
// we need the check for style in case a browser which returns unreliable values
|
|
// for getComputedStyle silently falls back to the reliable elem.style
|
|
valueIsBorderBox = isBorderBox &&
|
|
( support.boxSizingReliable() || val === elem.style[ name ] );
|
|
|
|
// Normalize "", auto, and prepare for extra
|
|
val = parseFloat( val ) || 0;
|
|
}
|
|
|
|
// use the active box-sizing model to add/subtract irrelevant styles
|
|
return ( val +
|
|
augmentWidthOrHeight(
|
|
elem,
|
|
name,
|
|
extra || ( isBorderBox ? "border" : "content" ),
|
|
valueIsBorderBox,
|
|
styles
|
|
)
|
|
) + "px";
|
|
}
|
|
|
|
function showHide( elements, show ) {
|
|
var display, elem, hidden,
|
|
values = [],
|
|
index = 0,
|
|
length = elements.length;
|
|
|
|
for ( ; index < length; index++ ) {
|
|
elem = elements[ index ];
|
|
if ( !elem.style ) {
|
|
continue;
|
|
}
|
|
|
|
values[ index ] = data_priv.get( elem, "olddisplay" );
|
|
display = elem.style.display;
|
|
if ( show ) {
|
|
// Reset the inline display of this element to learn if it is
|
|
// being hidden by cascaded rules or not
|
|
if ( !values[ index ] && display === "none" ) {
|
|
elem.style.display = "";
|
|
}
|
|
|
|
// Set elements which have been overridden with display: none
|
|
// in a stylesheet to whatever the default browser style is
|
|
// for such an element
|
|
if ( elem.style.display === "" && isHidden( elem ) ) {
|
|
values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
|
|
}
|
|
} else {
|
|
|
|
if ( !values[ index ] ) {
|
|
hidden = isHidden( elem );
|
|
|
|
if ( display && display !== "none" || !hidden ) {
|
|
data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the display of most of the elements in a second loop
|
|
// to avoid the constant reflow
|
|
for ( index = 0; index < length; index++ ) {
|
|
elem = elements[ index ];
|
|
if ( !elem.style ) {
|
|
continue;
|
|
}
|
|
if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
|
|
elem.style.display = show ? values[ index ] || "" : "none";
|
|
}
|
|
}
|
|
|
|
return elements;
|
|
}
|
|
|
|
jQuery.extend({
|
|
// Add in style property hooks for overriding the default
|
|
// behavior of getting and setting a style property
|
|
cssHooks: {
|
|
opacity: {
|
|
get: function( elem, computed ) {
|
|
if ( computed ) {
|
|
// We should always get a number back from opacity
|
|
var ret = curCSS( elem, "opacity" );
|
|
return ret === "" ? "1" : ret;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
// Don't automatically add "px" to these possibly-unitless properties
|
|
cssNumber: {
|
|
"columnCount": true,
|
|
"fillOpacity": true,
|
|
"fontWeight": true,
|
|
"lineHeight": true,
|
|
"opacity": true,
|
|
"order": true,
|
|
"orphans": true,
|
|
"widows": true,
|
|
"zIndex": true,
|
|
"zoom": true
|
|
},
|
|
|
|
// Add in properties whose names you wish to fix before
|
|
// setting or getting the value
|
|
cssProps: {
|
|
// normalize float css property
|
|
"float": "cssFloat"
|
|
},
|
|
|
|
// Get and set the style property on a DOM Node
|
|
style: function( elem, name, value, extra ) {
|
|
// Don't set styles on text and comment nodes
|
|
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
|
|
return;
|
|
}
|
|
|
|
// Make sure that we're working with the right name
|
|
var ret, type, hooks,
|
|
origName = jQuery.camelCase( name ),
|
|
style = elem.style;
|
|
|
|
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
|
|
|
|
// gets hook for the prefixed version
|
|
// followed by the unprefixed version
|
|
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
|
|
|
|
// Check if we're setting a value
|
|
if ( value !== undefined ) {
|
|
type = typeof value;
|
|
|
|
// convert relative number strings (+= or -=) to relative numbers. #7345
|
|
if ( type === "string" && (ret = rrelNum.exec( value )) ) {
|
|
value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
|
|
// Fixes bug #9237
|
|
type = "number";
|
|
}
|
|
|
|
// Make sure that null and NaN values aren't set. See: #7116
|
|
if ( value == null || value !== value ) {
|
|
return;
|
|
}
|
|
|
|
// If a number was passed in, add 'px' to the (except for certain CSS properties)
|
|
if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
|
|
value += "px";
|
|
}
|
|
|
|
// Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
|
|
// but it would mean to define eight (for every problematic property) identical functions
|
|
if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
|
|
style[ name ] = "inherit";
|
|
}
|
|
|
|
// If a hook was provided, use that value, otherwise just set the specified value
|
|
if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
|
|
// Support: Chrome, Safari
|
|
// Setting style to blank string required to delete "style: x !important;"
|
|
style[ name ] = "";
|
|
style[ name ] = value;
|
|
}
|
|
|
|
} else {
|
|
// If a hook was provided get the non-computed value from there
|
|
if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
|
|
return ret;
|
|
}
|
|
|
|
// Otherwise just get the value from the style object
|
|
return style[ name ];
|
|
}
|
|
},
|
|
|
|
css: function( elem, name, extra, styles ) {
|
|
var val, num, hooks,
|
|
origName = jQuery.camelCase( name );
|
|
|
|
// Make sure that we're working with the right name
|
|
name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
|
|
|
|
// gets hook for the prefixed version
|
|
// followed by the unprefixed version
|
|
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
|
|
|
|
// If a hook was provided get the computed value from there
|
|
if ( hooks && "get" in hooks ) {
|
|
val = hooks.get( elem, true, extra );
|
|
}
|
|
|
|
// Otherwise, if a way to get the computed value exists, use that
|
|
if ( val === undefined ) {
|
|
val = curCSS( elem, name, styles );
|
|
}
|
|
|
|
//convert "normal" to computed value
|
|
if ( val === "normal" && name in cssNormalTransform ) {
|
|
val = cssNormalTransform[ name ];
|
|
}
|
|
|
|
// Return, converting to number if forced or a qualifier was provided and val looks numeric
|
|
if ( extra === "" || extra ) {
|
|
num = parseFloat( val );
|
|
return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
|
|
}
|
|
return val;
|
|
}
|
|
});
|
|
|
|
jQuery.each([ "height", "width" ], function( i, name ) {
|
|
jQuery.cssHooks[ name ] = {
|
|
get: function( elem, computed, extra ) {
|
|
if ( computed ) {
|
|
// certain elements can have dimension info if we invisibly show them
|
|
// however, it must have a current display style that would benefit from this
|
|
return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
|
|
jQuery.swap( elem, cssShow, function() {
|
|
return getWidthOrHeight( elem, name, extra );
|
|
}) :
|
|
getWidthOrHeight( elem, name, extra );
|
|
}
|
|
},
|
|
|
|
set: function( elem, value, extra ) {
|
|
var styles = extra && getStyles( elem );
|
|
return setPositiveNumber( elem, value, extra ?
|
|
augmentWidthOrHeight(
|
|
elem,
|
|
name,
|
|
extra,
|
|
jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
|
|
styles
|
|
) : 0
|
|
);
|
|
}
|
|
};
|
|
});
|
|
|
|
// Support: Android 2.3
|
|
jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
|
|
function( elem, computed ) {
|
|
if ( computed ) {
|
|
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
|
|
// Work around by temporarily setting element display to inline-block
|
|
return jQuery.swap( elem, { "display": "inline-block" },
|
|
curCSS, [ elem, "marginRight" ] );
|
|
}
|
|
}
|
|
);
|
|
|
|
// These hooks are used by animate to expand properties
|
|
jQuery.each({
|
|
margin: "",
|
|
padding: "",
|
|
border: "Width"
|
|
}, function( prefix, suffix ) {
|
|
jQuery.cssHooks[ prefix + suffix ] = {
|
|
expand: function( value ) {
|
|
var i = 0,
|
|
expanded = {},
|
|
|
|
// assumes a single number if not a string
|
|
parts = typeof value === "string" ? value.split(" ") : [ value ];
|
|
|
|
for ( ; i < 4; i++ ) {
|
|
expanded[ prefix + cssExpand[ i ] + suffix ] =
|
|
parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
|
|
}
|
|
|
|
return expanded;
|
|
}
|
|
};
|
|
|
|
if ( !rmargin.test( prefix ) ) {
|
|
jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
|
|
}
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
css: function( name, value ) {
|
|
return access( this, function( elem, name, value ) {
|
|
var styles, len,
|
|
map = {},
|
|
i = 0;
|
|
|
|
if ( jQuery.isArray( name ) ) {
|
|
styles = getStyles( elem );
|
|
len = name.length;
|
|
|
|
for ( ; i < len; i++ ) {
|
|
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
|
|
}
|
|
|
|
return map;
|
|
}
|
|
|
|
return value !== undefined ?
|
|
jQuery.style( elem, name, value ) :
|
|
jQuery.css( elem, name );
|
|
}, name, value, arguments.length > 1 );
|
|
},
|
|
show: function() {
|
|
return showHide( this, true );
|
|
},
|
|
hide: function() {
|
|
return showHide( this );
|
|
},
|
|
toggle: function( state ) {
|
|
if ( typeof state === "boolean" ) {
|
|
return state ? this.show() : this.hide();
|
|
}
|
|
|
|
return this.each(function() {
|
|
if ( isHidden( this ) ) {
|
|
jQuery( this ).show();
|
|
} else {
|
|
jQuery( this ).hide();
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
function Tween( elem, options, prop, end, easing ) {
|
|
return new Tween.prototype.init( elem, options, prop, end, easing );
|
|
}
|
|
jQuery.Tween = Tween;
|
|
|
|
Tween.prototype = {
|
|
constructor: Tween,
|
|
init: function( elem, options, prop, end, easing, unit ) {
|
|
this.elem = elem;
|
|
this.prop = prop;
|
|
this.easing = easing || "swing";
|
|
this.options = options;
|
|
this.start = this.now = this.cur();
|
|
this.end = end;
|
|
this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
|
|
},
|
|
cur: function() {
|
|
var hooks = Tween.propHooks[ this.prop ];
|
|
|
|
return hooks && hooks.get ?
|
|
hooks.get( this ) :
|
|
Tween.propHooks._default.get( this );
|
|
},
|
|
run: function( percent ) {
|
|
var eased,
|
|
hooks = Tween.propHooks[ this.prop ];
|
|
|
|
if ( this.options.duration ) {
|
|
this.pos = eased = jQuery.easing[ this.easing ](
|
|
percent, this.options.duration * percent, 0, 1, this.options.duration
|
|
);
|
|
} else {
|
|
this.pos = eased = percent;
|
|
}
|
|
this.now = ( this.end - this.start ) * eased + this.start;
|
|
|
|
if ( this.options.step ) {
|
|
this.options.step.call( this.elem, this.now, this );
|
|
}
|
|
|
|
if ( hooks && hooks.set ) {
|
|
hooks.set( this );
|
|
} else {
|
|
Tween.propHooks._default.set( this );
|
|
}
|
|
return this;
|
|
}
|
|
};
|
|
|
|
Tween.prototype.init.prototype = Tween.prototype;
|
|
|
|
Tween.propHooks = {
|
|
_default: {
|
|
get: function( tween ) {
|
|
var result;
|
|
|
|
if ( tween.elem[ tween.prop ] != null &&
|
|
(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
|
|
return tween.elem[ tween.prop ];
|
|
}
|
|
|
|
// passing an empty string as a 3rd parameter to .css will automatically
|
|
// attempt a parseFloat and fallback to a string if the parse fails
|
|
// so, simple values such as "10px" are parsed to Float.
|
|
// complex values such as "rotate(1rad)" are returned as is.
|
|
result = jQuery.css( tween.elem, tween.prop, "" );
|
|
// Empty strings, null, undefined and "auto" are converted to 0.
|
|
return !result || result === "auto" ? 0 : result;
|
|
},
|
|
set: function( tween ) {
|
|
// use step hook for back compat - use cssHook if its there - use .style if its
|
|
// available and use plain properties where available
|
|
if ( jQuery.fx.step[ tween.prop ] ) {
|
|
jQuery.fx.step[ tween.prop ]( tween );
|
|
} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
|
|
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
|
|
} else {
|
|
tween.elem[ tween.prop ] = tween.now;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Support: IE9
|
|
// Panic based approach to setting things on disconnected nodes
|
|
|
|
Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
|
|
set: function( tween ) {
|
|
if ( tween.elem.nodeType && tween.elem.parentNode ) {
|
|
tween.elem[ tween.prop ] = tween.now;
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.easing = {
|
|
linear: function( p ) {
|
|
return p;
|
|
},
|
|
swing: function( p ) {
|
|
return 0.5 - Math.cos( p * Math.PI ) / 2;
|
|
}
|
|
};
|
|
|
|
jQuery.fx = Tween.prototype.init;
|
|
|
|
// Back Compat <1.8 extension point
|
|
jQuery.fx.step = {};
|
|
|
|
|
|
|
|
|
|
var
|
|
fxNow, timerId,
|
|
rfxtypes = /^(?:toggle|show|hide)$/,
|
|
rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
|
|
rrun = /queueHooks$/,
|
|
animationPrefilters = [ defaultPrefilter ],
|
|
tweeners = {
|
|
"*": [ function( prop, value ) {
|
|
var tween = this.createTween( prop, value ),
|
|
target = tween.cur(),
|
|
parts = rfxnum.exec( value ),
|
|
unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
|
|
|
|
// Starting value computation is required for potential unit mismatches
|
|
start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
|
|
rfxnum.exec( jQuery.css( tween.elem, prop ) ),
|
|
scale = 1,
|
|
maxIterations = 20;
|
|
|
|
if ( start && start[ 3 ] !== unit ) {
|
|
// Trust units reported by jQuery.css
|
|
unit = unit || start[ 3 ];
|
|
|
|
// Make sure we update the tween properties later on
|
|
parts = parts || [];
|
|
|
|
// Iteratively approximate from a nonzero starting point
|
|
start = +target || 1;
|
|
|
|
do {
|
|
// If previous iteration zeroed out, double until we get *something*
|
|
// Use a string for doubling factor so we don't accidentally see scale as unchanged below
|
|
scale = scale || ".5";
|
|
|
|
// Adjust and apply
|
|
start = start / scale;
|
|
jQuery.style( tween.elem, prop, start + unit );
|
|
|
|
// Update scale, tolerating zero or NaN from tween.cur()
|
|
// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
|
|
} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
|
|
}
|
|
|
|
// Update tween properties
|
|
if ( parts ) {
|
|
start = tween.start = +start || +target || 0;
|
|
tween.unit = unit;
|
|
// If a +=/-= token was provided, we're doing a relative animation
|
|
tween.end = parts[ 1 ] ?
|
|
start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
|
|
+parts[ 2 ];
|
|
}
|
|
|
|
return tween;
|
|
} ]
|
|
};
|
|
|
|
// Animations created synchronously will run synchronously
|
|
function createFxNow() {
|
|
setTimeout(function() {
|
|
fxNow = undefined;
|
|
});
|
|
return ( fxNow = jQuery.now() );
|
|
}
|
|
|
|
// Generate parameters to create a standard animation
|
|
function genFx( type, includeWidth ) {
|
|
var which,
|
|
i = 0,
|
|
attrs = { height: type };
|
|
|
|
// if we include width, step value is 1 to do all cssExpand values,
|
|
// if we don't include width, step value is 2 to skip over Left and Right
|
|
includeWidth = includeWidth ? 1 : 0;
|
|
for ( ; i < 4 ; i += 2 - includeWidth ) {
|
|
which = cssExpand[ i ];
|
|
attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
|
|
}
|
|
|
|
if ( includeWidth ) {
|
|
attrs.opacity = attrs.width = type;
|
|
}
|
|
|
|
return attrs;
|
|
}
|
|
|
|
function createTween( value, prop, animation ) {
|
|
var tween,
|
|
collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
|
|
index = 0,
|
|
length = collection.length;
|
|
for ( ; index < length; index++ ) {
|
|
if ( (tween = collection[ index ].call( animation, prop, value )) ) {
|
|
|
|
// we're done with this property
|
|
return tween;
|
|
}
|
|
}
|
|
}
|
|
|
|
function defaultPrefilter( elem, props, opts ) {
|
|
/* jshint validthis: true */
|
|
var prop, value, toggle, tween, hooks, oldfire, display,
|
|
anim = this,
|
|
orig = {},
|
|
style = elem.style,
|
|
hidden = elem.nodeType && isHidden( elem ),
|
|
dataShow = data_priv.get( elem, "fxshow" );
|
|
|
|
// handle queue: false promises
|
|
if ( !opts.queue ) {
|
|
hooks = jQuery._queueHooks( elem, "fx" );
|
|
if ( hooks.unqueued == null ) {
|
|
hooks.unqueued = 0;
|
|
oldfire = hooks.empty.fire;
|
|
hooks.empty.fire = function() {
|
|
if ( !hooks.unqueued ) {
|
|
oldfire();
|
|
}
|
|
};
|
|
}
|
|
hooks.unqueued++;
|
|
|
|
anim.always(function() {
|
|
// doing this makes sure that the complete handler will be called
|
|
// before this completes
|
|
anim.always(function() {
|
|
hooks.unqueued--;
|
|
if ( !jQuery.queue( elem, "fx" ).length ) {
|
|
hooks.empty.fire();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// height/width overflow pass
|
|
if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
|
|
// Make sure that nothing sneaks out
|
|
// Record all 3 overflow attributes because IE9-10 do not
|
|
// change the overflow attribute when overflowX and
|
|
// overflowY are set to the same value
|
|
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
|
|
|
|
// Set display property to inline-block for height/width
|
|
// animations on inline elements that are having width/height animated
|
|
display = jQuery.css( elem, "display" );
|
|
// Get default display if display is currently "none"
|
|
if ( display === "none" ) {
|
|
display = defaultDisplay( elem.nodeName );
|
|
}
|
|
if ( display === "inline" &&
|
|
jQuery.css( elem, "float" ) === "none" ) {
|
|
|
|
style.display = "inline-block";
|
|
}
|
|
}
|
|
|
|
if ( opts.overflow ) {
|
|
style.overflow = "hidden";
|
|
anim.always(function() {
|
|
style.overflow = opts.overflow[ 0 ];
|
|
style.overflowX = opts.overflow[ 1 ];
|
|
style.overflowY = opts.overflow[ 2 ];
|
|
});
|
|
}
|
|
|
|
// show/hide pass
|
|
for ( prop in props ) {
|
|
value = props[ prop ];
|
|
if ( rfxtypes.exec( value ) ) {
|
|
delete props[ prop ];
|
|
toggle = toggle || value === "toggle";
|
|
if ( value === ( hidden ? "hide" : "show" ) ) {
|
|
|
|
// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
|
|
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
|
|
hidden = true;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
|
|
}
|
|
}
|
|
|
|
if ( !jQuery.isEmptyObject( orig ) ) {
|
|
if ( dataShow ) {
|
|
if ( "hidden" in dataShow ) {
|
|
hidden = dataShow.hidden;
|
|
}
|
|
} else {
|
|
dataShow = data_priv.access( elem, "fxshow", {} );
|
|
}
|
|
|
|
// store state if its toggle - enables .stop().toggle() to "reverse"
|
|
if ( toggle ) {
|
|
dataShow.hidden = !hidden;
|
|
}
|
|
if ( hidden ) {
|
|
jQuery( elem ).show();
|
|
} else {
|
|
anim.done(function() {
|
|
jQuery( elem ).hide();
|
|
});
|
|
}
|
|
anim.done(function() {
|
|
var prop;
|
|
|
|
data_priv.remove( elem, "fxshow" );
|
|
for ( prop in orig ) {
|
|
jQuery.style( elem, prop, orig[ prop ] );
|
|
}
|
|
});
|
|
for ( prop in orig ) {
|
|
tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
|
|
|
|
if ( !( prop in dataShow ) ) {
|
|
dataShow[ prop ] = tween.start;
|
|
if ( hidden ) {
|
|
tween.end = tween.start;
|
|
tween.start = prop === "width" || prop === "height" ? 1 : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function propFilter( props, specialEasing ) {
|
|
var index, name, easing, value, hooks;
|
|
|
|
// camelCase, specialEasing and expand cssHook pass
|
|
for ( index in props ) {
|
|
name = jQuery.camelCase( index );
|
|
easing = specialEasing[ name ];
|
|
value = props[ index ];
|
|
if ( jQuery.isArray( value ) ) {
|
|
easing = value[ 1 ];
|
|
value = props[ index ] = value[ 0 ];
|
|
}
|
|
|
|
if ( index !== name ) {
|
|
props[ name ] = value;
|
|
delete props[ index ];
|
|
}
|
|
|
|
hooks = jQuery.cssHooks[ name ];
|
|
if ( hooks && "expand" in hooks ) {
|
|
value = hooks.expand( value );
|
|
delete props[ name ];
|
|
|
|
// not quite $.extend, this wont overwrite keys already present.
|
|
// also - reusing 'index' from above because we have the correct "name"
|
|
for ( index in value ) {
|
|
if ( !( index in props ) ) {
|
|
props[ index ] = value[ index ];
|
|
specialEasing[ index ] = easing;
|
|
}
|
|
}
|
|
} else {
|
|
specialEasing[ name ] = easing;
|
|
}
|
|
}
|
|
}
|
|
|
|
function Animation( elem, properties, options ) {
|
|
var result,
|
|
stopped,
|
|
index = 0,
|
|
length = animationPrefilters.length,
|
|
deferred = jQuery.Deferred().always( function() {
|
|
// don't match elem in the :animated selector
|
|
delete tick.elem;
|
|
}),
|
|
tick = function() {
|
|
if ( stopped ) {
|
|
return false;
|
|
}
|
|
var currentTime = fxNow || createFxNow(),
|
|
remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
|
|
// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
|
|
temp = remaining / animation.duration || 0,
|
|
percent = 1 - temp,
|
|
index = 0,
|
|
length = animation.tweens.length;
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
animation.tweens[ index ].run( percent );
|
|
}
|
|
|
|
deferred.notifyWith( elem, [ animation, percent, remaining ]);
|
|
|
|
if ( percent < 1 && length ) {
|
|
return remaining;
|
|
} else {
|
|
deferred.resolveWith( elem, [ animation ] );
|
|
return false;
|
|
}
|
|
},
|
|
animation = deferred.promise({
|
|
elem: elem,
|
|
props: jQuery.extend( {}, properties ),
|
|
opts: jQuery.extend( true, { specialEasing: {} }, options ),
|
|
originalProperties: properties,
|
|
originalOptions: options,
|
|
startTime: fxNow || createFxNow(),
|
|
duration: options.duration,
|
|
tweens: [],
|
|
createTween: function( prop, end ) {
|
|
var tween = jQuery.Tween( elem, animation.opts, prop, end,
|
|
animation.opts.specialEasing[ prop ] || animation.opts.easing );
|
|
animation.tweens.push( tween );
|
|
return tween;
|
|
},
|
|
stop: function( gotoEnd ) {
|
|
var index = 0,
|
|
// if we are going to the end, we want to run all the tweens
|
|
// otherwise we skip this part
|
|
length = gotoEnd ? animation.tweens.length : 0;
|
|
if ( stopped ) {
|
|
return this;
|
|
}
|
|
stopped = true;
|
|
for ( ; index < length ; index++ ) {
|
|
animation.tweens[ index ].run( 1 );
|
|
}
|
|
|
|
// resolve when we played the last frame
|
|
// otherwise, reject
|
|
if ( gotoEnd ) {
|
|
deferred.resolveWith( elem, [ animation, gotoEnd ] );
|
|
} else {
|
|
deferred.rejectWith( elem, [ animation, gotoEnd ] );
|
|
}
|
|
return this;
|
|
}
|
|
}),
|
|
props = animation.props;
|
|
|
|
propFilter( props, animation.opts.specialEasing );
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
|
|
if ( result ) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
jQuery.map( props, createTween, animation );
|
|
|
|
if ( jQuery.isFunction( animation.opts.start ) ) {
|
|
animation.opts.start.call( elem, animation );
|
|
}
|
|
|
|
jQuery.fx.timer(
|
|
jQuery.extend( tick, {
|
|
elem: elem,
|
|
anim: animation,
|
|
queue: animation.opts.queue
|
|
})
|
|
);
|
|
|
|
// attach callbacks from options
|
|
return animation.progress( animation.opts.progress )
|
|
.done( animation.opts.done, animation.opts.complete )
|
|
.fail( animation.opts.fail )
|
|
.always( animation.opts.always );
|
|
}
|
|
|
|
jQuery.Animation = jQuery.extend( Animation, {
|
|
|
|
tweener: function( props, callback ) {
|
|
if ( jQuery.isFunction( props ) ) {
|
|
callback = props;
|
|
props = [ "*" ];
|
|
} else {
|
|
props = props.split(" ");
|
|
}
|
|
|
|
var prop,
|
|
index = 0,
|
|
length = props.length;
|
|
|
|
for ( ; index < length ; index++ ) {
|
|
prop = props[ index ];
|
|
tweeners[ prop ] = tweeners[ prop ] || [];
|
|
tweeners[ prop ].unshift( callback );
|
|
}
|
|
},
|
|
|
|
prefilter: function( callback, prepend ) {
|
|
if ( prepend ) {
|
|
animationPrefilters.unshift( callback );
|
|
} else {
|
|
animationPrefilters.push( callback );
|
|
}
|
|
}
|
|
});
|
|
|
|
jQuery.speed = function( speed, easing, fn ) {
|
|
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
|
|
complete: fn || !fn && easing ||
|
|
jQuery.isFunction( speed ) && speed,
|
|
duration: speed,
|
|
easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
|
|
};
|
|
|
|
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
|
|
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
|
|
|
|
// normalize opt.queue - true/undefined/null -> "fx"
|
|
if ( opt.queue == null || opt.queue === true ) {
|
|
opt.queue = "fx";
|
|
}
|
|
|
|
// Queueing
|
|
opt.old = opt.complete;
|
|
|
|
opt.complete = function() {
|
|
if ( jQuery.isFunction( opt.old ) ) {
|
|
opt.old.call( this );
|
|
}
|
|
|
|
if ( opt.queue ) {
|
|
jQuery.dequeue( this, opt.queue );
|
|
}
|
|
};
|
|
|
|
return opt;
|
|
};
|
|
|
|
jQuery.fn.extend({
|
|
fadeTo: function( speed, to, easing, callback ) {
|
|
|
|
// show any hidden elements after setting opacity to 0
|
|
return this.filter( isHidden ).css( "opacity", 0 ).show()
|
|
|
|
// animate to the value specified
|
|
.end().animate({ opacity: to }, speed, easing, callback );
|
|
},
|
|
animate: function( prop, speed, easing, callback ) {
|
|
var empty = jQuery.isEmptyObject( prop ),
|
|
optall = jQuery.speed( speed, easing, callback ),
|
|
doAnimation = function() {
|
|
// Operate on a copy of prop so per-property easing won't be lost
|
|
var anim = Animation( this, jQuery.extend( {}, prop ), optall );
|
|
|
|
// Empty animations, or finishing resolves immediately
|
|
if ( empty || data_priv.get( this, "finish" ) ) {
|
|
anim.stop( true );
|
|
}
|
|
};
|
|
doAnimation.finish = doAnimation;
|
|
|
|
return empty || optall.queue === false ?
|
|
this.each( doAnimation ) :
|
|
this.queue( optall.queue, doAnimation );
|
|
},
|
|
stop: function( type, clearQueue, gotoEnd ) {
|
|
var stopQueue = function( hooks ) {
|
|
var stop = hooks.stop;
|
|
delete hooks.stop;
|
|
stop( gotoEnd );
|
|
};
|
|
|
|
if ( typeof type !== "string" ) {
|
|
gotoEnd = clearQueue;
|
|
clearQueue = type;
|
|
type = undefined;
|
|
}
|
|
if ( clearQueue && type !== false ) {
|
|
this.queue( type || "fx", [] );
|
|
}
|
|
|
|
return this.each(function() {
|
|
var dequeue = true,
|
|
index = type != null && type + "queueHooks",
|
|
timers = jQuery.timers,
|
|
data = data_priv.get( this );
|
|
|
|
if ( index ) {
|
|
if ( data[ index ] && data[ index ].stop ) {
|
|
stopQueue( data[ index ] );
|
|
}
|
|
} else {
|
|
for ( index in data ) {
|
|
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
|
|
stopQueue( data[ index ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
for ( index = timers.length; index--; ) {
|
|
if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
|
|
timers[ index ].anim.stop( gotoEnd );
|
|
dequeue = false;
|
|
timers.splice( index, 1 );
|
|
}
|
|
}
|
|
|
|
// start the next in the queue if the last step wasn't forced
|
|
// timers currently will call their complete callbacks, which will dequeue
|
|
// but only if they were gotoEnd
|
|
if ( dequeue || !gotoEnd ) {
|
|
jQuery.dequeue( this, type );
|
|
}
|
|
});
|
|
},
|
|
finish: function( type ) {
|
|
if ( type !== false ) {
|
|
type = type || "fx";
|
|
}
|
|
return this.each(function() {
|
|
var index,
|
|
data = data_priv.get( this ),
|
|
queue = data[ type + "queue" ],
|
|
hooks = data[ type + "queueHooks" ],
|
|
timers = jQuery.timers,
|
|
length = queue ? queue.length : 0;
|
|
|
|
// enable finishing flag on private data
|
|
data.finish = true;
|
|
|
|
// empty the queue first
|
|
jQuery.queue( this, type, [] );
|
|
|
|
if ( hooks && hooks.stop ) {
|
|
hooks.stop.call( this, true );
|
|
}
|
|
|
|
// look for any active animations, and finish them
|
|
for ( index = timers.length; index--; ) {
|
|
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
|
|
timers[ index ].anim.stop( true );
|
|
timers.splice( index, 1 );
|
|
}
|
|
}
|
|
|
|
// look for any animations in the old queue and finish them
|
|
for ( index = 0; index < length; index++ ) {
|
|
if ( queue[ index ] && queue[ index ].finish ) {
|
|
queue[ index ].finish.call( this );
|
|
}
|
|
}
|
|
|
|
// turn off finishing flag
|
|
delete data.finish;
|
|
});
|
|
}
|
|
});
|
|
|
|
jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
|
|
var cssFn = jQuery.fn[ name ];
|
|
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
|
return speed == null || typeof speed === "boolean" ?
|
|
cssFn.apply( this, arguments ) :
|
|
this.animate( genFx( name, true ), speed, easing, callback );
|
|
};
|
|
});
|
|
|
|
// Generate shortcuts for custom animations
|
|
jQuery.each({
|
|
slideDown: genFx("show"),
|
|
slideUp: genFx("hide"),
|
|
slideToggle: genFx("toggle"),
|
|
fadeIn: { opacity: "show" },
|
|
fadeOut: { opacity: "hide" },
|
|
fadeToggle: { opacity: "toggle" }
|
|
}, function( name, props ) {
|
|
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
|
return this.animate( props, speed, easing, callback );
|
|
};
|
|
});
|
|
|
|
jQuery.timers = [];
|
|
jQuery.fx.tick = function() {
|
|
var timer,
|
|
i = 0,
|
|
timers = jQuery.timers;
|
|
|
|
fxNow = jQuery.now();
|
|
|
|
for ( ; i < timers.length; i++ ) {
|
|
timer = timers[ i ];
|
|
// Checks the timer has not already been removed
|
|
if ( !timer() && timers[ i ] === timer ) {
|
|
timers.splice( i--, 1 );
|
|
}
|
|
}
|
|
|
|
if ( !timers.length ) {
|
|
jQuery.fx.stop();
|
|
}
|
|
fxNow = undefined;
|
|
};
|
|
|
|
jQuery.fx.timer = function( timer ) {
|
|
jQuery.timers.push( timer );
|
|
if ( timer() ) {
|
|
jQuery.fx.start();
|
|
} else {
|
|
jQuery.timers.pop();
|
|
}
|
|
};
|
|
|
|
jQuery.fx.interval = 13;
|
|
|
|
jQuery.fx.start = function() {
|
|
if ( !timerId ) {
|
|
timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
|
|
}
|
|
};
|
|
|
|
jQuery.fx.stop = function() {
|
|
clearInterval( timerId );
|
|
timerId = null;
|
|
};
|
|
|
|
jQuery.fx.speeds = {
|
|
slow: 600,
|
|
fast: 200,
|
|
// Default speed
|
|
_default: 400
|
|
};
|
|
|
|
|
|
// Based off of the plugin by Clint Helfers, with permission.
|
|
// http://blindsignals.com/index.php/2009/07/jquery-delay/
|
|
jQuery.fn.delay = function( time, type ) {
|
|
time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
|
|
type = type || "fx";
|
|
|
|
return this.queue( type, function( next, hooks ) {
|
|
var timeout = setTimeout( next, time );
|
|
hooks.stop = function() {
|
|
clearTimeout( timeout );
|
|
};
|
|
});
|
|
};
|
|
|
|
|
|
(function() {
|
|
var input = document.createElement( "input" ),
|
|
select = document.createElement( "select" ),
|
|
opt = select.appendChild( document.createElement( "option" ) );
|
|
|
|
input.type = "checkbox";
|
|
|
|
// Support: iOS 5.1, Android 4.x, Android 2.3
|
|
// Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
|
|
support.checkOn = input.value !== "";
|
|
|
|
// Must access the parent to make an option select properly
|
|
// Support: IE9, IE10
|
|
support.optSelected = opt.selected;
|
|
|
|
// Make sure that the options inside disabled selects aren't marked as disabled
|
|
// (WebKit marks them as disabled)
|
|
select.disabled = true;
|
|
support.optDisabled = !opt.disabled;
|
|
|
|
// Check if an input maintains its value after becoming a radio
|
|
// Support: IE9, IE10
|
|
input = document.createElement( "input" );
|
|
input.value = "t";
|
|
input.type = "radio";
|
|
support.radioValue = input.value === "t";
|
|
})();
|
|
|
|
|
|
var nodeHook, boolHook,
|
|
attrHandle = jQuery.expr.attrHandle;
|
|
|
|
jQuery.fn.extend({
|
|
attr: function( name, value ) {
|
|
return access( this, jQuery.attr, name, value, arguments.length > 1 );
|
|
},
|
|
|
|
removeAttr: function( name ) {
|
|
return this.each(function() {
|
|
jQuery.removeAttr( this, name );
|
|
});
|
|
}
|
|
});
|
|
|
|
jQuery.extend({
|
|
attr: function( elem, name, value ) {
|
|
var hooks, ret,
|
|
nType = elem.nodeType;
|
|
|
|
// don't get/set attributes on text, comment and attribute nodes
|
|
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
|
|
return;
|
|
}
|
|
|
|
// Fallback to prop when attributes are not supported
|
|
if ( typeof elem.getAttribute === strundefined ) {
|
|
return jQuery.prop( elem, name, value );
|
|
}
|
|
|
|
// All attributes are lowercase
|
|
// Grab necessary hook if one is defined
|
|
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
|
|
name = name.toLowerCase();
|
|
hooks = jQuery.attrHooks[ name ] ||
|
|
( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
|
|
}
|
|
|
|
if ( value !== undefined ) {
|
|
|
|
if ( value === null ) {
|
|
jQuery.removeAttr( elem, name );
|
|
|
|
} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
|
|
return ret;
|
|
|
|
} else {
|
|
elem.setAttribute( name, value + "" );
|
|
return value;
|
|
}
|
|
|
|
} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
|
|
return ret;
|
|
|
|
} else {
|
|
ret = jQuery.find.attr( elem, name );
|
|
|
|
// Non-existent attributes return null, we normalize to undefined
|
|
return ret == null ?
|
|
undefined :
|
|
ret;
|
|
}
|
|
},
|
|
|
|
removeAttr: function( elem, value ) {
|
|
var name, propName,
|
|
i = 0,
|
|
attrNames = value && value.match( rnotwhite );
|
|
|
|
if ( attrNames && elem.nodeType === 1 ) {
|
|
while ( (name = attrNames[i++]) ) {
|
|
propName = jQuery.propFix[ name ] || name;
|
|
|
|
// Boolean attributes get special treatment (#10870)
|
|
if ( jQuery.expr.match.bool.test( name ) ) {
|
|
// Set corresponding property to false
|
|
elem[ propName ] = false;
|
|
}
|
|
|
|
elem.removeAttribute( name );
|
|
}
|
|
}
|
|
},
|
|
|
|
attrHooks: {
|
|
type: {
|
|
set: function( elem, value ) {
|
|
if ( !support.radioValue && value === "radio" &&
|
|
jQuery.nodeName( elem, "input" ) ) {
|
|
// Setting the type on a radio button after the value resets the value in IE6-9
|
|
// Reset value to default in case type is set after value during creation
|
|
var val = elem.value;
|
|
elem.setAttribute( "type", value );
|
|
if ( val ) {
|
|
elem.value = val;
|
|
}
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Hooks for boolean attributes
|
|
boolHook = {
|
|
set: function( elem, value, name ) {
|
|
if ( value === false ) {
|
|
// Remove boolean attributes when set to false
|
|
jQuery.removeAttr( elem, name );
|
|
} else {
|
|
elem.setAttribute( name, name );
|
|
}
|
|
return name;
|
|
}
|
|
};
|
|
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
|
|
var getter = attrHandle[ name ] || jQuery.find.attr;
|
|
|
|
attrHandle[ name ] = function( elem, name, isXML ) {
|
|
var ret, handle;
|
|
if ( !isXML ) {
|
|
// Avoid an infinite loop by temporarily removing this function from the getter
|
|
handle = attrHandle[ name ];
|
|
attrHandle[ name ] = ret;
|
|
ret = getter( elem, name, isXML ) != null ?
|
|
name.toLowerCase() :
|
|
null;
|
|
attrHandle[ name ] = handle;
|
|
}
|
|
return ret;
|
|
};
|
|
});
|
|
|
|
|
|
|
|
|
|
var rfocusable = /^(?:input|select|textarea|button)$/i;
|
|
|
|
jQuery.fn.extend({
|
|
prop: function( name, value ) {
|
|
return access( this, jQuery.prop, name, value, arguments.length > 1 );
|
|
},
|
|
|
|
removeProp: function( name ) {
|
|
return this.each(function() {
|
|
delete this[ jQuery.propFix[ name ] || name ];
|
|
});
|
|
}
|
|
});
|
|
|
|
jQuery.extend({
|
|
propFix: {
|
|
"for": "htmlFor",
|
|
"class": "className"
|
|
},
|
|
|
|
prop: function( elem, name, value ) {
|
|
var ret, hooks, notxml,
|
|
nType = elem.nodeType;
|
|
|
|
// don't get/set properties on text, comment and attribute nodes
|
|
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
|
|
return;
|
|
}
|
|
|
|
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
|
|
|
|
if ( notxml ) {
|
|
// Fix name and attach hooks
|
|
name = jQuery.propFix[ name ] || name;
|
|
hooks = jQuery.propHooks[ name ];
|
|
}
|
|
|
|
if ( value !== undefined ) {
|
|
return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
|
|
ret :
|
|
( elem[ name ] = value );
|
|
|
|
} else {
|
|
return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
|
|
ret :
|
|
elem[ name ];
|
|
}
|
|
},
|
|
|
|
propHooks: {
|
|
tabIndex: {
|
|
get: function( elem ) {
|
|
return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
|
|
elem.tabIndex :
|
|
-1;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Support: IE9+
|
|
// Selectedness for an option in an optgroup can be inaccurate
|
|
if ( !support.optSelected ) {
|
|
jQuery.propHooks.selected = {
|
|
get: function( elem ) {
|
|
var parent = elem.parentNode;
|
|
if ( parent && parent.parentNode ) {
|
|
parent.parentNode.selectedIndex;
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
|
|
jQuery.each([
|
|
"tabIndex",
|
|
"readOnly",
|
|
"maxLength",
|
|
"cellSpacing",
|
|
"cellPadding",
|
|
"rowSpan",
|
|
"colSpan",
|
|
"useMap",
|
|
"frameBorder",
|
|
"contentEditable"
|
|
], function() {
|
|
jQuery.propFix[ this.toLowerCase() ] = this;
|
|
});
|
|
|
|
|
|
|
|
|
|
var rclass = /[\t\r\n\f]/g;
|
|
|
|
jQuery.fn.extend({
|
|
addClass: function( value ) {
|
|
var classes, elem, cur, clazz, j, finalValue,
|
|
proceed = typeof value === "string" && value,
|
|
i = 0,
|
|
len = this.length;
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each(function( j ) {
|
|
jQuery( this ).addClass( value.call( this, j, this.className ) );
|
|
});
|
|
}
|
|
|
|
if ( proceed ) {
|
|
// The disjunction here is for better compressibility (see removeClass)
|
|
classes = ( value || "" ).match( rnotwhite ) || [];
|
|
|
|
for ( ; i < len; i++ ) {
|
|
elem = this[ i ];
|
|
cur = elem.nodeType === 1 && ( elem.className ?
|
|
( " " + elem.className + " " ).replace( rclass, " " ) :
|
|
" "
|
|
);
|
|
|
|
if ( cur ) {
|
|
j = 0;
|
|
while ( (clazz = classes[j++]) ) {
|
|
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
|
|
cur += clazz + " ";
|
|
}
|
|
}
|
|
|
|
// only assign if different to avoid unneeded rendering.
|
|
finalValue = jQuery.trim( cur );
|
|
if ( elem.className !== finalValue ) {
|
|
elem.className = finalValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
removeClass: function( value ) {
|
|
var classes, elem, cur, clazz, j, finalValue,
|
|
proceed = arguments.length === 0 || typeof value === "string" && value,
|
|
i = 0,
|
|
len = this.length;
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each(function( j ) {
|
|
jQuery( this ).removeClass( value.call( this, j, this.className ) );
|
|
});
|
|
}
|
|
if ( proceed ) {
|
|
classes = ( value || "" ).match( rnotwhite ) || [];
|
|
|
|
for ( ; i < len; i++ ) {
|
|
elem = this[ i ];
|
|
// This expression is here for better compressibility (see addClass)
|
|
cur = elem.nodeType === 1 && ( elem.className ?
|
|
( " " + elem.className + " " ).replace( rclass, " " ) :
|
|
""
|
|
);
|
|
|
|
if ( cur ) {
|
|
j = 0;
|
|
while ( (clazz = classes[j++]) ) {
|
|
// Remove *all* instances
|
|
while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
|
|
cur = cur.replace( " " + clazz + " ", " " );
|
|
}
|
|
}
|
|
|
|
// only assign if different to avoid unneeded rendering.
|
|
finalValue = value ? jQuery.trim( cur ) : "";
|
|
if ( elem.className !== finalValue ) {
|
|
elem.className = finalValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
toggleClass: function( value, stateVal ) {
|
|
var type = typeof value;
|
|
|
|
if ( typeof stateVal === "boolean" && type === "string" ) {
|
|
return stateVal ? this.addClass( value ) : this.removeClass( value );
|
|
}
|
|
|
|
if ( jQuery.isFunction( value ) ) {
|
|
return this.each(function( i ) {
|
|
jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
|
|
});
|
|
}
|
|
|
|
return this.each(function() {
|
|
if ( type === "string" ) {
|
|
// toggle individual class names
|
|
var className,
|
|
i = 0,
|
|
self = jQuery( this ),
|
|
classNames = value.match( rnotwhite ) || [];
|
|
|
|
while ( (className = classNames[ i++ ]) ) {
|
|
// check each className given, space separated list
|
|
if ( self.hasClass( className ) ) {
|
|
self.removeClass( className );
|
|
} else {
|
|
self.addClass( className );
|
|
}
|
|
}
|
|
|
|
// Toggle whole class name
|
|
} else if ( type === strundefined || type === "boolean" ) {
|
|
if ( this.className ) {
|
|
// store className if set
|
|
data_priv.set( this, "__className__", this.className );
|
|
}
|
|
|
|
// If the element has a class name or if we're passed "false",
|
|
// then remove the whole classname (if there was one, the above saved it).
|
|
// Otherwise bring back whatever was previously saved (if anything),
|
|
// falling back to the empty string if nothing was stored.
|
|
this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
|
|
}
|
|
});
|
|
},
|
|
|
|
hasClass: function( selector ) {
|
|
var className = " " + selector + " ",
|
|
i = 0,
|
|
l = this.length;
|
|
for ( ; i < l; i++ ) {
|
|
if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
var rreturn = /\r/g;
|
|
|
|
jQuery.fn.extend({
|
|
val: function( value ) {
|
|
var hooks, ret, isFunction,
|
|
elem = this[0];
|
|
|
|
if ( !arguments.length ) {
|
|
if ( elem ) {
|
|
hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
|
|
|
|
if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
|
|
return ret;
|
|
}
|
|
|
|
ret = elem.value;
|
|
|
|
return typeof ret === "string" ?
|
|
// handle most common string cases
|
|
ret.replace(rreturn, "") :
|
|
// handle cases where value is null/undef or number
|
|
ret == null ? "" : ret;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
isFunction = jQuery.isFunction( value );
|
|
|
|
return this.each(function( i ) {
|
|
var val;
|
|
|
|
if ( this.nodeType !== 1 ) {
|
|
return;
|
|
}
|
|
|
|
if ( isFunction ) {
|
|
val = value.call( this, i, jQuery( this ).val() );
|
|
} else {
|
|
val = value;
|
|
}
|
|
|
|
// Treat null/undefined as ""; convert numbers to string
|
|
if ( val == null ) {
|
|
val = "";
|
|
|
|
} else if ( typeof val === "number" ) {
|
|
val += "";
|
|
|
|
} else if ( jQuery.isArray( val ) ) {
|
|
val = jQuery.map( val, function( value ) {
|
|
return value == null ? "" : value + "";
|
|
});
|
|
}
|
|
|
|
hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
|
|
|
|
// If set returns undefined, fall back to normal setting
|
|
if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
|
|
this.value = val;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
jQuery.extend({
|
|
valHooks: {
|
|
select: {
|
|
get: function( elem ) {
|
|
var value, option,
|
|
options = elem.options,
|
|
index = elem.selectedIndex,
|
|
one = elem.type === "select-one" || index < 0,
|
|
values = one ? null : [],
|
|
max = one ? index + 1 : options.length,
|
|
i = index < 0 ?
|
|
max :
|
|
one ? index : 0;
|
|
|
|
// Loop through all the selected options
|
|
for ( ; i < max; i++ ) {
|
|
option = options[ i ];
|
|
|
|
// IE6-9 doesn't update selected after form reset (#2551)
|
|
if ( ( option.selected || i === index ) &&
|
|
// Don't return options that are disabled or in a disabled optgroup
|
|
( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
|
|
( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
|
|
|
|
// Get the specific value for the option
|
|
value = jQuery( option ).val();
|
|
|
|
// We don't need an array for one selects
|
|
if ( one ) {
|
|
return value;
|
|
}
|
|
|
|
// Multi-Selects return an array
|
|
values.push( value );
|
|
}
|
|
}
|
|
|
|
return values;
|
|
},
|
|
|
|
set: function( elem, value ) {
|
|
var optionSet, option,
|
|
options = elem.options,
|
|
values = jQuery.makeArray( value ),
|
|
i = options.length;
|
|
|
|
while ( i-- ) {
|
|
option = options[ i ];
|
|
if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) {
|
|
optionSet = true;
|
|
}
|
|
}
|
|
|
|
// force browsers to behave consistently when non-matching value is set
|
|
if ( !optionSet ) {
|
|
elem.selectedIndex = -1;
|
|
}
|
|
return values;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Radios and checkboxes getter/setter
|
|
jQuery.each([ "radio", "checkbox" ], function() {
|
|
jQuery.valHooks[ this ] = {
|
|
set: function( elem, value ) {
|
|
if ( jQuery.isArray( value ) ) {
|
|
return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
|
|
}
|
|
}
|
|
};
|
|
if ( !support.checkOn ) {
|
|
jQuery.valHooks[ this ].get = function( elem ) {
|
|
// Support: Webkit
|
|
// "" is returned instead of "on" if a value isn't specified
|
|
return elem.getAttribute("value") === null ? "on" : elem.value;
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
// Return jQuery for attributes-only inclusion
|
|
|
|
|
|
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
|
|
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
|
|
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
|
|
|
|
// Handle event binding
|
|
jQuery.fn[ name ] = function( data, fn ) {
|
|
return arguments.length > 0 ?
|
|
this.on( name, null, data, fn ) :
|
|
this.trigger( name );
|
|
};
|
|
});
|
|
|
|
jQuery.fn.extend({
|
|
hover: function( fnOver, fnOut ) {
|
|
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
|
|
},
|
|
|
|
bind: function( types, data, fn ) {
|
|
return this.on( types, null, data, fn );
|
|
},
|
|
unbind: function( types, fn ) {
|
|
return this.off( types, null, fn );
|
|
},
|
|
|
|
delegate: function( selector, types, data, fn ) {
|
|
return this.on( types, selector, data, fn );
|
|
},
|
|
undelegate: function( selector, types, fn ) {
|
|
// ( namespace ) or ( selector, types [, fn] )
|
|
return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
|
|
}
|
|
});
|
|
|
|
|
|
var nonce = jQuery.now();
|
|
|
|
var rquery = (/\?/);
|
|
|
|
|
|
|
|
// Support: Android 2.3
|
|
// Workaround failure to string-cast null input
|
|
jQuery.parseJSON = function( data ) {
|
|
return JSON.parse( data + "" );
|
|
};
|
|
|
|
|
|
// Cross-browser xml parsing
|
|
jQuery.parseXML = function( data ) {
|
|
var xml, tmp;
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
|
|
// Support: IE9
|
|
try {
|
|
tmp = new DOMParser();
|
|
xml = tmp.parseFromString( data, "text/xml" );
|
|
} catch ( e ) {
|
|
xml = undefined;
|
|
}
|
|
|
|
if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
|
|
jQuery.error( "Invalid XML: " + data );
|
|
}
|
|
return xml;
|
|
};
|
|
|
|
|
|
var
|
|
// Document location
|
|
ajaxLocParts,
|
|
ajaxLocation,
|
|
|
|
rhash = /#.*$/,
|
|
rts = /([?&])_=[^&]*/,
|
|
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
|
|
// #7653, #8125, #8152: local protocol detection
|
|
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
|
|
rnoContent = /^(?:GET|HEAD)$/,
|
|
rprotocol = /^\/\//,
|
|
rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
|
|
|
|
/* Prefilters
|
|
* 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
|
|
* 2) These are called:
|
|
* - BEFORE asking for a transport
|
|
* - AFTER param serialization (s.data is a string if s.processData is true)
|
|
* 3) key is the dataType
|
|
* 4) the catchall symbol "*" can be used
|
|
* 5) execution will start with transport dataType and THEN continue down to "*" if needed
|
|
*/
|
|
prefilters = {},
|
|
|
|
/* Transports bindings
|
|
* 1) key is the dataType
|
|
* 2) the catchall symbol "*" can be used
|
|
* 3) selection will start with transport dataType and THEN go to "*" if needed
|
|
*/
|
|
transports = {},
|
|
|
|
// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
|
|
allTypes = "*/".concat("*");
|
|
|
|
// #8138, IE may throw an exception when accessing
|
|
// a field from window.location if document.domain has been set
|
|
try {
|
|
ajaxLocation = location.href;
|
|
} catch( e ) {
|
|
// Use the href attribute of an A element
|
|
// since IE will modify it given document.location
|
|
ajaxLocation = document.createElement( "a" );
|
|
ajaxLocation.href = "";
|
|
ajaxLocation = ajaxLocation.href;
|
|
}
|
|
|
|
// Segment location into parts
|
|
ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
|
|
|
|
// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
|
|
function addToPrefiltersOrTransports( structure ) {
|
|
|
|
// dataTypeExpression is optional and defaults to "*"
|
|
return function( dataTypeExpression, func ) {
|
|
|
|
if ( typeof dataTypeExpression !== "string" ) {
|
|
func = dataTypeExpression;
|
|
dataTypeExpression = "*";
|
|
}
|
|
|
|
var dataType,
|
|
i = 0,
|
|
dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
|
|
|
|
if ( jQuery.isFunction( func ) ) {
|
|
// For each dataType in the dataTypeExpression
|
|
while ( (dataType = dataTypes[i++]) ) {
|
|
// Prepend if requested
|
|
if ( dataType[0] === "+" ) {
|
|
dataType = dataType.slice( 1 ) || "*";
|
|
(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
|
|
|
|
// Otherwise append
|
|
} else {
|
|
(structure[ dataType ] = structure[ dataType ] || []).push( func );
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Base inspection function for prefilters and transports
|
|
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
|
|
|
|
var inspected = {},
|
|
seekingTransport = ( structure === transports );
|
|
|
|
function inspect( dataType ) {
|
|
var selected;
|
|
inspected[ dataType ] = true;
|
|
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
|
|
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
|
|
if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
|
|
options.dataTypes.unshift( dataTypeOrTransport );
|
|
inspect( dataTypeOrTransport );
|
|
return false;
|
|
} else if ( seekingTransport ) {
|
|
return !( selected = dataTypeOrTransport );
|
|
}
|
|
});
|
|
return selected;
|
|
}
|
|
|
|
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
|
|
}
|
|
|
|
// A special extend for ajax options
|
|
// that takes "flat" options (not to be deep extended)
|
|
// Fixes #9887
|
|
function ajaxExtend( target, src ) {
|
|
var key, deep,
|
|
flatOptions = jQuery.ajaxSettings.flatOptions || {};
|
|
|
|
for ( key in src ) {
|
|
if ( src[ key ] !== undefined ) {
|
|
( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
|
|
}
|
|
}
|
|
if ( deep ) {
|
|
jQuery.extend( true, target, deep );
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
/* Handles responses to an ajax request:
|
|
* - finds the right dataType (mediates between content-type and expected dataType)
|
|
* - returns the corresponding response
|
|
*/
|
|
function ajaxHandleResponses( s, jqXHR, responses ) {
|
|
|
|
var ct, type, finalDataType, firstDataType,
|
|
contents = s.contents,
|
|
dataTypes = s.dataTypes;
|
|
|
|
// Remove auto dataType and get content-type in the process
|
|
while ( dataTypes[ 0 ] === "*" ) {
|
|
dataTypes.shift();
|
|
if ( ct === undefined ) {
|
|
ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
|
|
}
|
|
}
|
|
|
|
// Check if we're dealing with a known content-type
|
|
if ( ct ) {
|
|
for ( type in contents ) {
|
|
if ( contents[ type ] && contents[ type ].test( ct ) ) {
|
|
dataTypes.unshift( type );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if we have a response for the expected dataType
|
|
if ( dataTypes[ 0 ] in responses ) {
|
|
finalDataType = dataTypes[ 0 ];
|
|
} else {
|
|
// Try convertible dataTypes
|
|
for ( type in responses ) {
|
|
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
|
|
finalDataType = type;
|
|
break;
|
|
}
|
|
if ( !firstDataType ) {
|
|
firstDataType = type;
|
|
}
|
|
}
|
|
// Or just use first one
|
|
finalDataType = finalDataType || firstDataType;
|
|
}
|
|
|
|
// If we found a dataType
|
|
// We add the dataType to the list if needed
|
|
// and return the corresponding response
|
|
if ( finalDataType ) {
|
|
if ( finalDataType !== dataTypes[ 0 ] ) {
|
|
dataTypes.unshift( finalDataType );
|
|
}
|
|
return responses[ finalDataType ];
|
|
}
|
|
}
|
|
|
|
/* Chain conversions given the request and the original response
|
|
* Also sets the responseXXX fields on the jqXHR instance
|
|
*/
|
|
function ajaxConvert( s, response, jqXHR, isSuccess ) {
|
|
var conv2, current, conv, tmp, prev,
|
|
converters = {},
|
|
// Work with a copy of dataTypes in case we need to modify it for conversion
|
|
dataTypes = s.dataTypes.slice();
|
|
|
|
// Create converters map with lowercased keys
|
|
if ( dataTypes[ 1 ] ) {
|
|
for ( conv in s.converters ) {
|
|
converters[ conv.toLowerCase() ] = s.converters[ conv ];
|
|
}
|
|
}
|
|
|
|
current = dataTypes.shift();
|
|
|
|
// Convert to each sequential dataType
|
|
while ( current ) {
|
|
|
|
if ( s.responseFields[ current ] ) {
|
|
jqXHR[ s.responseFields[ current ] ] = response;
|
|
}
|
|
|
|
// Apply the dataFilter if provided
|
|
if ( !prev && isSuccess && s.dataFilter ) {
|
|
response = s.dataFilter( response, s.dataType );
|
|
}
|
|
|
|
prev = current;
|
|
current = dataTypes.shift();
|
|
|
|
if ( current ) {
|
|
|
|
// There's only work to do if current dataType is non-auto
|
|
if ( current === "*" ) {
|
|
|
|
current = prev;
|
|
|
|
// Convert response if prev dataType is non-auto and differs from current
|
|
} else if ( prev !== "*" && prev !== current ) {
|
|
|
|
// Seek a direct converter
|
|
conv = converters[ prev + " " + current ] || converters[ "* " + current ];
|
|
|
|
// If none found, seek a pair
|
|
if ( !conv ) {
|
|
for ( conv2 in converters ) {
|
|
|
|
// If conv2 outputs current
|
|
tmp = conv2.split( " " );
|
|
if ( tmp[ 1 ] === current ) {
|
|
|
|
// If prev can be converted to accepted input
|
|
conv = converters[ prev + " " + tmp[ 0 ] ] ||
|
|
converters[ "* " + tmp[ 0 ] ];
|
|
if ( conv ) {
|
|
// Condense equivalence converters
|
|
if ( conv === true ) {
|
|
conv = converters[ conv2 ];
|
|
|
|
// Otherwise, insert the intermediate dataType
|
|
} else if ( converters[ conv2 ] !== true ) {
|
|
current = tmp[ 0 ];
|
|
dataTypes.unshift( tmp[ 1 ] );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply converter (if not an equivalence)
|
|
if ( conv !== true ) {
|
|
|
|
// Unless errors are allowed to bubble, catch and return them
|
|
if ( conv && s[ "throws" ] ) {
|
|
response = conv( response );
|
|
} else {
|
|
try {
|
|
response = conv( response );
|
|
} catch ( e ) {
|
|
return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return { state: "success", data: response };
|
|
}
|
|
|
|
jQuery.extend({
|
|
|
|
// Counter for holding the number of active queries
|
|
active: 0,
|
|
|
|
// Last-Modified header cache for next request
|
|
lastModified: {},
|
|
etag: {},
|
|
|
|
ajaxSettings: {
|
|
url: ajaxLocation,
|
|
type: "GET",
|
|
isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
|
|
global: true,
|
|
processData: true,
|
|
async: true,
|
|
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
|
|
/*
|
|
timeout: 0,
|
|
data: null,
|
|
dataType: null,
|
|
username: null,
|
|
password: null,
|
|
cache: null,
|
|
throws: false,
|
|
traditional: false,
|
|
headers: {},
|
|
*/
|
|
|
|
accepts: {
|
|
"*": allTypes,
|
|
text: "text/plain",
|
|
html: "text/html",
|
|
xml: "application/xml, text/xml",
|
|
json: "application/json, text/javascript"
|
|
},
|
|
|
|
contents: {
|
|
xml: /xml/,
|
|
html: /html/,
|
|
json: /json/
|
|
},
|
|
|
|
responseFields: {
|
|
xml: "responseXML",
|
|
text: "responseText",
|
|
json: "responseJSON"
|
|
},
|
|
|
|
// Data converters
|
|
// Keys separate source (or catchall "*") and destination types with a single space
|
|
converters: {
|
|
|
|
// Convert anything to text
|
|
"* text": String,
|
|
|
|
// Text to html (true = no transformation)
|
|
"text html": true,
|
|
|
|
// Evaluate text as a json expression
|
|
"text json": jQuery.parseJSON,
|
|
|
|
// Parse text as xml
|
|
"text xml": jQuery.parseXML
|
|
},
|
|
|
|
// For options that shouldn't be deep extended:
|
|
// you can add your own custom options here if
|
|
// and when you create one that shouldn't be
|
|
// deep extended (see ajaxExtend)
|
|
flatOptions: {
|
|
url: true,
|
|
context: true
|
|
}
|
|
},
|
|
|
|
// Creates a full fledged settings object into target
|
|
// with both ajaxSettings and settings fields.
|
|
// If target is omitted, writes into ajaxSettings.
|
|
ajaxSetup: function( target, settings ) {
|
|
return settings ?
|
|
|
|
// Building a settings object
|
|
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
|
|
|
|
// Extending ajaxSettings
|
|
ajaxExtend( jQuery.ajaxSettings, target );
|
|
},
|
|
|
|
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
|
|
ajaxTransport: addToPrefiltersOrTransports( transports ),
|
|
|
|
// Main method
|
|
ajax: function( url, options ) {
|
|
|
|
// If url is an object, simulate pre-1.5 signature
|
|
if ( typeof url === "object" ) {
|
|
options = url;
|
|
url = undefined;
|
|
}
|
|
|
|
// Force options to be an object
|
|
options = options || {};
|
|
|
|
var transport,
|
|
// URL without anti-cache param
|
|
cacheURL,
|
|
// Response headers
|
|
responseHeadersString,
|
|
responseHeaders,
|
|
// timeout handle
|
|
timeoutTimer,
|
|
// Cross-domain detection vars
|
|
parts,
|
|
// To know if global events are to be dispatched
|
|
fireGlobals,
|
|
// Loop variable
|
|
i,
|
|
// Create the final options object
|
|
s = jQuery.ajaxSetup( {}, options ),
|
|
// Callbacks context
|
|
callbackContext = s.context || s,
|
|
// Context for global events is callbackContext if it is a DOM node or jQuery collection
|
|
globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
|
|
jQuery( callbackContext ) :
|
|
jQuery.event,
|
|
// Deferreds
|
|
deferred = jQuery.Deferred(),
|
|
completeDeferred = jQuery.Callbacks("once memory"),
|
|
// Status-dependent callbacks
|
|
statusCode = s.statusCode || {},
|
|
// Headers (they are sent all at once)
|
|
requestHeaders = {},
|
|
requestHeadersNames = {},
|
|
// The jqXHR state
|
|
state = 0,
|
|
// Default abort message
|
|
strAbort = "canceled",
|
|
// Fake xhr
|
|
jqXHR = {
|
|
readyState: 0,
|
|
|
|
// Builds headers hashtable if needed
|
|
getResponseHeader: function( key ) {
|
|
var match;
|
|
if ( state === 2 ) {
|
|
if ( !responseHeaders ) {
|
|
responseHeaders = {};
|
|
while ( (match = rheaders.exec( responseHeadersString )) ) {
|
|
responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
|
|
}
|
|
}
|
|
match = responseHeaders[ key.toLowerCase() ];
|
|
}
|
|
return match == null ? null : match;
|
|
},
|
|
|
|
// Raw string
|
|
getAllResponseHeaders: function() {
|
|
return state === 2 ? responseHeadersString : null;
|
|
},
|
|
|
|
// Caches the header
|
|
setRequestHeader: function( name, value ) {
|
|
var lname = name.toLowerCase();
|
|
if ( !state ) {
|
|
name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
|
|
requestHeaders[ name ] = value;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Overrides response content-type header
|
|
overrideMimeType: function( type ) {
|
|
if ( !state ) {
|
|
s.mimeType = type;
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Status-dependent callbacks
|
|
statusCode: function( map ) {
|
|
var code;
|
|
if ( map ) {
|
|
if ( state < 2 ) {
|
|
for ( code in map ) {
|
|
// Lazy-add the new callback in a way that preserves old ones
|
|
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
|
|
}
|
|
} else {
|
|
// Execute the appropriate callbacks
|
|
jqXHR.always( map[ jqXHR.status ] );
|
|
}
|
|
}
|
|
return this;
|
|
},
|
|
|
|
// Cancel the request
|
|
abort: function( statusText ) {
|
|
var finalText = statusText || strAbort;
|
|
if ( transport ) {
|
|
transport.abort( finalText );
|
|
}
|
|
done( 0, finalText );
|
|
return this;
|
|
}
|
|
};
|
|
|
|
// Attach deferreds
|
|
deferred.promise( jqXHR ).complete = completeDeferred.add;
|
|
jqXHR.success = jqXHR.done;
|
|
jqXHR.error = jqXHR.fail;
|
|
|
|
// Remove hash character (#7531: and string promotion)
|
|
// Add protocol if not provided (prefilters might expect it)
|
|
// Handle falsy url in the settings object (#10093: consistency with old signature)
|
|
// We also use the url parameter if available
|
|
s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
|
|
.replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
|
|
|
|
// Alias method option to type as per ticket #12004
|
|
s.type = options.method || options.type || s.method || s.type;
|
|
|
|
// Extract dataTypes list
|
|
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
|
|
|
|
// A cross-domain request is in order when we have a protocol:host:port mismatch
|
|
if ( s.crossDomain == null ) {
|
|
parts = rurl.exec( s.url.toLowerCase() );
|
|
s.crossDomain = !!( parts &&
|
|
( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
|
|
( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
|
|
( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
|
|
);
|
|
}
|
|
|
|
// Convert data if not already a string
|
|
if ( s.data && s.processData && typeof s.data !== "string" ) {
|
|
s.data = jQuery.param( s.data, s.traditional );
|
|
}
|
|
|
|
// Apply prefilters
|
|
inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
|
|
|
|
// If request was aborted inside a prefilter, stop there
|
|
if ( state === 2 ) {
|
|
return jqXHR;
|
|
}
|
|
|
|
// We can fire global events as of now if asked to
|
|
fireGlobals = s.global;
|
|
|
|
// Watch for a new set of requests
|
|
if ( fireGlobals && jQuery.active++ === 0 ) {
|
|
jQuery.event.trigger("ajaxStart");
|
|
}
|
|
|
|
// Uppercase the type
|
|
s.type = s.type.toUpperCase();
|
|
|
|
// Determine if request has content
|
|
s.hasContent = !rnoContent.test( s.type );
|
|
|
|
// Save the URL in case we're toying with the If-Modified-Since
|
|
// and/or If-None-Match header later on
|
|
cacheURL = s.url;
|
|
|
|
// More options handling for requests with no content
|
|
if ( !s.hasContent ) {
|
|
|
|
// If data is available, append data to url
|
|
if ( s.data ) {
|
|
cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
|
|
// #9682: remove data so that it's not used in an eventual retry
|
|
delete s.data;
|
|
}
|
|
|
|
// Add anti-cache in url if needed
|
|
if ( s.cache === false ) {
|
|
s.url = rts.test( cacheURL ) ?
|
|
|
|
// If there is already a '_' parameter, set its value
|
|
cacheURL.replace( rts, "$1_=" + nonce++ ) :
|
|
|
|
// Otherwise add one to the end
|
|
cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
|
|
}
|
|
}
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
if ( jQuery.lastModified[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
|
|
}
|
|
if ( jQuery.etag[ cacheURL ] ) {
|
|
jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
|
|
}
|
|
}
|
|
|
|
// Set the correct header, if data is being sent
|
|
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
|
|
jqXHR.setRequestHeader( "Content-Type", s.contentType );
|
|
}
|
|
|
|
// Set the Accepts header for the server, depending on the dataType
|
|
jqXHR.setRequestHeader(
|
|
"Accept",
|
|
s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
|
|
s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
|
|
s.accepts[ "*" ]
|
|
);
|
|
|
|
// Check for headers option
|
|
for ( i in s.headers ) {
|
|
jqXHR.setRequestHeader( i, s.headers[ i ] );
|
|
}
|
|
|
|
// Allow custom headers/mimetypes and early abort
|
|
if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
|
|
// Abort if not done already and return
|
|
return jqXHR.abort();
|
|
}
|
|
|
|
// aborting is no longer a cancellation
|
|
strAbort = "abort";
|
|
|
|
// Install callbacks on deferreds
|
|
for ( i in { success: 1, error: 1, complete: 1 } ) {
|
|
jqXHR[ i ]( s[ i ] );
|
|
}
|
|
|
|
// Get transport
|
|
transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
|
|
|
|
// If no transport, we auto-abort
|
|
if ( !transport ) {
|
|
done( -1, "No Transport" );
|
|
} else {
|
|
jqXHR.readyState = 1;
|
|
|
|
// Send global event
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
|
|
}
|
|
// Timeout
|
|
if ( s.async && s.timeout > 0 ) {
|
|
timeoutTimer = setTimeout(function() {
|
|
jqXHR.abort("timeout");
|
|
}, s.timeout );
|
|
}
|
|
|
|
try {
|
|
state = 1;
|
|
transport.send( requestHeaders, done );
|
|
} catch ( e ) {
|
|
// Propagate exception as error if not done
|
|
if ( state < 2 ) {
|
|
done( -1, e );
|
|
// Simply rethrow otherwise
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Callback for when everything is done
|
|
function done( status, nativeStatusText, responses, headers ) {
|
|
var isSuccess, success, error, response, modified,
|
|
statusText = nativeStatusText;
|
|
|
|
// Called once
|
|
if ( state === 2 ) {
|
|
return;
|
|
}
|
|
|
|
// State is "done" now
|
|
state = 2;
|
|
|
|
// Clear timeout if it exists
|
|
if ( timeoutTimer ) {
|
|
clearTimeout( timeoutTimer );
|
|
}
|
|
|
|
// Dereference transport for early garbage collection
|
|
// (no matter how long the jqXHR object will be used)
|
|
transport = undefined;
|
|
|
|
// Cache response headers
|
|
responseHeadersString = headers || "";
|
|
|
|
// Set readyState
|
|
jqXHR.readyState = status > 0 ? 4 : 0;
|
|
|
|
// Determine if successful
|
|
isSuccess = status >= 200 && status < 300 || status === 304;
|
|
|
|
// Get response data
|
|
if ( responses ) {
|
|
response = ajaxHandleResponses( s, jqXHR, responses );
|
|
}
|
|
|
|
// Convert no matter what (that way responseXXX fields are always set)
|
|
response = ajaxConvert( s, response, jqXHR, isSuccess );
|
|
|
|
// If successful, handle type chaining
|
|
if ( isSuccess ) {
|
|
|
|
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
|
|
if ( s.ifModified ) {
|
|
modified = jqXHR.getResponseHeader("Last-Modified");
|
|
if ( modified ) {
|
|
jQuery.lastModified[ cacheURL ] = modified;
|
|
}
|
|
modified = jqXHR.getResponseHeader("etag");
|
|
if ( modified ) {
|
|
jQuery.etag[ cacheURL ] = modified;
|
|
}
|
|
}
|
|
|
|
// if no content
|
|
if ( status === 204 || s.type === "HEAD" ) {
|
|
statusText = "nocontent";
|
|
|
|
// if not modified
|
|
} else if ( status === 304 ) {
|
|
statusText = "notmodified";
|
|
|
|
// If we have data, let's convert it
|
|
} else {
|
|
statusText = response.state;
|
|
success = response.data;
|
|
error = response.error;
|
|
isSuccess = !error;
|
|
}
|
|
} else {
|
|
// We extract error from statusText
|
|
// then normalize statusText and status for non-aborts
|
|
error = statusText;
|
|
if ( status || !statusText ) {
|
|
statusText = "error";
|
|
if ( status < 0 ) {
|
|
status = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set data for the fake xhr object
|
|
jqXHR.status = status;
|
|
jqXHR.statusText = ( nativeStatusText || statusText ) + "";
|
|
|
|
// Success/Error
|
|
if ( isSuccess ) {
|
|
deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
|
|
} else {
|
|
deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
|
|
}
|
|
|
|
// Status-dependent callbacks
|
|
jqXHR.statusCode( statusCode );
|
|
statusCode = undefined;
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
|
|
[ jqXHR, s, isSuccess ? success : error ] );
|
|
}
|
|
|
|
// Complete
|
|
completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
|
|
|
|
if ( fireGlobals ) {
|
|
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
|
|
// Handle the global AJAX counter
|
|
if ( !( --jQuery.active ) ) {
|
|
jQuery.event.trigger("ajaxStop");
|
|
}
|
|
}
|
|
}
|
|
|
|
return jqXHR;
|
|
},
|
|
|
|
getJSON: function( url, data, callback ) {
|
|
return jQuery.get( url, data, callback, "json" );
|
|
},
|
|
|
|
getScript: function( url, callback ) {
|
|
return jQuery.get( url, undefined, callback, "script" );
|
|
}
|
|
});
|
|
|
|
jQuery.each( [ "get", "post" ], function( i, method ) {
|
|
jQuery[ method ] = function( url, data, callback, type ) {
|
|
// shift arguments if data argument was omitted
|
|
if ( jQuery.isFunction( data ) ) {
|
|
type = type || callback;
|
|
callback = data;
|
|
data = undefined;
|
|
}
|
|
|
|
return jQuery.ajax({
|
|
url: url,
|
|
type: method,
|
|
dataType: type,
|
|
data: data,
|
|
success: callback
|
|
});
|
|
};
|
|
});
|
|
|
|
// Attach a bunch of functions for handling common AJAX events
|
|
jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
|
|
jQuery.fn[ type ] = function( fn ) {
|
|
return this.on( type, fn );
|
|
};
|
|
});
|
|
|
|
|
|
jQuery._evalUrl = function( url ) {
|
|
return jQuery.ajax({
|
|
url: url,
|
|
type: "GET",
|
|
dataType: "script",
|
|
async: false,
|
|
global: false,
|
|
"throws": true
|
|
});
|
|
};
|
|
|
|
|
|
jQuery.fn.extend({
|
|
wrapAll: function( html ) {
|
|
var wrap;
|
|
|
|
if ( jQuery.isFunction( html ) ) {
|
|
return this.each(function( i ) {
|
|
jQuery( this ).wrapAll( html.call(this, i) );
|
|
});
|
|
}
|
|
|
|
if ( this[ 0 ] ) {
|
|
|
|
// The elements to wrap the target around
|
|
wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
|
|
|
|
if ( this[ 0 ].parentNode ) {
|
|
wrap.insertBefore( this[ 0 ] );
|
|
}
|
|
|
|
wrap.map(function() {
|
|
var elem = this;
|
|
|
|
while ( elem.firstElementChild ) {
|
|
elem = elem.firstElementChild;
|
|
}
|
|
|
|
return elem;
|
|
}).append( this );
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
wrapInner: function( html ) {
|
|
if ( jQuery.isFunction( html ) ) {
|
|
return this.each(function( i ) {
|
|
jQuery( this ).wrapInner( html.call(this, i) );
|
|
});
|
|
}
|
|
|
|
return this.each(function() {
|
|
var self = jQuery( this ),
|
|
contents = self.contents();
|
|
|
|
if ( contents.length ) {
|
|
contents.wrapAll( html );
|
|
|
|
} else {
|
|
self.append( html );
|
|
}
|
|
});
|
|
},
|
|
|
|
wrap: function( html ) {
|
|
var isFunction = jQuery.isFunction( html );
|
|
|
|
return this.each(function( i ) {
|
|
jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
|
|
});
|
|
},
|
|
|
|
unwrap: function() {
|
|
return this.parent().each(function() {
|
|
if ( !jQuery.nodeName( this, "body" ) ) {
|
|
jQuery( this ).replaceWith( this.childNodes );
|
|
}
|
|
}).end();
|
|
}
|
|
});
|
|
|
|
|
|
jQuery.expr.filters.hidden = function( elem ) {
|
|
// Support: Opera <= 12.12
|
|
// Opera reports offsetWidths and offsetHeights less than zero on some elements
|
|
return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
|
|
};
|
|
jQuery.expr.filters.visible = function( elem ) {
|
|
return !jQuery.expr.filters.hidden( elem );
|
|
};
|
|
|
|
|
|
|
|
|
|
var r20 = /%20/g,
|
|
rbracket = /\[\]$/,
|
|
rCRLF = /\r?\n/g,
|
|
rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
|
|
rsubmittable = /^(?:input|select|textarea|keygen)/i;
|
|
|
|
function buildParams( prefix, obj, traditional, add ) {
|
|
var name;
|
|
|
|
if ( jQuery.isArray( obj ) ) {
|
|
// Serialize array item.
|
|
jQuery.each( obj, function( i, v ) {
|
|
if ( traditional || rbracket.test( prefix ) ) {
|
|
// Treat each array item as a scalar.
|
|
add( prefix, v );
|
|
|
|
} else {
|
|
// Item is non-scalar (array or object), encode its numeric index.
|
|
buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
|
|
}
|
|
});
|
|
|
|
} else if ( !traditional && jQuery.type( obj ) === "object" ) {
|
|
// Serialize object item.
|
|
for ( name in obj ) {
|
|
buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
|
|
}
|
|
|
|
} else {
|
|
// Serialize scalar item.
|
|
add( prefix, obj );
|
|
}
|
|
}
|
|
|
|
// Serialize an array of form elements or a set of
|
|
// key/values into a query string
|
|
jQuery.param = function( a, traditional ) {
|
|
var prefix,
|
|
s = [],
|
|
add = function( key, value ) {
|
|
// If value is a function, invoke it and return its value
|
|
value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
|
|
s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
|
|
};
|
|
|
|
// Set traditional to true for jQuery <= 1.3.2 behavior.
|
|
if ( traditional === undefined ) {
|
|
traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
|
|
}
|
|
|
|
// If an array was passed in, assume that it is an array of form elements.
|
|
if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
|
|
// Serialize the form elements
|
|
jQuery.each( a, function() {
|
|
add( this.name, this.value );
|
|
});
|
|
|
|
} else {
|
|
// If traditional, encode the "old" way (the way 1.3.2 or older
|
|
// did it), otherwise encode params recursively.
|
|
for ( prefix in a ) {
|
|
buildParams( prefix, a[ prefix ], traditional, add );
|
|
}
|
|
}
|
|
|
|
// Return the resulting serialization
|
|
return s.join( "&" ).replace( r20, "+" );
|
|
};
|
|
|
|
jQuery.fn.extend({
|
|
serialize: function() {
|
|
return jQuery.param( this.serializeArray() );
|
|
},
|
|
serializeArray: function() {
|
|
return this.map(function() {
|
|
// Can add propHook for "elements" to filter or add form elements
|
|
var elements = jQuery.prop( this, "elements" );
|
|
return elements ? jQuery.makeArray( elements ) : this;
|
|
})
|
|
.filter(function() {
|
|
var type = this.type;
|
|
|
|
// Use .is( ":disabled" ) so that fieldset[disabled] works
|
|
return this.name && !jQuery( this ).is( ":disabled" ) &&
|
|
rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
|
|
( this.checked || !rcheckableType.test( type ) );
|
|
})
|
|
.map(function( i, elem ) {
|
|
var val = jQuery( this ).val();
|
|
|
|
return val == null ?
|
|
null :
|
|
jQuery.isArray( val ) ?
|
|
jQuery.map( val, function( val ) {
|
|
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
}) :
|
|
{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
|
}).get();
|
|
}
|
|
});
|
|
|
|
|
|
jQuery.ajaxSettings.xhr = function() {
|
|
try {
|
|
return new XMLHttpRequest();
|
|
} catch( e ) {}
|
|
};
|
|
|
|
var xhrId = 0,
|
|
xhrCallbacks = {},
|
|
xhrSuccessStatus = {
|
|
// file protocol always yields status code 0, assume 200
|
|
0: 200,
|
|
// Support: IE9
|
|
// #1450: sometimes IE returns 1223 when it should be 204
|
|
1223: 204
|
|
},
|
|
xhrSupported = jQuery.ajaxSettings.xhr();
|
|
|
|
// Support: IE9
|
|
// Open requests must be manually aborted on unload (#5280)
|
|
if ( window.ActiveXObject ) {
|
|
jQuery( window ).on( "unload", function() {
|
|
for ( var key in xhrCallbacks ) {
|
|
xhrCallbacks[ key ]();
|
|
}
|
|
});
|
|
}
|
|
|
|
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
|
|
support.ajax = xhrSupported = !!xhrSupported;
|
|
|
|
jQuery.ajaxTransport(function( options ) {
|
|
var callback;
|
|
|
|
// Cross domain only allowed if supported through XMLHttpRequest
|
|
if ( support.cors || xhrSupported && !options.crossDomain ) {
|
|
return {
|
|
send: function( headers, complete ) {
|
|
var i,
|
|
xhr = options.xhr(),
|
|
id = ++xhrId;
|
|
|
|
xhr.open( options.type, options.url, options.async, options.username, options.password );
|
|
|
|
// Apply custom fields if provided
|
|
if ( options.xhrFields ) {
|
|
for ( i in options.xhrFields ) {
|
|
xhr[ i ] = options.xhrFields[ i ];
|
|
}
|
|
}
|
|
|
|
// Override mime type if needed
|
|
if ( options.mimeType && xhr.overrideMimeType ) {
|
|
xhr.overrideMimeType( options.mimeType );
|
|
}
|
|
|
|
// X-Requested-With header
|
|
// For cross-domain requests, seeing as conditions for a preflight are
|
|
// akin to a jigsaw puzzle, we simply never set it to be sure.
|
|
// (it can always be set on a per-request basis or even using ajaxSetup)
|
|
// For same-domain requests, won't change header if already provided.
|
|
if ( !options.crossDomain && !headers["X-Requested-With"] ) {
|
|
headers["X-Requested-With"] = "XMLHttpRequest";
|
|
}
|
|
|
|
// Set headers
|
|
for ( i in headers ) {
|
|
xhr.setRequestHeader( i, headers[ i ] );
|
|
}
|
|
|
|
// Callback
|
|
callback = function( type ) {
|
|
return function() {
|
|
if ( callback ) {
|
|
delete xhrCallbacks[ id ];
|
|
callback = xhr.onload = xhr.onerror = null;
|
|
|
|
if ( type === "abort" ) {
|
|
xhr.abort();
|
|
} else if ( type === "error" ) {
|
|
complete(
|
|
// file: protocol always yields status 0; see #8605, #14207
|
|
xhr.status,
|
|
xhr.statusText
|
|
);
|
|
} else {
|
|
complete(
|
|
xhrSuccessStatus[ xhr.status ] || xhr.status,
|
|
xhr.statusText,
|
|
// Support: IE9
|
|
// Accessing binary-data responseText throws an exception
|
|
// (#11426)
|
|
typeof xhr.responseText === "string" ? {
|
|
text: xhr.responseText
|
|
} : undefined,
|
|
xhr.getAllResponseHeaders()
|
|
);
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
// Listen to events
|
|
xhr.onload = callback();
|
|
xhr.onerror = callback("error");
|
|
|
|
// Create the abort callback
|
|
callback = xhrCallbacks[ id ] = callback("abort");
|
|
|
|
// Do send the request
|
|
// This may raise an exception which is actually
|
|
// handled in jQuery.ajax (so no try/catch here)
|
|
xhr.send( options.hasContent && options.data || null );
|
|
},
|
|
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
// Install script dataType
|
|
jQuery.ajaxSetup({
|
|
accepts: {
|
|
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
|
|
},
|
|
contents: {
|
|
script: /(?:java|ecma)script/
|
|
},
|
|
converters: {
|
|
"text script": function( text ) {
|
|
jQuery.globalEval( text );
|
|
return text;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Handle cache's special case and crossDomain
|
|
jQuery.ajaxPrefilter( "script", function( s ) {
|
|
if ( s.cache === undefined ) {
|
|
s.cache = false;
|
|
}
|
|
if ( s.crossDomain ) {
|
|
s.type = "GET";
|
|
}
|
|
});
|
|
|
|
// Bind script tag hack transport
|
|
jQuery.ajaxTransport( "script", function( s ) {
|
|
// This transport only deals with cross domain requests
|
|
if ( s.crossDomain ) {
|
|
var script, callback;
|
|
return {
|
|
send: function( _, complete ) {
|
|
script = jQuery("<script>").prop({
|
|
async: true,
|
|
charset: s.scriptCharset,
|
|
src: s.url
|
|
}).on(
|
|
"load error",
|
|
callback = function( evt ) {
|
|
script.remove();
|
|
callback = null;
|
|
if ( evt ) {
|
|
complete( evt.type === "error" ? 404 : 200, evt.type );
|
|
}
|
|
}
|
|
);
|
|
document.head.appendChild( script[ 0 ] );
|
|
},
|
|
abort: function() {
|
|
if ( callback ) {
|
|
callback();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
var oldCallbacks = [],
|
|
rjsonp = /(=)\?(?=&|$)|\?\?/;
|
|
|
|
// Default jsonp settings
|
|
jQuery.ajaxSetup({
|
|
jsonp: "callback",
|
|
jsonpCallback: function() {
|
|
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
|
|
this[ callback ] = true;
|
|
return callback;
|
|
}
|
|
});
|
|
|
|
// Detect, normalize options and install callbacks for jsonp requests
|
|
jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
|
|
|
|
var callbackName, overwritten, responseContainer,
|
|
jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
|
|
"url" :
|
|
typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
|
|
);
|
|
|
|
// Handle iff the expected data type is "jsonp" or we have a parameter to set
|
|
if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
|
|
|
|
// Get callback name, remembering preexisting value associated with it
|
|
callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
|
|
s.jsonpCallback() :
|
|
s.jsonpCallback;
|
|
|
|
// Insert callback into url or form data
|
|
if ( jsonProp ) {
|
|
s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
|
|
} else if ( s.jsonp !== false ) {
|
|
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
|
|
}
|
|
|
|
// Use data converter to retrieve json after script execution
|
|
s.converters["script json"] = function() {
|
|
if ( !responseContainer ) {
|
|
jQuery.error( callbackName + " was not called" );
|
|
}
|
|
return responseContainer[ 0 ];
|
|
};
|
|
|
|
// force json dataType
|
|
s.dataTypes[ 0 ] = "json";
|
|
|
|
// Install callback
|
|
overwritten = window[ callbackName ];
|
|
window[ callbackName ] = function() {
|
|
responseContainer = arguments;
|
|
};
|
|
|
|
// Clean-up function (fires after converters)
|
|
jqXHR.always(function() {
|
|
// Restore preexisting value
|
|
window[ callbackName ] = overwritten;
|
|
|
|
// Save back as free
|
|
if ( s[ callbackName ] ) {
|
|
// make sure that re-using the options doesn't screw things around
|
|
s.jsonpCallback = originalSettings.jsonpCallback;
|
|
|
|
// save the callback name for future use
|
|
oldCallbacks.push( callbackName );
|
|
}
|
|
|
|
// Call if it was a function and we have a response
|
|
if ( responseContainer && jQuery.isFunction( overwritten ) ) {
|
|
overwritten( responseContainer[ 0 ] );
|
|
}
|
|
|
|
responseContainer = overwritten = undefined;
|
|
});
|
|
|
|
// Delegate to script
|
|
return "script";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
// data: string of html
|
|
// context (optional): If specified, the fragment will be created in this context, defaults to document
|
|
// keepScripts (optional): If true, will include scripts passed in the html string
|
|
jQuery.parseHTML = function( data, context, keepScripts ) {
|
|
if ( !data || typeof data !== "string" ) {
|
|
return null;
|
|
}
|
|
if ( typeof context === "boolean" ) {
|
|
keepScripts = context;
|
|
context = false;
|
|
}
|
|
context = context || document;
|
|
|
|
var parsed = rsingleTag.exec( data ),
|
|
scripts = !keepScripts && [];
|
|
|
|
// Single tag
|
|
if ( parsed ) {
|
|
return [ context.createElement( parsed[1] ) ];
|
|
}
|
|
|
|
parsed = jQuery.buildFragment( [ data ], context, scripts );
|
|
|
|
if ( scripts && scripts.length ) {
|
|
jQuery( scripts ).remove();
|
|
}
|
|
|
|
return jQuery.merge( [], parsed.childNodes );
|
|
};
|
|
|
|
|
|
// Keep a copy of the old load method
|
|
var _load = jQuery.fn.load;
|
|
|
|
/**
|
|
* Load a url into a page
|
|
*/
|
|
jQuery.fn.load = function( url, params, callback ) {
|
|
if ( typeof url !== "string" && _load ) {
|
|
return _load.apply( this, arguments );
|
|
}
|
|
|
|
var selector, type, response,
|
|
self = this,
|
|
off = url.indexOf(" ");
|
|
|
|
if ( off >= 0 ) {
|
|
selector = url.slice( off );
|
|
url = url.slice( 0, off );
|
|
}
|
|
|
|
// If it's a function
|
|
if ( jQuery.isFunction( params ) ) {
|
|
|
|
// We assume that it's the callback
|
|
callback = params;
|
|
params = undefined;
|
|
|
|
// Otherwise, build a param string
|
|
} else if ( params && typeof params === "object" ) {
|
|
type = "POST";
|
|
}
|
|
|
|
// If we have elements to modify, make the request
|
|
if ( self.length > 0 ) {
|
|
jQuery.ajax({
|
|
url: url,
|
|
|
|
// if "type" variable is undefined, then "GET" method will be used
|
|
type: type,
|
|
dataType: "html",
|
|
data: params
|
|
}).done(function( responseText ) {
|
|
|
|
// Save response for use in complete callback
|
|
response = arguments;
|
|
|
|
self.html( selector ?
|
|
|
|
// If a selector was specified, locate the right elements in a dummy div
|
|
// Exclude scripts to avoid IE 'Permission Denied' errors
|
|
jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
|
|
|
|
// Otherwise use the full result
|
|
responseText );
|
|
|
|
}).complete( callback && function( jqXHR, status ) {
|
|
self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
|
|
});
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
|
|
|
|
|
|
jQuery.expr.filters.animated = function( elem ) {
|
|
return jQuery.grep(jQuery.timers, function( fn ) {
|
|
return elem === fn.elem;
|
|
}).length;
|
|
};
|
|
|
|
|
|
|
|
|
|
var docElem = window.document.documentElement;
|
|
|
|
/**
|
|
* Gets a window from an element
|
|
*/
|
|
function getWindow( elem ) {
|
|
return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
|
|
}
|
|
|
|
jQuery.offset = {
|
|
setOffset: function( elem, options, i ) {
|
|
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
|
|
position = jQuery.css( elem, "position" ),
|
|
curElem = jQuery( elem ),
|
|
props = {};
|
|
|
|
// Set position first, in-case top/left are set even on static elem
|
|
if ( position === "static" ) {
|
|
elem.style.position = "relative";
|
|
}
|
|
|
|
curOffset = curElem.offset();
|
|
curCSSTop = jQuery.css( elem, "top" );
|
|
curCSSLeft = jQuery.css( elem, "left" );
|
|
calculatePosition = ( position === "absolute" || position === "fixed" ) &&
|
|
( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
|
|
|
|
// Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
|
|
if ( calculatePosition ) {
|
|
curPosition = curElem.position();
|
|
curTop = curPosition.top;
|
|
curLeft = curPosition.left;
|
|
|
|
} else {
|
|
curTop = parseFloat( curCSSTop ) || 0;
|
|
curLeft = parseFloat( curCSSLeft ) || 0;
|
|
}
|
|
|
|
if ( jQuery.isFunction( options ) ) {
|
|
options = options.call( elem, i, curOffset );
|
|
}
|
|
|
|
if ( options.top != null ) {
|
|
props.top = ( options.top - curOffset.top ) + curTop;
|
|
}
|
|
if ( options.left != null ) {
|
|
props.left = ( options.left - curOffset.left ) + curLeft;
|
|
}
|
|
|
|
if ( "using" in options ) {
|
|
options.using.call( elem, props );
|
|
|
|
} else {
|
|
curElem.css( props );
|
|
}
|
|
}
|
|
};
|
|
|
|
jQuery.fn.extend({
|
|
offset: function( options ) {
|
|
if ( arguments.length ) {
|
|
return options === undefined ?
|
|
this :
|
|
this.each(function( i ) {
|
|
jQuery.offset.setOffset( this, options, i );
|
|
});
|
|
}
|
|
|
|
var docElem, win,
|
|
elem = this[ 0 ],
|
|
box = { top: 0, left: 0 },
|
|
doc = elem && elem.ownerDocument;
|
|
|
|
if ( !doc ) {
|
|
return;
|
|
}
|
|
|
|
docElem = doc.documentElement;
|
|
|
|
// Make sure it's not a disconnected DOM node
|
|
if ( !jQuery.contains( docElem, elem ) ) {
|
|
return box;
|
|
}
|
|
|
|
// If we don't have gBCR, just use 0,0 rather than error
|
|
// BlackBerry 5, iOS 3 (original iPhone)
|
|
if ( typeof elem.getBoundingClientRect !== strundefined ) {
|
|
box = elem.getBoundingClientRect();
|
|
}
|
|
win = getWindow( doc );
|
|
return {
|
|
top: box.top + win.pageYOffset - docElem.clientTop,
|
|
left: box.left + win.pageXOffset - docElem.clientLeft
|
|
};
|
|
},
|
|
|
|
position: function() {
|
|
if ( !this[ 0 ] ) {
|
|
return;
|
|
}
|
|
|
|
var offsetParent, offset,
|
|
elem = this[ 0 ],
|
|
parentOffset = { top: 0, left: 0 };
|
|
|
|
// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
|
|
if ( jQuery.css( elem, "position" ) === "fixed" ) {
|
|
// We assume that getBoundingClientRect is available when computed position is fixed
|
|
offset = elem.getBoundingClientRect();
|
|
|
|
} else {
|
|
// Get *real* offsetParent
|
|
offsetParent = this.offsetParent();
|
|
|
|
// Get correct offsets
|
|
offset = this.offset();
|
|
if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
|
|
parentOffset = offsetParent.offset();
|
|
}
|
|
|
|
// Add offsetParent borders
|
|
parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
|
|
parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
|
|
}
|
|
|
|
// Subtract parent offsets and element margins
|
|
return {
|
|
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
|
|
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
|
|
};
|
|
},
|
|
|
|
offsetParent: function() {
|
|
return this.map(function() {
|
|
var offsetParent = this.offsetParent || docElem;
|
|
|
|
while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
|
|
offsetParent = offsetParent.offsetParent;
|
|
}
|
|
|
|
return offsetParent || docElem;
|
|
});
|
|
}
|
|
});
|
|
|
|
// Create scrollLeft and scrollTop methods
|
|
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
|
|
var top = "pageYOffset" === prop;
|
|
|
|
jQuery.fn[ method ] = function( val ) {
|
|
return access( this, function( elem, method, val ) {
|
|
var win = getWindow( elem );
|
|
|
|
if ( val === undefined ) {
|
|
return win ? win[ prop ] : elem[ method ];
|
|
}
|
|
|
|
if ( win ) {
|
|
win.scrollTo(
|
|
!top ? val : window.pageXOffset,
|
|
top ? val : window.pageYOffset
|
|
);
|
|
|
|
} else {
|
|
elem[ method ] = val;
|
|
}
|
|
}, method, val, arguments.length, null );
|
|
};
|
|
});
|
|
|
|
// Add the top/left cssHooks using jQuery.fn.position
|
|
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
|
|
// getComputedStyle returns percent when specified for top/left/bottom/right
|
|
// rather than make the css module depend on the offset module, we just check for it here
|
|
jQuery.each( [ "top", "left" ], function( i, prop ) {
|
|
jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
|
|
function( elem, computed ) {
|
|
if ( computed ) {
|
|
computed = curCSS( elem, prop );
|
|
// if curCSS returns percentage, fallback to offset
|
|
return rnumnonpx.test( computed ) ?
|
|
jQuery( elem ).position()[ prop ] + "px" :
|
|
computed;
|
|
}
|
|
}
|
|
);
|
|
});
|
|
|
|
|
|
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
|
|
jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
|
|
jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
|
|
// margin is only for outerHeight, outerWidth
|
|
jQuery.fn[ funcName ] = function( margin, value ) {
|
|
var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
|
|
extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
|
|
|
|
return access( this, function( elem, type, value ) {
|
|
var doc;
|
|
|
|
if ( jQuery.isWindow( elem ) ) {
|
|
// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
|
|
// isn't a whole lot we can do. See pull request at this URL for discussion:
|
|
// https://github.com/jquery/jquery/pull/764
|
|
return elem.document.documentElement[ "client" + name ];
|
|
}
|
|
|
|
// Get document width or height
|
|
if ( elem.nodeType === 9 ) {
|
|
doc = elem.documentElement;
|
|
|
|
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
|
|
// whichever is greatest
|
|
return Math.max(
|
|
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
|
|
elem.body[ "offset" + name ], doc[ "offset" + name ],
|
|
doc[ "client" + name ]
|
|
);
|
|
}
|
|
|
|
return value === undefined ?
|
|
// Get width or height on the element, requesting but not forcing parseFloat
|
|
jQuery.css( elem, type, extra ) :
|
|
|
|
// Set width or height on the element
|
|
jQuery.style( elem, type, value, extra );
|
|
}, type, chainable ? margin : undefined, chainable, null );
|
|
};
|
|
});
|
|
});
|
|
|
|
|
|
// The number of elements contained in the matched element set
|
|
jQuery.fn.size = function() {
|
|
return this.length;
|
|
};
|
|
|
|
jQuery.fn.andSelf = jQuery.fn.addBack;
|
|
|
|
|
|
|
|
|
|
// Register as a named AMD module, since jQuery can be concatenated with other
|
|
// files that may use define, but not via a proper concatenation script that
|
|
// understands anonymous AMD modules. A named AMD is safest and most robust
|
|
// way to register. Lowercase jquery is used because AMD module names are
|
|
// derived from file names, and jQuery is normally delivered in a lowercase
|
|
// file name. Do this after creating the global so that if an AMD module wants
|
|
// to call noConflict to hide this version of jQuery, it will work.
|
|
if ( typeof define === "function" && define.amd ) {
|
|
define( "jquery", [], function() {
|
|
return jQuery;
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
var
|
|
// Map over jQuery in case of overwrite
|
|
_jQuery = window.jQuery,
|
|
|
|
// Map over the $ in case of overwrite
|
|
_$ = window.$;
|
|
|
|
jQuery.noConflict = function( deep ) {
|
|
if ( window.$ === jQuery ) {
|
|
window.$ = _$;
|
|
}
|
|
|
|
if ( deep && window.jQuery === jQuery ) {
|
|
window.jQuery = _jQuery;
|
|
}
|
|
|
|
return jQuery;
|
|
};
|
|
|
|
// Expose jQuery and $ identifiers, even in
|
|
// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
|
|
// and CommonJS for browser emulators (#13566)
|
|
if ( typeof noGlobal === strundefined ) {
|
|
window.jQuery = window.$ = jQuery;
|
|
}
|
|
|
|
|
|
|
|
|
|
return jQuery;
|
|
|
|
}));
|
|
|
|
define("jQuery", (function (global) {
|
|
return function () {
|
|
var ret, fn;
|
|
return ret || global.$;
|
|
};
|
|
}(this)));
|
|
|
|
define('utils/storage',[],function(){
|
|
var baseKey = "";
|
|
|
|
/*
|
|
* Simple module for storing data in the browser's local storage
|
|
*/
|
|
return {
|
|
setBaseKey: function(key) {
|
|
baseKey = key;
|
|
},
|
|
set: function(key, value) {
|
|
key = baseKey+":"+key;
|
|
localStorage[key] = JSON.stringify(value);
|
|
},
|
|
get: function(key, def) {
|
|
key = baseKey+":"+key;
|
|
if (localStorage[key] === undefined) return def;
|
|
try {
|
|
var v = JSON.parse(localStorage[key]);
|
|
return v == null ? def : v;;
|
|
} catch(err) {
|
|
console.error(err);
|
|
return localStorage[key] || def;
|
|
}
|
|
},
|
|
remove: function(key) {
|
|
key = baseKey+":"+key;
|
|
localStorage.removeItem(key);
|
|
}
|
|
};
|
|
});
|
|
/**
|
|
* @license
|
|
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
|
|
* Build: `lodash modern -o ./dist/lodash.js`
|
|
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
|
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
|
|
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
|
* Available under MIT license <http://lodash.com/license>
|
|
*/
|
|
;(function() {
|
|
|
|
/** Used as a safe reference for `undefined` in pre ES5 environments */
|
|
var undefined;
|
|
|
|
/** Used to pool arrays and objects used internally */
|
|
var arrayPool = [],
|
|
objectPool = [];
|
|
|
|
/** Used to generate unique IDs */
|
|
var idCounter = 0;
|
|
|
|
/** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
|
|
var keyPrefix = +new Date + '';
|
|
|
|
/** Used as the size when optimizations are enabled for large arrays */
|
|
var largeArraySize = 75;
|
|
|
|
/** Used as the max size of the `arrayPool` and `objectPool` */
|
|
var maxPoolSize = 40;
|
|
|
|
/** Used to detect and test whitespace */
|
|
var whitespace = (
|
|
// whitespace
|
|
' \t\x0B\f\xA0\ufeff' +
|
|
|
|
// line terminators
|
|
'\n\r\u2028\u2029' +
|
|
|
|
// unicode category "Zs" space separators
|
|
'\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
|
|
);
|
|
|
|
/** Used to match empty string literals in compiled template source */
|
|
var reEmptyStringLeading = /\b__p \+= '';/g,
|
|
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
|
|
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
|
|
|
|
/**
|
|
* Used to match ES6 template delimiters
|
|
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
|
|
*/
|
|
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
|
|
|
|
/** Used to match regexp flags from their coerced string values */
|
|
var reFlags = /\w*$/;
|
|
|
|
/** Used to detected named functions */
|
|
var reFuncName = /^\s*function[ \n\r\t]+\w/;
|
|
|
|
/** Used to match "interpolate" template delimiters */
|
|
var reInterpolate = /<%=([\s\S]+?)%>/g;
|
|
|
|
/** Used to match leading whitespace and zeros to be removed */
|
|
var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
|
|
|
|
/** Used to ensure capturing order of template delimiters */
|
|
var reNoMatch = /($^)/;
|
|
|
|
/** Used to detect functions containing a `this` reference */
|
|
var reThis = /\bthis\b/;
|
|
|
|
/** Used to match unescaped characters in compiled string literals */
|
|
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
|
|
|
|
/** Used to assign default `context` object properties */
|
|
var contextProps = [
|
|
'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
|
|
'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
|
|
'parseInt', 'setTimeout'
|
|
];
|
|
|
|
/** Used to make template sourceURLs easier to identify */
|
|
var templateCounter = 0;
|
|
|
|
/** `Object#toString` result shortcuts */
|
|
var argsClass = '[object Arguments]',
|
|
arrayClass = '[object Array]',
|
|
boolClass = '[object Boolean]',
|
|
dateClass = '[object Date]',
|
|
funcClass = '[object Function]',
|
|
numberClass = '[object Number]',
|
|
objectClass = '[object Object]',
|
|
regexpClass = '[object RegExp]',
|
|
stringClass = '[object String]';
|
|
|
|
/** Used to identify object classifications that `_.clone` supports */
|
|
var cloneableClasses = {};
|
|
cloneableClasses[funcClass] = false;
|
|
cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
|
|
cloneableClasses[boolClass] = cloneableClasses[dateClass] =
|
|
cloneableClasses[numberClass] = cloneableClasses[objectClass] =
|
|
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
|
|
|
|
/** Used as an internal `_.debounce` options object */
|
|
var debounceOptions = {
|
|
'leading': false,
|
|
'maxWait': 0,
|
|
'trailing': false
|
|
};
|
|
|
|
/** Used as the property descriptor for `__bindData__` */
|
|
var descriptor = {
|
|
'configurable': false,
|
|
'enumerable': false,
|
|
'value': null,
|
|
'writable': false
|
|
};
|
|
|
|
/** Used to determine if values are of the language type Object */
|
|
var objectTypes = {
|
|
'boolean': false,
|
|
'function': true,
|
|
'object': true,
|
|
'number': false,
|
|
'string': false,
|
|
'undefined': false
|
|
};
|
|
|
|
/** Used to escape characters for inclusion in compiled string literals */
|
|
var stringEscapes = {
|
|
'\\': '\\',
|
|
"'": "'",
|
|
'\n': 'n',
|
|
'\r': 'r',
|
|
'\t': 't',
|
|
'\u2028': 'u2028',
|
|
'\u2029': 'u2029'
|
|
};
|
|
|
|
/** Used as a reference to the global object */
|
|
var root = (objectTypes[typeof window] && window) || this;
|
|
|
|
/** 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 the popular CommonJS extension `module.exports` */
|
|
var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
|
|
|
|
/** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
|
|
var freeGlobal = objectTypes[typeof global] && global;
|
|
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
|
|
root = freeGlobal;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* The base implementation of `_.indexOf` without support for binary searches
|
|
* or `fromIndex` constraints.
|
|
*
|
|
* @private
|
|
* @param {Array} array The array to search.
|
|
* @param {*} value The value to search for.
|
|
* @param {number} [fromIndex=0] The index to search from.
|
|
* @returns {number} Returns the index of the matched value or `-1`.
|
|
*/
|
|
function baseIndexOf(array, value, fromIndex) {
|
|
var index = (fromIndex || 0) - 1,
|
|
length = array ? array.length : 0;
|
|
|
|
while (++index < length) {
|
|
if (array[index] === value) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* An implementation of `_.contains` for cache objects that mimics the return
|
|
* signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
|
|
*
|
|
* @private
|
|
* @param {Object} cache The cache object to inspect.
|
|
* @param {*} value The value to search for.
|
|
* @returns {number} Returns `0` if `value` is found, else `-1`.
|
|
*/
|
|
function cacheIndexOf(cache, value) {
|
|
var type = typeof value;
|
|
cache = cache.cache;
|
|
|
|
if (type == 'boolean' || value == null) {
|
|
return cache[value] ? 0 : -1;
|
|
}
|
|
if (type != 'number' && type != 'string') {
|
|
type = 'object';
|
|
}
|
|
var key = type == 'number' ? value : keyPrefix + value;
|
|
cache = (cache = cache[type]) && cache[key];
|
|
|
|
return type == 'object'
|
|
? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
|
|
: (cache ? 0 : -1);
|
|
}
|
|
|
|
/**
|
|
* Adds a given value to the corresponding cache object.
|
|
*
|
|
* @private
|
|
* @param {*} value The value to add to the cache.
|
|
*/
|
|
function cachePush(value) {
|
|
var cache = this.cache,
|
|
type = typeof value;
|
|
|
|
if (type == 'boolean' || value == null) {
|
|
cache[value] = true;
|
|
} else {
|
|
if (type != 'number' && type != 'string') {
|
|
type = 'object';
|
|
}
|
|
var key = type == 'number' ? value : keyPrefix + value,
|
|
typeCache = cache[type] || (cache[type] = {});
|
|
|
|
if (type == 'object') {
|
|
(typeCache[key] || (typeCache[key] = [])).push(value);
|
|
} else {
|
|
typeCache[key] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Used by `_.max` and `_.min` as the default callback when a given
|
|
* collection is a string value.
|
|
*
|
|
* @private
|
|
* @param {string} value The character to inspect.
|
|
* @returns {number} Returns the code unit of given character.
|
|
*/
|
|
function charAtCallback(value) {
|
|
return value.charCodeAt(0);
|
|
}
|
|
|
|
/**
|
|
* Used by `sortBy` to compare transformed `collection` elements, stable sorting
|
|
* them in ascending order.
|
|
*
|
|
* @private
|
|
* @param {Object} a The object to compare to `b`.
|
|
* @param {Object} b The object to compare to `a`.
|
|
* @returns {number} Returns the sort order indicator of `1` or `-1`.
|
|
*/
|
|
function compareAscending(a, b) {
|
|
var ac = a.criteria,
|
|
bc = b.criteria,
|
|
index = -1,
|
|
length = ac.length;
|
|
|
|
while (++index < length) {
|
|
var value = ac[index],
|
|
other = bc[index];
|
|
|
|
if (value !== other) {
|
|
if (value > other || typeof value == 'undefined') {
|
|
return 1;
|
|
}
|
|
if (value < other || typeof other == 'undefined') {
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
|
|
// that causes it, under certain circumstances, to return the same value for
|
|
// `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
|
|
//
|
|
// This also ensures a stable sort in V8 and other engines.
|
|
// See http://code.google.com/p/v8/issues/detail?id=90
|
|
return a.index - b.index;
|
|
}
|
|
|
|
/**
|
|
* Creates a cache object to optimize linear searches of large arrays.
|
|
*
|
|
* @private
|
|
* @param {Array} [array=[]] The array to search.
|
|
* @returns {null|Object} Returns the cache object or `null` if caching should not be used.
|
|
*/
|
|
function createCache(array) {
|
|
var index = -1,
|
|
length = array.length,
|
|
first = array[0],
|
|
mid = array[(length / 2) | 0],
|
|
last = array[length - 1];
|
|
|
|
if (first && typeof first == 'object' &&
|
|
mid && typeof mid == 'object' && last && typeof last == 'object') {
|
|
return false;
|
|
}
|
|
var cache = getObject();
|
|
cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
|
|
|
|
var result = getObject();
|
|
result.array = array;
|
|
result.cache = cache;
|
|
result.push = cachePush;
|
|
|
|
while (++index < length) {
|
|
result.push(array[index]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Used by `template` to escape characters for inclusion in compiled
|
|
* string literals.
|
|
*
|
|
* @private
|
|
* @param {string} match The matched character to escape.
|
|
* @returns {string} Returns the escaped character.
|
|
*/
|
|
function escapeStringChar(match) {
|
|
return '\\' + stringEscapes[match];
|
|
}
|
|
|
|
/**
|
|
* Gets an array from the array pool or creates a new one if the pool is empty.
|
|
*
|
|
* @private
|
|
* @returns {Array} The array from the pool.
|
|
*/
|
|
function getArray() {
|
|
return arrayPool.pop() || [];
|
|
}
|
|
|
|
/**
|
|
* Gets an object from the object pool or creates a new one if the pool is empty.
|
|
*
|
|
* @private
|
|
* @returns {Object} The object from the pool.
|
|
*/
|
|
function getObject() {
|
|
return objectPool.pop() || {
|
|
'array': null,
|
|
'cache': null,
|
|
'criteria': null,
|
|
'false': false,
|
|
'index': 0,
|
|
'null': false,
|
|
'number': null,
|
|
'object': null,
|
|
'push': null,
|
|
'string': null,
|
|
'true': false,
|
|
'undefined': false,
|
|
'value': null
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Releases the given array back to the array pool.
|
|
*
|
|
* @private
|
|
* @param {Array} [array] The array to release.
|
|
*/
|
|
function releaseArray(array) {
|
|
array.length = 0;
|
|
if (arrayPool.length < maxPoolSize) {
|
|
arrayPool.push(array);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Releases the given object back to the object pool.
|
|
*
|
|
* @private
|
|
* @param {Object} [object] The object to release.
|
|
*/
|
|
function releaseObject(object) {
|
|
var cache = object.cache;
|
|
if (cache) {
|
|
releaseObject(cache);
|
|
}
|
|
object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
|
|
if (objectPool.length < maxPoolSize) {
|
|
objectPool.push(object);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Slices the `collection` from the `start` index up to, but not including,
|
|
* the `end` index.
|
|
*
|
|
* Note: This function is used instead of `Array#slice` to support node lists
|
|
* in IE < 9 and to ensure dense arrays are returned.
|
|
*
|
|
* @private
|
|
* @param {Array|Object|string} collection The collection to slice.
|
|
* @param {number} start The start index.
|
|
* @param {number} end The end index.
|
|
* @returns {Array} Returns the new array.
|
|
*/
|
|
function slice(array, start, end) {
|
|
start || (start = 0);
|
|
if (typeof end == 'undefined') {
|
|
end = array ? array.length : 0;
|
|
}
|
|
var index = -1,
|
|
length = end - start || 0,
|
|
result = Array(length < 0 ? 0 : length);
|
|
|
|
while (++index < length) {
|
|
result[index] = array[start + index];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Create a new `lodash` function using the given context object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {Object} [context=root] The context object.
|
|
* @returns {Function} Returns the `lodash` function.
|
|
*/
|
|
function runInContext(context) {
|
|
// Avoid issues with some ES3 environments that attempt to use values, named
|
|
// after built-in constructors like `Object`, for the creation of literals.
|
|
// ES5 clears this up by stating that literals must use built-in constructors.
|
|
// See http://es5.github.io/#x11.1.5.
|
|
context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
|
|
|
|
/** Native constructor references */
|
|
var Array = context.Array,
|
|
Boolean = context.Boolean,
|
|
Date = context.Date,
|
|
Function = context.Function,
|
|
Math = context.Math,
|
|
Number = context.Number,
|
|
Object = context.Object,
|
|
RegExp = context.RegExp,
|
|
String = context.String,
|
|
TypeError = context.TypeError;
|
|
|
|
/**
|
|
* Used for `Array` method references.
|
|
*
|
|
* Normally `Array.prototype` would suffice, however, using an array literal
|
|
* avoids issues in Narwhal.
|
|
*/
|
|
var arrayRef = [];
|
|
|
|
/** Used for native method references */
|
|
var objectProto = Object.prototype;
|
|
|
|
/** Used to restore the original `_` reference in `noConflict` */
|
|
var oldDash = context._;
|
|
|
|
/** Used to resolve the internal [[Class]] of values */
|
|
var toString = objectProto.toString;
|
|
|
|
/** Used to detect if a method is native */
|
|
var reNative = RegExp('^' +
|
|
String(toString)
|
|
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
.replace(/toString| for [^\]]+/g, '.*?') + '$'
|
|
);
|
|
|
|
/** Native method shortcuts */
|
|
var ceil = Math.ceil,
|
|
clearTimeout = context.clearTimeout,
|
|
floor = Math.floor,
|
|
fnToString = Function.prototype.toString,
|
|
getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
|
|
hasOwnProperty = objectProto.hasOwnProperty,
|
|
push = arrayRef.push,
|
|
setTimeout = context.setTimeout,
|
|
splice = arrayRef.splice,
|
|
unshift = arrayRef.unshift;
|
|
|
|
/** Used to set meta data on functions */
|
|
var defineProperty = (function() {
|
|
// IE 8 only accepts DOM elements
|
|
try {
|
|
var o = {},
|
|
func = isNative(func = Object.defineProperty) && func,
|
|
result = func(o, o, o) && func;
|
|
} catch(e) { }
|
|
return result;
|
|
}());
|
|
|
|
/* Native method shortcuts for methods with the same name as other `lodash` methods */
|
|
var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
|
|
nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
|
|
nativeIsFinite = context.isFinite,
|
|
nativeIsNaN = context.isNaN,
|
|
nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
|
|
nativeMax = Math.max,
|
|
nativeMin = Math.min,
|
|
nativeParseInt = context.parseInt,
|
|
nativeRandom = Math.random;
|
|
|
|
/** Used to lookup a built-in constructor by [[Class]] */
|
|
var ctorByClass = {};
|
|
ctorByClass[arrayClass] = Array;
|
|
ctorByClass[boolClass] = Boolean;
|
|
ctorByClass[dateClass] = Date;
|
|
ctorByClass[funcClass] = Function;
|
|
ctorByClass[objectClass] = Object;
|
|
ctorByClass[numberClass] = Number;
|
|
ctorByClass[regexpClass] = RegExp;
|
|
ctorByClass[stringClass] = String;
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates a `lodash` object which wraps the given value to enable intuitive
|
|
* method chaining.
|
|
*
|
|
* In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
|
|
* `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
|
|
* and `unshift`
|
|
*
|
|
* Chaining is supported in custom builds as long as the `value` method is
|
|
* implicitly or explicitly included in the build.
|
|
*
|
|
* The chainable wrapper functions are:
|
|
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
|
|
* `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
|
|
* `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
|
|
* `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
|
|
* `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
|
|
* `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
|
|
* `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
|
|
* `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
|
|
* `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
|
|
* `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
|
|
* and `zip`
|
|
*
|
|
* The non-chainable wrapper functions are:
|
|
* `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
|
|
* `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
|
|
* `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
|
|
* `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
|
|
* `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
|
|
* `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
|
|
* `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
|
|
* `template`, `unescape`, `uniqueId`, and `value`
|
|
*
|
|
* The wrapper functions `first` and `last` return wrapped values when `n` is
|
|
* provided, otherwise they return unwrapped values.
|
|
*
|
|
* Explicit chaining can be enabled by using the `_.chain` method.
|
|
*
|
|
* @name _
|
|
* @constructor
|
|
* @category Chaining
|
|
* @param {*} value The value to wrap in a `lodash` instance.
|
|
* @returns {Object} Returns a `lodash` instance.
|
|
* @example
|
|
*
|
|
* var wrapped = _([1, 2, 3]);
|
|
*
|
|
* // returns an unwrapped value
|
|
* wrapped.reduce(function(sum, num) {
|
|
* return sum + num;
|
|
* });
|
|
* // => 6
|
|
*
|
|
* // returns a wrapped value
|
|
* var squares = wrapped.map(function(num) {
|
|
* return num * num;
|
|
* });
|
|
*
|
|
* _.isArray(squares);
|
|
* // => false
|
|
*
|
|
* _.isArray(squares.value());
|
|
* // => true
|
|
*/
|
|
function lodash(value) {
|
|
// don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
|
|
return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
|
|
? value
|
|
: new lodashWrapper(value);
|
|
}
|
|
|
|
/**
|
|
* A fast path for creating `lodash` wrapper objects.
|
|
*
|
|
* @private
|
|
* @param {*} value The value to wrap in a `lodash` instance.
|
|
* @param {boolean} chainAll A flag to enable chaining for all methods
|
|
* @returns {Object} Returns a `lodash` instance.
|
|
*/
|
|
function lodashWrapper(value, chainAll) {
|
|
this.__chain__ = !!chainAll;
|
|
this.__wrapped__ = value;
|
|
}
|
|
// ensure `new lodashWrapper` is an instance of `lodash`
|
|
lodashWrapper.prototype = lodash.prototype;
|
|
|
|
/**
|
|
* An object used to flag environments features.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Object
|
|
*/
|
|
var support = lodash.support = {};
|
|
|
|
/**
|
|
* Detect if functions can be decompiled by `Function#toString`
|
|
* (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
|
|
*
|
|
* @memberOf _.support
|
|
* @type boolean
|
|
*/
|
|
support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
|
|
|
|
/**
|
|
* Detect if `Function#name` is supported (all but IE).
|
|
*
|
|
* @memberOf _.support
|
|
* @type boolean
|
|
*/
|
|
support.funcNames = typeof Function.name == 'string';
|
|
|
|
/**
|
|
* By default, the template delimiters used by Lo-Dash are similar to those in
|
|
* embedded Ruby (ERB). Change the following template settings to use alternative
|
|
* delimiters.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Object
|
|
*/
|
|
lodash.templateSettings = {
|
|
|
|
/**
|
|
* Used to detect `data` property values to be HTML-escaped.
|
|
*
|
|
* @memberOf _.templateSettings
|
|
* @type RegExp
|
|
*/
|
|
'escape': /<%-([\s\S]+?)%>/g,
|
|
|
|
/**
|
|
* Used to detect code to be evaluated.
|
|
*
|
|
* @memberOf _.templateSettings
|
|
* @type RegExp
|
|
*/
|
|
'evaluate': /<%([\s\S]+?)%>/g,
|
|
|
|
/**
|
|
* Used to detect `data` property values to inject.
|
|
*
|
|
* @memberOf _.templateSettings
|
|
* @type RegExp
|
|
*/
|
|
'interpolate': reInterpolate,
|
|
|
|
/**
|
|
* Used to reference the data object in the template text.
|
|
*
|
|
* @memberOf _.templateSettings
|
|
* @type string
|
|
*/
|
|
'variable': '',
|
|
|
|
/**
|
|
* Used to import variables into the compiled template.
|
|
*
|
|
* @memberOf _.templateSettings
|
|
* @type Object
|
|
*/
|
|
'imports': {
|
|
|
|
/**
|
|
* A reference to the `lodash` function.
|
|
*
|
|
* @memberOf _.templateSettings.imports
|
|
* @type Function
|
|
*/
|
|
'_': lodash
|
|
}
|
|
};
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* The base implementation of `_.bind` that creates the bound function and
|
|
* sets its meta data.
|
|
*
|
|
* @private
|
|
* @param {Array} bindData The bind data array.
|
|
* @returns {Function} Returns the new bound function.
|
|
*/
|
|
function baseBind(bindData) {
|
|
var func = bindData[0],
|
|
partialArgs = bindData[2],
|
|
thisArg = bindData[4];
|
|
|
|
function bound() {
|
|
// `Function#bind` spec
|
|
// http://es5.github.io/#x15.3.4.5
|
|
if (partialArgs) {
|
|
// avoid `arguments` object deoptimizations by using `slice` instead
|
|
// of `Array.prototype.slice.call` and not assigning `arguments` to a
|
|
// variable as a ternary expression
|
|
var args = slice(partialArgs);
|
|
push.apply(args, arguments);
|
|
}
|
|
// mimic the constructor's `return` behavior
|
|
// http://es5.github.io/#x13.2.2
|
|
if (this instanceof bound) {
|
|
// ensure `new bound` is an instance of `func`
|
|
var thisBinding = baseCreate(func.prototype),
|
|
result = func.apply(thisBinding, args || arguments);
|
|
return isObject(result) ? result : thisBinding;
|
|
}
|
|
return func.apply(thisArg, args || arguments);
|
|
}
|
|
setBindData(bound, bindData);
|
|
return bound;
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.clone` without argument juggling or support
|
|
* for `thisArg` binding.
|
|
*
|
|
* @private
|
|
* @param {*} value The value to clone.
|
|
* @param {boolean} [isDeep=false] Specify a deep clone.
|
|
* @param {Function} [callback] The function to customize cloning values.
|
|
* @param {Array} [stackA=[]] Tracks traversed source objects.
|
|
* @param {Array} [stackB=[]] Associates clones with source counterparts.
|
|
* @returns {*} Returns the cloned value.
|
|
*/
|
|
function baseClone(value, isDeep, callback, stackA, stackB) {
|
|
if (callback) {
|
|
var result = callback(value);
|
|
if (typeof result != 'undefined') {
|
|
return result;
|
|
}
|
|
}
|
|
// inspect [[Class]]
|
|
var isObj = isObject(value);
|
|
if (isObj) {
|
|
var className = toString.call(value);
|
|
if (!cloneableClasses[className]) {
|
|
return value;
|
|
}
|
|
var ctor = ctorByClass[className];
|
|
switch (className) {
|
|
case boolClass:
|
|
case dateClass:
|
|
return new ctor(+value);
|
|
|
|
case numberClass:
|
|
case stringClass:
|
|
return new ctor(value);
|
|
|
|
case regexpClass:
|
|
result = ctor(value.source, reFlags.exec(value));
|
|
result.lastIndex = value.lastIndex;
|
|
return result;
|
|
}
|
|
} else {
|
|
return value;
|
|
}
|
|
var isArr = isArray(value);
|
|
if (isDeep) {
|
|
// check for circular references and return corresponding clone
|
|
var initedStack = !stackA;
|
|
stackA || (stackA = getArray());
|
|
stackB || (stackB = getArray());
|
|
|
|
var length = stackA.length;
|
|
while (length--) {
|
|
if (stackA[length] == value) {
|
|
return stackB[length];
|
|
}
|
|
}
|
|
result = isArr ? ctor(value.length) : {};
|
|
}
|
|
else {
|
|
result = isArr ? slice(value) : assign({}, value);
|
|
}
|
|
// add array properties assigned by `RegExp#exec`
|
|
if (isArr) {
|
|
if (hasOwnProperty.call(value, 'index')) {
|
|
result.index = value.index;
|
|
}
|
|
if (hasOwnProperty.call(value, 'input')) {
|
|
result.input = value.input;
|
|
}
|
|
}
|
|
// exit for shallow clone
|
|
if (!isDeep) {
|
|
return result;
|
|
}
|
|
// 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 ? forEach : forOwn)(value, function(objValue, key) {
|
|
result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
|
|
});
|
|
|
|
if (initedStack) {
|
|
releaseArray(stackA);
|
|
releaseArray(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.
|
|
*/
|
|
function baseCreate(prototype, properties) {
|
|
return isObject(prototype) ? nativeCreate(prototype) : {};
|
|
}
|
|
// fallback for browsers without `Object.create`
|
|
if (!nativeCreate) {
|
|
baseCreate = (function() {
|
|
function Object() {}
|
|
return function(prototype) {
|
|
if (isObject(prototype)) {
|
|
Object.prototype = prototype;
|
|
var result = new Object;
|
|
Object.prototype = null;
|
|
}
|
|
return result || context.Object();
|
|
};
|
|
}());
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.createCallback` without support for creating
|
|
* "_.pluck" or "_.where" style callbacks.
|
|
*
|
|
* @private
|
|
* @param {*} [func=identity] The value to convert to a callback.
|
|
* @param {*} [thisArg] The `this` binding of the created callback.
|
|
* @param {number} [argCount] The number of arguments the callback accepts.
|
|
* @returns {Function} Returns a callback function.
|
|
*/
|
|
function baseCreateCallback(func, thisArg, argCount) {
|
|
if (typeof func != 'function') {
|
|
return identity;
|
|
}
|
|
// exit early for no `thisArg` or already bound by `Function#bind`
|
|
if (typeof thisArg == 'undefined' || !('prototype' in func)) {
|
|
return func;
|
|
}
|
|
var bindData = func.__bindData__;
|
|
if (typeof bindData == 'undefined') {
|
|
if (support.funcNames) {
|
|
bindData = !func.name;
|
|
}
|
|
bindData = bindData || !support.funcDecomp;
|
|
if (!bindData) {
|
|
var source = fnToString.call(func);
|
|
if (!support.funcNames) {
|
|
bindData = !reFuncName.test(source);
|
|
}
|
|
if (!bindData) {
|
|
// checks if `func` references the `this` keyword and stores the result
|
|
bindData = reThis.test(source);
|
|
setBindData(func, bindData);
|
|
}
|
|
}
|
|
}
|
|
// exit early if there are no `this` references or `func` is bound
|
|
if (bindData === false || (bindData !== true && bindData[1] & 1)) {
|
|
return func;
|
|
}
|
|
switch (argCount) {
|
|
case 1: return function(value) {
|
|
return func.call(thisArg, value);
|
|
};
|
|
case 2: return function(a, b) {
|
|
return func.call(thisArg, a, b);
|
|
};
|
|
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);
|
|
};
|
|
}
|
|
return bind(func, thisArg);
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `createWrapper` that creates the wrapper and
|
|
* sets its meta data.
|
|
*
|
|
* @private
|
|
* @param {Array} bindData The bind data array.
|
|
* @returns {Function} Returns the new function.
|
|
*/
|
|
function baseCreateWrapper(bindData) {
|
|
var func = bindData[0],
|
|
bitmask = bindData[1],
|
|
partialArgs = bindData[2],
|
|
partialRightArgs = bindData[3],
|
|
thisArg = bindData[4],
|
|
arity = bindData[5];
|
|
|
|
var isBind = bitmask & 1,
|
|
isBindKey = bitmask & 2,
|
|
isCurry = bitmask & 4,
|
|
isCurryBound = bitmask & 8,
|
|
key = func;
|
|
|
|
function bound() {
|
|
var thisBinding = isBind ? thisArg : this;
|
|
if (partialArgs) {
|
|
var args = slice(partialArgs);
|
|
push.apply(args, arguments);
|
|
}
|
|
if (partialRightArgs || isCurry) {
|
|
args || (args = slice(arguments));
|
|
if (partialRightArgs) {
|
|
push.apply(args, partialRightArgs);
|
|
}
|
|
if (isCurry && args.length < arity) {
|
|
bitmask |= 16 & ~32;
|
|
return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
|
|
}
|
|
}
|
|
args || (args = arguments);
|
|
if (isBindKey) {
|
|
func = thisBinding[key];
|
|
}
|
|
if (this instanceof bound) {
|
|
thisBinding = baseCreate(func.prototype);
|
|
var result = func.apply(thisBinding, args);
|
|
return isObject(result) ? result : thisBinding;
|
|
}
|
|
return func.apply(thisBinding, args);
|
|
}
|
|
setBindData(bound, bindData);
|
|
return bound;
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.difference` that accepts a single array
|
|
* of values to exclude.
|
|
*
|
|
* @private
|
|
* @param {Array} array The array to process.
|
|
* @param {Array} [values] The array of values to exclude.
|
|
* @returns {Array} Returns a new array of filtered values.
|
|
*/
|
|
function baseDifference(array, values) {
|
|
var index = -1,
|
|
indexOf = getIndexOf(),
|
|
length = array ? array.length : 0,
|
|
isLarge = length >= largeArraySize && indexOf === baseIndexOf,
|
|
result = [];
|
|
|
|
if (isLarge) {
|
|
var cache = createCache(values);
|
|
if (cache) {
|
|
indexOf = cacheIndexOf;
|
|
values = cache;
|
|
} else {
|
|
isLarge = false;
|
|
}
|
|
}
|
|
while (++index < length) {
|
|
var value = array[index];
|
|
if (indexOf(values, value) < 0) {
|
|
result.push(value);
|
|
}
|
|
}
|
|
if (isLarge) {
|
|
releaseObject(values);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.flatten` without support for callback
|
|
* shorthands or `thisArg` binding.
|
|
*
|
|
* @private
|
|
* @param {Array} array The array to flatten.
|
|
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
|
|
* @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
|
|
* @param {number} [fromIndex=0] The index to start from.
|
|
* @returns {Array} Returns a new flattened array.
|
|
*/
|
|
function baseFlatten(array, isShallow, isStrict, fromIndex) {
|
|
var index = (fromIndex || 0) - 1,
|
|
length = array ? array.length : 0,
|
|
result = [];
|
|
|
|
while (++index < length) {
|
|
var value = array[index];
|
|
|
|
if (value && typeof value == 'object' && typeof value.length == 'number'
|
|
&& (isArray(value) || isArguments(value))) {
|
|
// recursively flatten arrays (susceptible to call stack limits)
|
|
if (!isShallow) {
|
|
value = baseFlatten(value, isShallow, isStrict);
|
|
}
|
|
var valIndex = -1,
|
|
valLength = value.length,
|
|
resIndex = result.length;
|
|
|
|
result.length += valLength;
|
|
while (++valIndex < valLength) {
|
|
result[resIndex++] = value[valIndex];
|
|
}
|
|
} else if (!isStrict) {
|
|
result.push(value);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.isEqual`, without support for `thisArg` binding,
|
|
* that allows partial "_.where" style comparisons.
|
|
*
|
|
* @private
|
|
* @param {*} a The value to compare.
|
|
* @param {*} b The other value to compare.
|
|
* @param {Function} [callback] The function to customize comparing values.
|
|
* @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
|
|
* @param {Array} [stackA=[]] Tracks traversed `a` objects.
|
|
* @param {Array} [stackB=[]] Tracks traversed `b` objects.
|
|
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
|
*/
|
|
function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
|
|
// used to indicate that when comparing objects, `a` has at least the properties of `b`
|
|
if (callback) {
|
|
var result = callback(a, b);
|
|
if (typeof result != 'undefined') {
|
|
return !!result;
|
|
}
|
|
}
|
|
// exit early for identical values
|
|
if (a === b) {
|
|
// treat `+0` vs. `-0` as not equal
|
|
return a !== 0 || (1 / a == 1 / b);
|
|
}
|
|
var type = typeof a,
|
|
otherType = typeof b;
|
|
|
|
// exit early for unlike primitive values
|
|
if (a === a &&
|
|
!(a && objectTypes[type]) &&
|
|
!(b && objectTypes[otherType])) {
|
|
return false;
|
|
}
|
|
// exit early for `null` and `undefined` avoiding ES3's Function#call behavior
|
|
// http://es5.github.io/#x15.3.4.4
|
|
if (a == null || b == null) {
|
|
return a === b;
|
|
}
|
|
// compare [[Class]] names
|
|
var className = toString.call(a),
|
|
otherClass = toString.call(b);
|
|
|
|
if (className == argsClass) {
|
|
className = objectClass;
|
|
}
|
|
if (otherClass == argsClass) {
|
|
otherClass = objectClass;
|
|
}
|
|
if (className != otherClass) {
|
|
return false;
|
|
}
|
|
switch (className) {
|
|
case boolClass:
|
|
case dateClass:
|
|
// 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 +a == +b;
|
|
|
|
case numberClass:
|
|
// treat `NaN` vs. `NaN` as equal
|
|
return (a != +a)
|
|
? b != +b
|
|
// but treat `+0` vs. `-0` as not equal
|
|
: (a == 0 ? (1 / a == 1 / b) : a == +b);
|
|
|
|
case regexpClass:
|
|
case stringClass:
|
|
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
|
|
// treat string primitives and their corresponding object instances as equal
|
|
return a == String(b);
|
|
}
|
|
var isArr = className == arrayClass;
|
|
if (!isArr) {
|
|
// unwrap any `lodash` wrapped values
|
|
var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
|
|
bWrapped = hasOwnProperty.call(b, '__wrapped__');
|
|
|
|
if (aWrapped || bWrapped) {
|
|
return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
|
|
}
|
|
// exit for functions and DOM nodes
|
|
if (className != objectClass) {
|
|
return false;
|
|
}
|
|
// in older versions of Opera, `arguments` objects have `Array` constructors
|
|
var ctorA = a.constructor,
|
|
ctorB = b.constructor;
|
|
|
|
// non `Object` object instances with different constructors are not equal
|
|
if (ctorA != ctorB &&
|
|
!(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
|
|
('constructor' in a && 'constructor' in b)
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
// assume cyclic structures are equal
|
|
// the algorithm for detecting cyclic structures is adapted from ES 5.1
|
|
// section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
|
|
var initedStack = !stackA;
|
|
stackA || (stackA = getArray());
|
|
stackB || (stackB = getArray());
|
|
|
|
var length = stackA.length;
|
|
while (length--) {
|
|
if (stackA[length] == a) {
|
|
return stackB[length] == b;
|
|
}
|
|
}
|
|
var size = 0;
|
|
result = true;
|
|
|
|
// add `a` and `b` to the stack of traversed objects
|
|
stackA.push(a);
|
|
stackB.push(b);
|
|
|
|
// recursively compare objects and arrays (susceptible to call stack limits)
|
|
if (isArr) {
|
|
// compare lengths to determine if a deep comparison is necessary
|
|
length = a.length;
|
|
size = b.length;
|
|
result = size == length;
|
|
|
|
if (result || isWhere) {
|
|
// deep compare the contents, ignoring non-numeric properties
|
|
while (size--) {
|
|
var index = length,
|
|
value = b[size];
|
|
|
|
if (isWhere) {
|
|
while (index--) {
|
|
if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
|
|
// which, in this case, is more costly
|
|
forIn(b, function(value, key, b) {
|
|
if (hasOwnProperty.call(b, key)) {
|
|
// count the number of properties.
|
|
size++;
|
|
// deep compare each property value.
|
|
return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
|
|
}
|
|
});
|
|
|
|
if (result && !isWhere) {
|
|
// ensure both objects have the same number of properties
|
|
forIn(a, function(value, key, a) {
|
|
if (hasOwnProperty.call(a, key)) {
|
|
// `size` will be `-1` if `a` has more properties than `b`
|
|
return (result = --size > -1);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
stackA.pop();
|
|
stackB.pop();
|
|
|
|
if (initedStack) {
|
|
releaseArray(stackA);
|
|
releaseArray(stackB);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.merge` without argument juggling or support
|
|
* for `thisArg` binding.
|
|
*
|
|
* @private
|
|
* @param {Object} object The destination object.
|
|
* @param {Object} source The source object.
|
|
* @param {Function} [callback] The function to customize merging properties.
|
|
* @param {Array} [stackA=[]] Tracks traversed source objects.
|
|
* @param {Array} [stackB=[]] Associates values with source counterparts.
|
|
*/
|
|
function baseMerge(object, source, callback, stackA, stackB) {
|
|
(isArray(source) ? forEach : forOwn)(source, function(source, key) {
|
|
var found,
|
|
isArr,
|
|
result = source,
|
|
value = object[key];
|
|
|
|
if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
|
|
// avoid merging previously merged cyclic sources
|
|
var stackLength = stackA.length;
|
|
while (stackLength--) {
|
|
if ((found = stackA[stackLength] == source)) {
|
|
value = stackB[stackLength];
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
var isShallow;
|
|
if (callback) {
|
|
result = callback(value, source);
|
|
if ((isShallow = typeof result != 'undefined')) {
|
|
value = result;
|
|
}
|
|
}
|
|
if (!isShallow) {
|
|
value = isArr
|
|
? (isArray(value) ? value : [])
|
|
: (isPlainObject(value) ? value : {});
|
|
}
|
|
// add `source` and associated `value` to the stack of traversed objects
|
|
stackA.push(source);
|
|
stackB.push(value);
|
|
|
|
// recursively merge objects and arrays (susceptible to call stack limits)
|
|
if (!isShallow) {
|
|
baseMerge(value, source, callback, stackA, stackB);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (callback) {
|
|
result = callback(value, source);
|
|
if (typeof result == 'undefined') {
|
|
result = source;
|
|
}
|
|
}
|
|
if (typeof result != 'undefined') {
|
|
value = result;
|
|
}
|
|
}
|
|
object[key] = value;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.random` without argument juggling or support
|
|
* for returning floating-point numbers.
|
|
*
|
|
* @private
|
|
* @param {number} min The minimum possible value.
|
|
* @param {number} max The maximum possible value.
|
|
* @returns {number} Returns a random number.
|
|
*/
|
|
function baseRandom(min, max) {
|
|
return min + floor(nativeRandom() * (max - min + 1));
|
|
}
|
|
|
|
/**
|
|
* The base implementation of `_.uniq` without support for callback shorthands
|
|
* or `thisArg` binding.
|
|
*
|
|
* @private
|
|
* @param {Array} array The array to process.
|
|
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
|
|
* @param {Function} [callback] The function called per iteration.
|
|
* @returns {Array} Returns a duplicate-value-free array.
|
|
*/
|
|
function baseUniq(array, isSorted, callback) {
|
|
var index = -1,
|
|
indexOf = getIndexOf(),
|
|
length = array ? array.length : 0,
|
|
result = [];
|
|
|
|
var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
|
|
seen = (callback || isLarge) ? getArray() : result;
|
|
|
|
if (isLarge) {
|
|
var cache = createCache(seen);
|
|
indexOf = cacheIndexOf;
|
|
seen = cache;
|
|
}
|
|
while (++index < length) {
|
|
var value = array[index],
|
|
computed = callback ? callback(value, index, array) : value;
|
|
|
|
if (isSorted
|
|
? !index || seen[seen.length - 1] !== computed
|
|
: indexOf(seen, computed) < 0
|
|
) {
|
|
if (callback || isLarge) {
|
|
seen.push(computed);
|
|
}
|
|
result.push(value);
|
|
}
|
|
}
|
|
if (isLarge) {
|
|
releaseArray(seen.array);
|
|
releaseObject(seen);
|
|
} else if (callback) {
|
|
releaseArray(seen);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates a function that aggregates a collection, creating an object composed
|
|
* of keys generated from the results of running each element of the collection
|
|
* through a callback. The given `setter` function sets the keys and values
|
|
* of the composed object.
|
|
*
|
|
* @private
|
|
* @param {Function} setter The setter function.
|
|
* @returns {Function} Returns the new aggregator function.
|
|
*/
|
|
function createAggregator(setter) {
|
|
return function(collection, callback, thisArg) {
|
|
var result = {};
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
var value = collection[index];
|
|
setter(result, value, callback(value, index, collection), collection);
|
|
}
|
|
} else {
|
|
forOwn(collection, function(value, key, collection) {
|
|
setter(result, value, callback(value, key, collection), collection);
|
|
});
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a function that, when called, either curries or invokes `func`
|
|
* with an 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 method flags to compose.
|
|
* The bitmask may be composed of the following flags:
|
|
* 1 - `_.bind`
|
|
* 2 - `_.bindKey`
|
|
* 4 - `_.curry`
|
|
* 8 - `_.curry` (bound)
|
|
* 16 - `_.partial`
|
|
* 32 - `_.partialRight`
|
|
* @param {Array} [partialArgs] An array of arguments to prepend to those
|
|
* provided to the new function.
|
|
* @param {Array} [partialRightArgs] An array of arguments to append to those
|
|
* provided to the new function.
|
|
* @param {*} [thisArg] The `this` binding of `func`.
|
|
* @param {number} [arity] The arity of `func`.
|
|
* @returns {Function} Returns the new function.
|
|
*/
|
|
function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
|
|
var isBind = bitmask & 1,
|
|
isBindKey = bitmask & 2,
|
|
isCurry = bitmask & 4,
|
|
isCurryBound = bitmask & 8,
|
|
isPartial = bitmask & 16,
|
|
isPartialRight = bitmask & 32;
|
|
|
|
if (!isBindKey && !isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
if (isPartial && !partialArgs.length) {
|
|
bitmask &= ~16;
|
|
isPartial = partialArgs = false;
|
|
}
|
|
if (isPartialRight && !partialRightArgs.length) {
|
|
bitmask &= ~32;
|
|
isPartialRight = partialRightArgs = false;
|
|
}
|
|
var bindData = func && func.__bindData__;
|
|
if (bindData && bindData !== true) {
|
|
// clone `bindData`
|
|
bindData = slice(bindData);
|
|
if (bindData[2]) {
|
|
bindData[2] = slice(bindData[2]);
|
|
}
|
|
if (bindData[3]) {
|
|
bindData[3] = slice(bindData[3]);
|
|
}
|
|
// set `thisBinding` is not previously bound
|
|
if (isBind && !(bindData[1] & 1)) {
|
|
bindData[4] = thisArg;
|
|
}
|
|
// set if previously bound but not currently (subsequent curried functions)
|
|
if (!isBind && bindData[1] & 1) {
|
|
bitmask |= 8;
|
|
}
|
|
// set curried arity if not yet set
|
|
if (isCurry && !(bindData[1] & 4)) {
|
|
bindData[5] = arity;
|
|
}
|
|
// append partial left arguments
|
|
if (isPartial) {
|
|
push.apply(bindData[2] || (bindData[2] = []), partialArgs);
|
|
}
|
|
// append partial right arguments
|
|
if (isPartialRight) {
|
|
unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
|
|
}
|
|
// merge flags
|
|
bindData[1] |= bitmask;
|
|
return createWrapper.apply(null, bindData);
|
|
}
|
|
// fast path for `_.bind`
|
|
var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
|
|
return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
|
|
}
|
|
|
|
/**
|
|
* Used by `escape` to convert characters to HTML entities.
|
|
*
|
|
* @private
|
|
* @param {string} match The matched character to escape.
|
|
* @returns {string} Returns the escaped character.
|
|
*/
|
|
function escapeHtmlChar(match) {
|
|
return htmlEscapes[match];
|
|
}
|
|
|
|
/**
|
|
* Gets the appropriate "indexOf" function. If the `_.indexOf` method is
|
|
* customized, this method returns the custom method, otherwise it returns
|
|
* the `baseIndexOf` function.
|
|
*
|
|
* @private
|
|
* @returns {Function} Returns the "indexOf" function.
|
|
*/
|
|
function getIndexOf() {
|
|
var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a native function.
|
|
*
|
|
* @private
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
|
|
*/
|
|
function isNative(value) {
|
|
return typeof value == 'function' && reNative.test(value);
|
|
}
|
|
|
|
/**
|
|
* Sets `this` binding data on a given function.
|
|
*
|
|
* @private
|
|
* @param {Function} func The function to set data on.
|
|
* @param {Array} value The data array to set.
|
|
*/
|
|
var setBindData = !defineProperty ? noop : function(func, value) {
|
|
descriptor.value = value;
|
|
defineProperty(func, '__bindData__', descriptor);
|
|
};
|
|
|
|
/**
|
|
* A fallback implementation of `isPlainObject` which checks if a given value
|
|
* is an object created by the `Object` constructor, assuming objects created
|
|
* by the `Object` constructor have no inherited enumerable properties and that
|
|
* there are no `Object.prototype` extensions.
|
|
*
|
|
* @private
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
|
|
*/
|
|
function shimIsPlainObject(value) {
|
|
var ctor,
|
|
result;
|
|
|
|
// avoid non Object objects, `arguments` objects, and DOM elements
|
|
if (!(value && toString.call(value) == objectClass) ||
|
|
(ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
|
|
return false;
|
|
}
|
|
// 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.
|
|
forIn(value, function(value, key) {
|
|
result = key;
|
|
});
|
|
return typeof result == 'undefined' || hasOwnProperty.call(value, result);
|
|
}
|
|
|
|
/**
|
|
* Used by `unescape` to convert HTML entities to characters.
|
|
*
|
|
* @private
|
|
* @param {string} match The matched character to unescape.
|
|
* @returns {string} Returns the unescaped character.
|
|
*/
|
|
function unescapeHtmlChar(match) {
|
|
return htmlUnescapes[match];
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Checks if `value` is an `arguments` object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
|
|
* @example
|
|
*
|
|
* (function() { return _.isArguments(arguments); })(1, 2, 3);
|
|
* // => true
|
|
*
|
|
* _.isArguments([1, 2, 3]);
|
|
* // => false
|
|
*/
|
|
function isArguments(value) {
|
|
return value && typeof value == 'object' && typeof value.length == 'number' &&
|
|
toString.call(value) == argsClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is an array.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is an array, else `false`.
|
|
* @example
|
|
*
|
|
* (function() { return _.isArray(arguments); })();
|
|
* // => false
|
|
*
|
|
* _.isArray([1, 2, 3]);
|
|
* // => true
|
|
*/
|
|
var isArray = nativeIsArray || function(value) {
|
|
return value && typeof value == 'object' && typeof value.length == 'number' &&
|
|
toString.call(value) == arrayClass || false;
|
|
};
|
|
|
|
/**
|
|
* A fallback implementation of `Object.keys` which produces an array of the
|
|
* given object's own enumerable property names.
|
|
*
|
|
* @private
|
|
* @type Function
|
|
* @param {Object} object The object to inspect.
|
|
* @returns {Array} Returns an array of property names.
|
|
*/
|
|
var shimKeys = function(object) {
|
|
var index, iterable = object, result = [];
|
|
if (!iterable) return result;
|
|
if (!(objectTypes[typeof object])) return result;
|
|
for (index in iterable) {
|
|
if (hasOwnProperty.call(iterable, index)) {
|
|
result.push(index);
|
|
}
|
|
}
|
|
return result
|
|
};
|
|
|
|
/**
|
|
* Creates an array composed of the own enumerable property names of an object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to inspect.
|
|
* @returns {Array} Returns an array of property names.
|
|
* @example
|
|
*
|
|
* _.keys({ 'one': 1, 'two': 2, 'three': 3 });
|
|
* // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
|
|
*/
|
|
var keys = !nativeKeys ? shimKeys : function(object) {
|
|
if (!isObject(object)) {
|
|
return [];
|
|
}
|
|
return nativeKeys(object);
|
|
};
|
|
|
|
/**
|
|
* Used to convert characters to HTML entities:
|
|
*
|
|
* Though the `>` character is escaped for symmetry, characters like `>` and `/`
|
|
* don't require escaping in HTML and have no special meaning unless they're part
|
|
* of a tag or an unquoted attribute value.
|
|
* http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
|
|
*/
|
|
var htmlEscapes = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
};
|
|
|
|
/** Used to convert HTML entities to characters */
|
|
var htmlUnescapes = invert(htmlEscapes);
|
|
|
|
/** Used to match HTML entities and HTML characters */
|
|
var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
|
|
reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Assigns own enumerable properties of source object(s) to the destination
|
|
* object. Subsequent sources will overwrite property assignments of previous
|
|
* sources. If a callback is provided it will be executed to produce the
|
|
* assigned values. The callback is bound to `thisArg` and invoked with two
|
|
* arguments; (objectValue, sourceValue).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @alias extend
|
|
* @category Objects
|
|
* @param {Object} object The destination object.
|
|
* @param {...Object} [source] The source objects.
|
|
* @param {Function} [callback] The function to customize assigning values.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns the destination object.
|
|
* @example
|
|
*
|
|
* _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
|
|
* // => { 'name': 'fred', 'employer': 'slate' }
|
|
*
|
|
* var defaults = _.partialRight(_.assign, function(a, b) {
|
|
* return typeof a == 'undefined' ? b : a;
|
|
* });
|
|
*
|
|
* var object = { 'name': 'barney' };
|
|
* defaults(object, { 'name': 'fred', 'employer': 'slate' });
|
|
* // => { 'name': 'barney', 'employer': 'slate' }
|
|
*/
|
|
var assign = function(object, source, guard) {
|
|
var index, iterable = object, result = iterable;
|
|
if (!iterable) return result;
|
|
var args = arguments,
|
|
argsIndex = 0,
|
|
argsLength = typeof guard == 'number' ? 2 : args.length;
|
|
if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
|
|
var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
|
|
} else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
|
|
callback = args[--argsLength];
|
|
}
|
|
while (++argsIndex < argsLength) {
|
|
iterable = args[argsIndex];
|
|
if (iterable && objectTypes[typeof iterable]) {
|
|
var ownIndex = -1,
|
|
ownProps = objectTypes[typeof iterable] && keys(iterable),
|
|
length = ownProps ? ownProps.length : 0;
|
|
|
|
while (++ownIndex < length) {
|
|
index = ownProps[ownIndex];
|
|
result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
};
|
|
|
|
/**
|
|
* Creates a clone of `value`. If `isDeep` is `true` nested objects will also
|
|
* be cloned, otherwise they will be assigned by reference. If a callback
|
|
* is provided it will be executed to produce the cloned values. If the
|
|
* callback returns `undefined` cloning will be handled by the method instead.
|
|
* The callback is bound to `thisArg` and invoked with one argument; (value).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to clone.
|
|
* @param {boolean} [isDeep=false] Specify a deep clone.
|
|
* @param {Function} [callback] The function to customize cloning values.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the cloned value.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* var shallow = _.clone(characters);
|
|
* shallow[0] === characters[0];
|
|
* // => true
|
|
*
|
|
* var deep = _.clone(characters, true);
|
|
* deep[0] === characters[0];
|
|
* // => false
|
|
*
|
|
* _.mixin({
|
|
* 'clone': _.partialRight(_.clone, function(value) {
|
|
* return _.isElement(value) ? value.cloneNode(false) : undefined;
|
|
* })
|
|
* });
|
|
*
|
|
* var clone = _.clone(document.body);
|
|
* clone.childNodes.length;
|
|
* // => 0
|
|
*/
|
|
function clone(value, isDeep, callback, thisArg) {
|
|
// allows working with "Collections" methods without using their `index`
|
|
// and `collection` arguments for `isDeep` and `callback`
|
|
if (typeof isDeep != 'boolean' && isDeep != null) {
|
|
thisArg = callback;
|
|
callback = isDeep;
|
|
isDeep = false;
|
|
}
|
|
return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
|
|
}
|
|
|
|
/**
|
|
* Creates a deep clone of `value`. If a callback is provided it will be
|
|
* executed to produce the cloned values. If the callback returns `undefined`
|
|
* cloning will be handled by the method instead. The callback is bound to
|
|
* `thisArg` and invoked with one argument; (value).
|
|
*
|
|
* Note: This method is loosely based on the structured clone algorithm. Functions
|
|
* and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
|
|
* objects created by constructors other than `Object` are cloned to plain `Object` objects.
|
|
* See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to deep clone.
|
|
* @param {Function} [callback] The function to customize cloning values.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the deep cloned value.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* var deep = _.cloneDeep(characters);
|
|
* deep[0] === characters[0];
|
|
* // => false
|
|
*
|
|
* var view = {
|
|
* 'label': 'docs',
|
|
* 'node': element
|
|
* };
|
|
*
|
|
* var clone = _.cloneDeep(view, function(value) {
|
|
* return _.isElement(value) ? value.cloneNode(true) : undefined;
|
|
* });
|
|
*
|
|
* clone.node == view.node;
|
|
* // => false
|
|
*/
|
|
function cloneDeep(value, callback, thisArg) {
|
|
return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
|
|
}
|
|
|
|
/**
|
|
* Creates an object that inherits from the given `prototype` object. If a
|
|
* `properties` object is provided its own enumerable properties are assigned
|
|
* to the created object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} prototype The object to inherit from.
|
|
* @param {Object} [properties] The properties to assign to the object.
|
|
* @returns {Object} Returns the new object.
|
|
* @example
|
|
*
|
|
* function Shape() {
|
|
* this.x = 0;
|
|
* this.y = 0;
|
|
* }
|
|
*
|
|
* function Circle() {
|
|
* Shape.call(this);
|
|
* }
|
|
*
|
|
* Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
|
|
*
|
|
* var circle = new Circle;
|
|
* circle instanceof Circle;
|
|
* // => true
|
|
*
|
|
* circle instanceof Shape;
|
|
* // => true
|
|
*/
|
|
function create(prototype, properties) {
|
|
var result = baseCreate(prototype);
|
|
return properties ? assign(result, properties) : result;
|
|
}
|
|
|
|
/**
|
|
* 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 defaults of the same property will be ignored.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Objects
|
|
* @param {Object} object The destination object.
|
|
* @param {...Object} [source] The source objects.
|
|
* @param- {Object} [guard] Allows working with `_.reduce` without using its
|
|
* `key` and `object` arguments as sources.
|
|
* @returns {Object} Returns the destination object.
|
|
* @example
|
|
*
|
|
* var object = { 'name': 'barney' };
|
|
* _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
|
|
* // => { 'name': 'barney', 'employer': 'slate' }
|
|
*/
|
|
var defaults = function(object, source, guard) {
|
|
var index, iterable = object, result = iterable;
|
|
if (!iterable) return result;
|
|
var args = arguments,
|
|
argsIndex = 0,
|
|
argsLength = typeof guard == 'number' ? 2 : args.length;
|
|
while (++argsIndex < argsLength) {
|
|
iterable = args[argsIndex];
|
|
if (iterable && objectTypes[typeof iterable]) {
|
|
var ownIndex = -1,
|
|
ownProps = objectTypes[typeof iterable] && keys(iterable),
|
|
length = ownProps ? ownProps.length : 0;
|
|
|
|
while (++ownIndex < length) {
|
|
index = ownProps[ownIndex];
|
|
if (typeof result[index] == 'undefined') result[index] = iterable[index];
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
};
|
|
|
|
/**
|
|
* This method is like `_.findIndex` except that it returns the key of the
|
|
* first element that passes the callback check, instead of the element itself.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to search.
|
|
* @param {Function|Object|string} [callback=identity] The function called per
|
|
* iteration. If a property name or object is provided it will be used to
|
|
* create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
|
|
* @example
|
|
*
|
|
* var characters = {
|
|
* 'barney': { 'age': 36, 'blocked': false },
|
|
* 'fred': { 'age': 40, 'blocked': true },
|
|
* 'pebbles': { 'age': 1, 'blocked': false }
|
|
* };
|
|
*
|
|
* _.findKey(characters, function(chr) {
|
|
* return chr.age < 40;
|
|
* });
|
|
* // => 'barney' (property order is not guaranteed across environments)
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.findKey(characters, { 'age': 1 });
|
|
* // => 'pebbles'
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.findKey(characters, 'blocked');
|
|
* // => 'fred'
|
|
*/
|
|
function findKey(object, callback, thisArg) {
|
|
var result;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
forOwn(object, function(value, key, object) {
|
|
if (callback(value, key, object)) {
|
|
result = key;
|
|
return false;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.findKey` except that it iterates over elements
|
|
* of a `collection` in the opposite order.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to search.
|
|
* @param {Function|Object|string} [callback=identity] The function called per
|
|
* iteration. If a property name or object is provided it will be used to
|
|
* create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {string|undefined} Returns the key of the found element, else `undefined`.
|
|
* @example
|
|
*
|
|
* var characters = {
|
|
* 'barney': { 'age': 36, 'blocked': true },
|
|
* 'fred': { 'age': 40, 'blocked': false },
|
|
* 'pebbles': { 'age': 1, 'blocked': true }
|
|
* };
|
|
*
|
|
* _.findLastKey(characters, function(chr) {
|
|
* return chr.age < 40;
|
|
* });
|
|
* // => returns `pebbles`, assuming `_.findKey` returns `barney`
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.findLastKey(characters, { 'age': 40 });
|
|
* // => 'fred'
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.findLastKey(characters, 'blocked');
|
|
* // => 'pebbles'
|
|
*/
|
|
function findLastKey(object, callback, thisArg) {
|
|
var result;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
forOwnRight(object, function(value, key, object) {
|
|
if (callback(value, key, object)) {
|
|
result = key;
|
|
return false;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Iterates over own and inherited enumerable properties of an object,
|
|
* executing the callback for each property. The callback is bound to `thisArg`
|
|
* and invoked with three arguments; (value, key, object). Callbacks may exit
|
|
* iteration early by explicitly returning `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Objects
|
|
* @param {Object} object The object to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns `object`.
|
|
* @example
|
|
*
|
|
* function Shape() {
|
|
* this.x = 0;
|
|
* this.y = 0;
|
|
* }
|
|
*
|
|
* Shape.prototype.move = function(x, y) {
|
|
* this.x += x;
|
|
* this.y += y;
|
|
* };
|
|
*
|
|
* _.forIn(new Shape, function(value, key) {
|
|
* console.log(key);
|
|
* });
|
|
* // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
|
|
*/
|
|
var forIn = function(collection, callback, thisArg) {
|
|
var index, iterable = collection, result = iterable;
|
|
if (!iterable) return result;
|
|
if (!objectTypes[typeof iterable]) return result;
|
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
|
for (index in iterable) {
|
|
if (callback(iterable[index], index, collection) === false) return result;
|
|
}
|
|
return result
|
|
};
|
|
|
|
/**
|
|
* This method is like `_.forIn` except that it iterates over elements
|
|
* of a `collection` in the opposite order.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns `object`.
|
|
* @example
|
|
*
|
|
* function Shape() {
|
|
* this.x = 0;
|
|
* this.y = 0;
|
|
* }
|
|
*
|
|
* Shape.prototype.move = function(x, y) {
|
|
* this.x += x;
|
|
* this.y += y;
|
|
* };
|
|
*
|
|
* _.forInRight(new Shape, function(value, key) {
|
|
* console.log(key);
|
|
* });
|
|
* // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
|
|
*/
|
|
function forInRight(object, callback, thisArg) {
|
|
var pairs = [];
|
|
|
|
forIn(object, function(value, key) {
|
|
pairs.push(key, value);
|
|
});
|
|
|
|
var length = pairs.length;
|
|
callback = baseCreateCallback(callback, thisArg, 3);
|
|
while (length--) {
|
|
if (callback(pairs[length--], pairs[length], object) === false) {
|
|
break;
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Iterates over own enumerable properties of an object, executing the callback
|
|
* for each property. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, key, object). Callbacks may exit iteration early by
|
|
* explicitly returning `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Objects
|
|
* @param {Object} object The object to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns `object`.
|
|
* @example
|
|
*
|
|
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
|
|
* console.log(key);
|
|
* });
|
|
* // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
|
|
*/
|
|
var forOwn = function(collection, callback, thisArg) {
|
|
var index, iterable = collection, result = iterable;
|
|
if (!iterable) return result;
|
|
if (!objectTypes[typeof iterable]) return result;
|
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
|
var ownIndex = -1,
|
|
ownProps = objectTypes[typeof iterable] && keys(iterable),
|
|
length = ownProps ? ownProps.length : 0;
|
|
|
|
while (++ownIndex < length) {
|
|
index = ownProps[ownIndex];
|
|
if (callback(iterable[index], index, collection) === false) return result;
|
|
}
|
|
return result
|
|
};
|
|
|
|
/**
|
|
* This method is like `_.forOwn` except that it iterates over elements
|
|
* of a `collection` in the opposite order.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns `object`.
|
|
* @example
|
|
*
|
|
* _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
|
|
* console.log(key);
|
|
* });
|
|
* // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
|
|
*/
|
|
function forOwnRight(object, callback, thisArg) {
|
|
var props = keys(object),
|
|
length = props.length;
|
|
|
|
callback = baseCreateCallback(callback, thisArg, 3);
|
|
while (length--) {
|
|
var key = props[length];
|
|
if (callback(object[key], key, object) === false) {
|
|
break;
|
|
}
|
|
}
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Creates a sorted array of property names of all enumerable properties,
|
|
* own and inherited, of `object` that have function values.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias methods
|
|
* @category Objects
|
|
* @param {Object} object The object to inspect.
|
|
* @returns {Array} Returns an array of property names that have function values.
|
|
* @example
|
|
*
|
|
* _.functions(_);
|
|
* // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
|
|
*/
|
|
function functions(object) {
|
|
var result = [];
|
|
forIn(object, function(value, key) {
|
|
if (isFunction(value)) {
|
|
result.push(key);
|
|
}
|
|
});
|
|
return result.sort();
|
|
}
|
|
|
|
/**
|
|
* Checks if the specified property name exists as a direct property of `object`,
|
|
* instead of an inherited property.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to inspect.
|
|
* @param {string} key The name of the property to check.
|
|
* @returns {boolean} Returns `true` if key is a direct property, else `false`.
|
|
* @example
|
|
*
|
|
* _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
|
|
* // => true
|
|
*/
|
|
function has(object, key) {
|
|
return object ? hasOwnProperty.call(object, key) : false;
|
|
}
|
|
|
|
/**
|
|
* Creates an object composed of the inverted keys and values of the given object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to invert.
|
|
* @returns {Object} Returns the created inverted object.
|
|
* @example
|
|
*
|
|
* _.invert({ 'first': 'fred', 'second': 'barney' });
|
|
* // => { 'fred': 'first', 'barney': 'second' }
|
|
*/
|
|
function invert(object) {
|
|
var index = -1,
|
|
props = keys(object),
|
|
length = props.length,
|
|
result = {};
|
|
|
|
while (++index < length) {
|
|
var key = props[index];
|
|
result[object[key]] = key;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a boolean value.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
|
|
* @example
|
|
*
|
|
* _.isBoolean(null);
|
|
* // => false
|
|
*/
|
|
function isBoolean(value) {
|
|
return value === true || value === false ||
|
|
value && typeof value == 'object' && toString.call(value) == boolClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a date.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a date, else `false`.
|
|
* @example
|
|
*
|
|
* _.isDate(new Date);
|
|
* // => true
|
|
*/
|
|
function isDate(value) {
|
|
return value && typeof value == 'object' && toString.call(value) == dateClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a DOM element.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
|
|
* @example
|
|
*
|
|
* _.isElement(document.body);
|
|
* // => true
|
|
*/
|
|
function isElement(value) {
|
|
return value && value.nodeType === 1 || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
|
|
* length of `0` and objects with no own enumerable properties are considered
|
|
* "empty".
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Array|Object|string} value The value to inspect.
|
|
* @returns {boolean} Returns `true` if the `value` is empty, else `false`.
|
|
* @example
|
|
*
|
|
* _.isEmpty([1, 2, 3]);
|
|
* // => false
|
|
*
|
|
* _.isEmpty({});
|
|
* // => true
|
|
*
|
|
* _.isEmpty('');
|
|
* // => true
|
|
*/
|
|
function isEmpty(value) {
|
|
var result = true;
|
|
if (!value) {
|
|
return result;
|
|
}
|
|
var className = toString.call(value),
|
|
length = value.length;
|
|
|
|
if ((className == arrayClass || className == stringClass || className == argsClass ) ||
|
|
(className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
|
|
return !length;
|
|
}
|
|
forOwn(value, function() {
|
|
return (result = false);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Performs a deep comparison between two values to determine if they are
|
|
* equivalent to each other. If a callback is provided it will be executed
|
|
* to compare values. If the callback returns `undefined` comparisons will
|
|
* be handled by the method instead. The callback is bound to `thisArg` and
|
|
* invoked with two arguments; (a, b).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} a The value to compare.
|
|
* @param {*} b The other value to compare.
|
|
* @param {Function} [callback] The function to customize comparing values.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
|
* @example
|
|
*
|
|
* var object = { 'name': 'fred' };
|
|
* var copy = { 'name': 'fred' };
|
|
*
|
|
* object == copy;
|
|
* // => false
|
|
*
|
|
* _.isEqual(object, copy);
|
|
* // => true
|
|
*
|
|
* var words = ['hello', 'goodbye'];
|
|
* var otherWords = ['hi', 'goodbye'];
|
|
*
|
|
* _.isEqual(words, otherWords, function(a, b) {
|
|
* var reGreet = /^(?:hello|hi)$/i,
|
|
* aGreet = _.isString(a) && reGreet.test(a),
|
|
* bGreet = _.isString(b) && reGreet.test(b);
|
|
*
|
|
* return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
|
|
* });
|
|
* // => true
|
|
*/
|
|
function isEqual(a, b, callback, thisArg) {
|
|
return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is, or can be coerced to, a finite number.
|
|
*
|
|
* Note: This is not the same as native `isFinite` which will return true for
|
|
* booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is finite, else `false`.
|
|
* @example
|
|
*
|
|
* _.isFinite(-101);
|
|
* // => true
|
|
*
|
|
* _.isFinite('10');
|
|
* // => true
|
|
*
|
|
* _.isFinite(true);
|
|
* // => false
|
|
*
|
|
* _.isFinite('');
|
|
* // => false
|
|
*
|
|
* _.isFinite(Infinity);
|
|
* // => false
|
|
*/
|
|
function isFinite(value) {
|
|
return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a function, else `false`.
|
|
* @example
|
|
*
|
|
* _.isFunction(_);
|
|
* // => true
|
|
*/
|
|
function isFunction(value) {
|
|
return typeof value == 'function';
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is the language type of Object.
|
|
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is an object, else `false`.
|
|
* @example
|
|
*
|
|
* _.isObject({});
|
|
* // => true
|
|
*
|
|
* _.isObject([1, 2, 3]);
|
|
* // => true
|
|
*
|
|
* _.isObject(1);
|
|
* // => false
|
|
*/
|
|
function isObject(value) {
|
|
// check if the value is the ECMAScript language type of Object
|
|
// http://es5.github.io/#x8
|
|
// and avoid a V8 bug
|
|
// http://code.google.com/p/v8/issues/detail?id=2291
|
|
return !!(value && objectTypes[typeof value]);
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is `NaN`.
|
|
*
|
|
* Note: This is not the same as native `isNaN` which will return `true` for
|
|
* `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
|
|
* @example
|
|
*
|
|
* _.isNaN(NaN);
|
|
* // => true
|
|
*
|
|
* _.isNaN(new Number(NaN));
|
|
* // => true
|
|
*
|
|
* isNaN(undefined);
|
|
* // => true
|
|
*
|
|
* _.isNaN(undefined);
|
|
* // => false
|
|
*/
|
|
function isNaN(value) {
|
|
// `NaN` as a primitive is the only value that is not equal to itself
|
|
// (perform the [[Class]] check first to avoid errors with some host objects in IE)
|
|
return isNumber(value) && value != +value;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is `null`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
|
|
* @example
|
|
*
|
|
* _.isNull(null);
|
|
* // => true
|
|
*
|
|
* _.isNull(undefined);
|
|
* // => false
|
|
*/
|
|
function isNull(value) {
|
|
return value === null;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a number.
|
|
*
|
|
* Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a number, else `false`.
|
|
* @example
|
|
*
|
|
* _.isNumber(8.4 * 5);
|
|
* // => true
|
|
*/
|
|
function isNumber(value) {
|
|
return typeof value == 'number' ||
|
|
value && typeof value == 'object' && toString.call(value) == numberClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is an object created by the `Object` constructor.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
|
|
* @example
|
|
*
|
|
* function Shape() {
|
|
* this.x = 0;
|
|
* this.y = 0;
|
|
* }
|
|
*
|
|
* _.isPlainObject(new Shape);
|
|
* // => false
|
|
*
|
|
* _.isPlainObject([1, 2, 3]);
|
|
* // => false
|
|
*
|
|
* _.isPlainObject({ 'x': 0, 'y': 0 });
|
|
* // => true
|
|
*/
|
|
var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
|
|
if (!(value && toString.call(value) == objectClass)) {
|
|
return false;
|
|
}
|
|
var valueOf = value.valueOf,
|
|
objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
|
|
|
|
return objProto
|
|
? (value == objProto || getPrototypeOf(value) == objProto)
|
|
: shimIsPlainObject(value);
|
|
};
|
|
|
|
/**
|
|
* Checks if `value` is a regular expression.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
|
|
* @example
|
|
*
|
|
* _.isRegExp(/fred/);
|
|
* // => true
|
|
*/
|
|
function isRegExp(value) {
|
|
return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is a string.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is a string, else `false`.
|
|
* @example
|
|
*
|
|
* _.isString('fred');
|
|
* // => true
|
|
*/
|
|
function isString(value) {
|
|
return typeof value == 'string' ||
|
|
value && typeof value == 'object' && toString.call(value) == stringClass || false;
|
|
}
|
|
|
|
/**
|
|
* Checks if `value` is `undefined`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {*} value The value to check.
|
|
* @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
|
|
* @example
|
|
*
|
|
* _.isUndefined(void 0);
|
|
* // => true
|
|
*/
|
|
function isUndefined(value) {
|
|
return typeof value == 'undefined';
|
|
}
|
|
|
|
/**
|
|
* Creates an object with the same keys as `object` and values generated by
|
|
* running each own enumerable property of `object` through the callback.
|
|
* The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, key, object).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new object with values of the results of each `callback` execution.
|
|
* @example
|
|
*
|
|
* _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
|
|
* // => { 'a': 3, 'b': 6, 'c': 9 }
|
|
*
|
|
* var characters = {
|
|
* 'fred': { 'name': 'fred', 'age': 40 },
|
|
* 'pebbles': { 'name': 'pebbles', 'age': 1 }
|
|
* };
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.mapValues(characters, 'age');
|
|
* // => { 'fred': 40, 'pebbles': 1 }
|
|
*/
|
|
function mapValues(object, callback, thisArg) {
|
|
var result = {};
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
forOwn(object, function(value, key, object) {
|
|
result[key] = callback(value, key, object);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Recursively merges own enumerable properties of the source object(s), that
|
|
* don't resolve to `undefined` into the destination object. Subsequent sources
|
|
* will overwrite property assignments of previous sources. If a callback is
|
|
* provided it will be executed to produce the merged values of the destination
|
|
* and source properties. If the callback returns `undefined` merging will
|
|
* be handled by the method instead. The callback is bound to `thisArg` and
|
|
* invoked with two arguments; (objectValue, sourceValue).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The destination object.
|
|
* @param {...Object} [source] The source objects.
|
|
* @param {Function} [callback] The function to customize merging properties.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns the destination object.
|
|
* @example
|
|
*
|
|
* var names = {
|
|
* 'characters': [
|
|
* { 'name': 'barney' },
|
|
* { 'name': 'fred' }
|
|
* ]
|
|
* };
|
|
*
|
|
* var ages = {
|
|
* 'characters': [
|
|
* { 'age': 36 },
|
|
* { 'age': 40 }
|
|
* ]
|
|
* };
|
|
*
|
|
* _.merge(names, ages);
|
|
* // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
|
|
*
|
|
* var food = {
|
|
* 'fruits': ['apple'],
|
|
* 'vegetables': ['beet']
|
|
* };
|
|
*
|
|
* var otherFood = {
|
|
* 'fruits': ['banana'],
|
|
* 'vegetables': ['carrot']
|
|
* };
|
|
*
|
|
* _.merge(food, otherFood, function(a, b) {
|
|
* return _.isArray(a) ? a.concat(b) : undefined;
|
|
* });
|
|
* // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
|
|
*/
|
|
function merge(object) {
|
|
var args = arguments,
|
|
length = 2;
|
|
|
|
if (!isObject(object)) {
|
|
return object;
|
|
}
|
|
// allows working with `_.reduce` and `_.reduceRight` without using
|
|
// their `index` and `collection` arguments
|
|
if (typeof args[2] != 'number') {
|
|
length = args.length;
|
|
}
|
|
if (length > 3 && typeof args[length - 2] == 'function') {
|
|
var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
|
|
} else if (length > 2 && typeof args[length - 1] == 'function') {
|
|
callback = args[--length];
|
|
}
|
|
var sources = slice(arguments, 1, length),
|
|
index = -1,
|
|
stackA = getArray(),
|
|
stackB = getArray();
|
|
|
|
while (++index < length) {
|
|
baseMerge(object, sources[index], callback, stackA, stackB);
|
|
}
|
|
releaseArray(stackA);
|
|
releaseArray(stackB);
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Creates a shallow clone of `object` excluding the specified properties.
|
|
* Property names may be specified as individual arguments or as arrays of
|
|
* property names. If a callback is provided it will be executed for each
|
|
* property of `object` omitting the properties the callback returns truey
|
|
* for. The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, key, object).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The source object.
|
|
* @param {Function|...string|string[]} [callback] The properties to omit or the
|
|
* function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns an object without the omitted properties.
|
|
* @example
|
|
*
|
|
* _.omit({ 'name': 'fred', 'age': 40 }, 'age');
|
|
* // => { 'name': 'fred' }
|
|
*
|
|
* _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
|
|
* return typeof value == 'number';
|
|
* });
|
|
* // => { 'name': 'fred' }
|
|
*/
|
|
function omit(object, callback, thisArg) {
|
|
var result = {};
|
|
if (typeof callback != 'function') {
|
|
var props = [];
|
|
forIn(object, function(value, key) {
|
|
props.push(key);
|
|
});
|
|
props = baseDifference(props, baseFlatten(arguments, true, false, 1));
|
|
|
|
var index = -1,
|
|
length = props.length;
|
|
|
|
while (++index < length) {
|
|
var key = props[index];
|
|
result[key] = object[key];
|
|
}
|
|
} else {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
forIn(object, function(value, key, object) {
|
|
if (!callback(value, key, object)) {
|
|
result[key] = value;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates a two dimensional array of an object's key-value pairs,
|
|
* i.e. `[[key1, value1], [key2, value2]]`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to inspect.
|
|
* @returns {Array} Returns new array of key-value pairs.
|
|
* @example
|
|
*
|
|
* _.pairs({ 'barney': 36, 'fred': 40 });
|
|
* // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
|
|
*/
|
|
function pairs(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 a shallow clone of `object` composed of the specified properties.
|
|
* Property names may be specified as individual arguments or as arrays of
|
|
* property names. If a callback is provided it will be executed for each
|
|
* property of `object` picking the properties the callback returns truey
|
|
* for. The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, key, object).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The source object.
|
|
* @param {Function|...string|string[]} [callback] The function called per
|
|
* iteration or property names to pick, specified as individual property
|
|
* names or arrays of property names.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns an object composed of the picked properties.
|
|
* @example
|
|
*
|
|
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
|
|
* // => { 'name': 'fred' }
|
|
*
|
|
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
|
|
* return key.charAt(0) != '_';
|
|
* });
|
|
* // => { 'name': 'fred' }
|
|
*/
|
|
function pick(object, callback, thisArg) {
|
|
var result = {};
|
|
if (typeof callback != 'function') {
|
|
var index = -1,
|
|
props = baseFlatten(arguments, true, false, 1),
|
|
length = isObject(object) ? props.length : 0;
|
|
|
|
while (++index < length) {
|
|
var key = props[index];
|
|
if (key in object) {
|
|
result[key] = object[key];
|
|
}
|
|
}
|
|
} else {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
forIn(object, function(value, key, object) {
|
|
if (callback(value, key, object)) {
|
|
result[key] = value;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* An alternative to `_.reduce` this method transforms `object` to a new
|
|
* `accumulator` object which is the result of running each of its own
|
|
* enumerable properties through a callback, with each callback execution
|
|
* potentially mutating the `accumulator` object. The callback is bound to
|
|
* `thisArg` and invoked with four arguments; (accumulator, value, key, object).
|
|
* Callbacks may exit iteration early by explicitly returning `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Array|Object} object The object to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [accumulator] The custom accumulator value.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the accumulated value.
|
|
* @example
|
|
*
|
|
* var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
|
|
* num *= num;
|
|
* if (num % 2) {
|
|
* return result.push(num) < 3;
|
|
* }
|
|
* });
|
|
* // => [1, 9, 25]
|
|
*
|
|
* var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
|
|
* result[key] = num * 3;
|
|
* });
|
|
* // => { 'a': 3, 'b': 6, 'c': 9 }
|
|
*/
|
|
function transform(object, callback, accumulator, thisArg) {
|
|
var isArr = isArray(object);
|
|
if (accumulator == null) {
|
|
if (isArr) {
|
|
accumulator = [];
|
|
} else {
|
|
var ctor = object && object.constructor,
|
|
proto = ctor && ctor.prototype;
|
|
|
|
accumulator = baseCreate(proto);
|
|
}
|
|
}
|
|
if (callback) {
|
|
callback = lodash.createCallback(callback, thisArg, 4);
|
|
(isArr ? forEach : forOwn)(object, function(value, index, object) {
|
|
return callback(accumulator, value, index, object);
|
|
});
|
|
}
|
|
return accumulator;
|
|
}
|
|
|
|
/**
|
|
* Creates an array composed of the own enumerable property values of `object`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Objects
|
|
* @param {Object} object The object to inspect.
|
|
* @returns {Array} Returns an array of property values.
|
|
* @example
|
|
*
|
|
* _.values({ 'one': 1, 'two': 2, 'three': 3 });
|
|
* // => [1, 2, 3] (property order is not guaranteed across environments)
|
|
*/
|
|
function values(object) {
|
|
var index = -1,
|
|
props = keys(object),
|
|
length = props.length,
|
|
result = Array(length);
|
|
|
|
while (++index < length) {
|
|
result[index] = object[props[index]];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates an array of elements from the specified indexes, or keys, of the
|
|
* `collection`. Indexes may be specified as individual arguments or as arrays
|
|
* of indexes.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
|
|
* to retrieve, specified as individual indexes or arrays of indexes.
|
|
* @returns {Array} Returns a new array of elements corresponding to the
|
|
* provided indexes.
|
|
* @example
|
|
*
|
|
* _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
|
|
* // => ['a', 'c', 'e']
|
|
*
|
|
* _.at(['fred', 'barney', 'pebbles'], 0, 2);
|
|
* // => ['fred', 'pebbles']
|
|
*/
|
|
function at(collection) {
|
|
var args = arguments,
|
|
index = -1,
|
|
props = baseFlatten(args, true, false, 1),
|
|
length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
|
|
result = Array(length);
|
|
|
|
while(++index < length) {
|
|
result[index] = collection[props[index]];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if a given value is present in a collection using strict equality
|
|
* for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
|
|
* offset from the end of the collection.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias include
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {*} target The value to check for.
|
|
* @param {number} [fromIndex=0] The index to search from.
|
|
* @returns {boolean} Returns `true` if the `target` element is found, else `false`.
|
|
* @example
|
|
*
|
|
* _.contains([1, 2, 3], 1);
|
|
* // => true
|
|
*
|
|
* _.contains([1, 2, 3], 1, 2);
|
|
* // => false
|
|
*
|
|
* _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
|
|
* // => true
|
|
*
|
|
* _.contains('pebbles', 'eb');
|
|
* // => true
|
|
*/
|
|
function contains(collection, target, fromIndex) {
|
|
var index = -1,
|
|
indexOf = getIndexOf(),
|
|
length = collection ? collection.length : 0,
|
|
result = false;
|
|
|
|
fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
|
|
if (isArray(collection)) {
|
|
result = indexOf(collection, target, fromIndex) > -1;
|
|
} else if (typeof length == 'number') {
|
|
result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
|
|
} else {
|
|
forOwn(collection, function(value) {
|
|
if (++index >= fromIndex) {
|
|
return !(result = value === target);
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an object composed of keys generated from the results of running
|
|
* each element of `collection` through the callback. The corresponding value
|
|
* of each key is the number of times the key was returned by the callback.
|
|
* The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns the composed aggregate object.
|
|
* @example
|
|
*
|
|
* _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
|
|
* // => { '4': 1, '6': 2 }
|
|
*
|
|
* _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
|
|
* // => { '4': 1, '6': 2 }
|
|
*
|
|
* _.countBy(['one', 'two', 'three'], 'length');
|
|
* // => { '3': 2, '5': 1 }
|
|
*/
|
|
var countBy = createAggregator(function(result, value, key) {
|
|
(hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
|
|
});
|
|
|
|
/**
|
|
* Checks if the given callback returns truey value for **all** elements of
|
|
* a collection. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias all
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {boolean} Returns `true` if all elements passed the callback check,
|
|
* else `false`.
|
|
* @example
|
|
*
|
|
* _.every([true, 1, null, 'yes']);
|
|
* // => false
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.every(characters, 'age');
|
|
* // => true
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.every(characters, { 'age': 36 });
|
|
* // => false
|
|
*/
|
|
function every(collection, callback, thisArg) {
|
|
var result = true;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
if (!(result = !!callback(collection[index], index, collection))) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
forOwn(collection, function(value, index, collection) {
|
|
return (result = !!callback(value, index, collection));
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Iterates over elements of a collection, returning an array of all elements
|
|
* the callback returns truey for. The callback is bound to `thisArg` and
|
|
* invoked with three arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias select
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new array of elements that passed the callback check.
|
|
* @example
|
|
*
|
|
* var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
|
|
* // => [2, 4, 6]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': false },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': true }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.filter(characters, 'blocked');
|
|
* // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.filter(characters, { 'age': 36 });
|
|
* // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
|
|
*/
|
|
function filter(collection, callback, thisArg) {
|
|
var result = [];
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
var value = collection[index];
|
|
if (callback(value, index, collection)) {
|
|
result.push(value);
|
|
}
|
|
}
|
|
} else {
|
|
forOwn(collection, function(value, index, collection) {
|
|
if (callback(value, index, collection)) {
|
|
result.push(value);
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Iterates over elements of a collection, returning the first element that
|
|
* the callback returns truey for. The callback is bound to `thisArg` and
|
|
* invoked with three arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias detect, findWhere
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the found element, else `undefined`.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': false },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': true },
|
|
* { 'name': 'pebbles', 'age': 1, 'blocked': false }
|
|
* ];
|
|
*
|
|
* _.find(characters, function(chr) {
|
|
* return chr.age < 40;
|
|
* });
|
|
* // => { 'name': 'barney', 'age': 36, 'blocked': false }
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.find(characters, { 'age': 1 });
|
|
* // => { 'name': 'pebbles', 'age': 1, 'blocked': false }
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.find(characters, 'blocked');
|
|
* // => { 'name': 'fred', 'age': 40, 'blocked': true }
|
|
*/
|
|
function find(collection, callback, thisArg) {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
var value = collection[index];
|
|
if (callback(value, index, collection)) {
|
|
return value;
|
|
}
|
|
}
|
|
} else {
|
|
var result;
|
|
forOwn(collection, function(value, index, collection) {
|
|
if (callback(value, index, collection)) {
|
|
result = value;
|
|
return false;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.find` except that it iterates over elements
|
|
* of a `collection` from right to left.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the found element, else `undefined`.
|
|
* @example
|
|
*
|
|
* _.findLast([1, 2, 3, 4], function(num) {
|
|
* return num % 2 == 1;
|
|
* });
|
|
* // => 3
|
|
*/
|
|
function findLast(collection, callback, thisArg) {
|
|
var result;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
forEachRight(collection, function(value, index, collection) {
|
|
if (callback(value, index, collection)) {
|
|
result = value;
|
|
return false;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Iterates over elements of a collection, executing the callback for each
|
|
* element. The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, index|key, collection). Callbacks 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 Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array|Object|string} Returns `collection`.
|
|
* @example
|
|
*
|
|
* _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
|
|
* // => logs each number and returns '1,2,3'
|
|
*
|
|
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
|
|
* // => logs each number and returns the object (property order is not guaranteed across environments)
|
|
*/
|
|
function forEach(collection, callback, thisArg) {
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
if (callback(collection[index], index, collection) === false) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
forOwn(collection, callback);
|
|
}
|
|
return collection;
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.forEach` except that it iterates over elements
|
|
* of a `collection` from right to left.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias eachRight
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array|Object|string} Returns `collection`.
|
|
* @example
|
|
*
|
|
* _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
|
|
* // => logs each number from right to left and returns '3,2,1'
|
|
*/
|
|
function forEachRight(collection, callback, thisArg) {
|
|
var length = collection ? collection.length : 0;
|
|
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
|
|
if (typeof length == 'number') {
|
|
while (length--) {
|
|
if (callback(collection[length], length, collection) === false) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
var props = keys(collection);
|
|
length = props.length;
|
|
forOwn(collection, function(value, key, collection) {
|
|
key = props ? props[--length] : --length;
|
|
return callback(collection[key], key, collection);
|
|
});
|
|
}
|
|
return collection;
|
|
}
|
|
|
|
/**
|
|
* Creates an object composed of keys generated from the results of running
|
|
* each element of a collection through the callback. The corresponding value
|
|
* of each key is an array of the elements responsible for generating the key.
|
|
* The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns the composed aggregate object.
|
|
* @example
|
|
*
|
|
* _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
|
|
* // => { '4': [4.2], '6': [6.1, 6.4] }
|
|
*
|
|
* _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
|
|
* // => { '4': [4.2], '6': [6.1, 6.4] }
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.groupBy(['one', 'two', 'three'], 'length');
|
|
* // => { '3': ['one', 'two'], '5': ['three'] }
|
|
*/
|
|
var groupBy = createAggregator(function(result, value, key) {
|
|
(hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
|
|
});
|
|
|
|
/**
|
|
* Creates an object composed of keys generated from the results of running
|
|
* each element of the collection through the given callback. The corresponding
|
|
* value of each key is the last element responsible for generating the key.
|
|
* The callback is bound to `thisArg` and invoked with three arguments;
|
|
* (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Object} Returns the composed aggregate object.
|
|
* @example
|
|
*
|
|
* var keys = [
|
|
* { 'dir': 'left', 'code': 97 },
|
|
* { 'dir': 'right', 'code': 100 }
|
|
* ];
|
|
*
|
|
* _.indexBy(keys, 'dir');
|
|
* // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
|
|
*
|
|
* _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
|
|
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
|
|
*
|
|
* _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
|
|
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
|
|
*/
|
|
var indexBy = createAggregator(function(result, value, key) {
|
|
result[key] = value;
|
|
});
|
|
|
|
/**
|
|
* Invokes the method named by `methodName` on each element in the `collection`
|
|
* returning an array of the results of each invoked method. Additional arguments
|
|
* will be provided to each invoked method. If `methodName` is a function it
|
|
* will be invoked for, and `this` bound to, each element in the `collection`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|string} methodName The name of the method to invoke or
|
|
* the function invoked per iteration.
|
|
* @param {...*} [arg] Arguments to invoke the method with.
|
|
* @returns {Array} Returns a new array of the results of each invoked method.
|
|
* @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']]
|
|
*/
|
|
function invoke(collection, methodName) {
|
|
var args = slice(arguments, 2),
|
|
index = -1,
|
|
isFunc = typeof methodName == 'function',
|
|
length = collection ? collection.length : 0,
|
|
result = Array(typeof length == 'number' ? length : 0);
|
|
|
|
forEach(collection, function(value) {
|
|
result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of values by running each element in the collection
|
|
* through the callback. The callback is bound to `thisArg` and invoked with
|
|
* three arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias collect
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new array of the results of each `callback` execution.
|
|
* @example
|
|
*
|
|
* _.map([1, 2, 3], function(num) { return num * 3; });
|
|
* // => [3, 6, 9]
|
|
*
|
|
* _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
|
|
* // => [3, 6, 9] (property order is not guaranteed across environments)
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.map(characters, 'name');
|
|
* // => ['barney', 'fred']
|
|
*/
|
|
function map(collection, callback, thisArg) {
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
if (typeof length == 'number') {
|
|
var result = Array(length);
|
|
while (++index < length) {
|
|
result[index] = callback(collection[index], index, collection);
|
|
}
|
|
} else {
|
|
result = [];
|
|
forOwn(collection, function(value, key, collection) {
|
|
result[++index] = callback(value, key, collection);
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the maximum value of a collection. If the collection is empty or
|
|
* falsey `-Infinity` is returned. If a callback is provided it will be executed
|
|
* for each value in the collection to generate the criterion by which the value
|
|
* is ranked. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, index, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the maximum value.
|
|
* @example
|
|
*
|
|
* _.max([4, 2, 8, 6]);
|
|
* // => 8
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* _.max(characters, function(chr) { return chr.age; });
|
|
* // => { 'name': 'fred', 'age': 40 };
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.max(characters, 'age');
|
|
* // => { 'name': 'fred', 'age': 40 };
|
|
*/
|
|
function max(collection, callback, thisArg) {
|
|
var computed = -Infinity,
|
|
result = computed;
|
|
|
|
// allows working with functions like `_.map` without using
|
|
// their `index` argument as a callback
|
|
if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
|
|
callback = null;
|
|
}
|
|
if (callback == null && isArray(collection)) {
|
|
var index = -1,
|
|
length = collection.length;
|
|
|
|
while (++index < length) {
|
|
var value = collection[index];
|
|
if (value > result) {
|
|
result = value;
|
|
}
|
|
}
|
|
} else {
|
|
callback = (callback == null && isString(collection))
|
|
? charAtCallback
|
|
: lodash.createCallback(callback, thisArg, 3);
|
|
|
|
forEach(collection, function(value, index, collection) {
|
|
var current = callback(value, index, collection);
|
|
if (current > computed) {
|
|
computed = current;
|
|
result = value;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the minimum value of a collection. If the collection is empty or
|
|
* falsey `Infinity` is returned. If a callback is provided it will be executed
|
|
* for each value in the collection to generate the criterion by which the value
|
|
* is ranked. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, index, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the minimum value.
|
|
* @example
|
|
*
|
|
* _.min([4, 2, 8, 6]);
|
|
* // => 2
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* _.min(characters, function(chr) { return chr.age; });
|
|
* // => { 'name': 'barney', 'age': 36 };
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.min(characters, 'age');
|
|
* // => { 'name': 'barney', 'age': 36 };
|
|
*/
|
|
function min(collection, callback, thisArg) {
|
|
var computed = Infinity,
|
|
result = computed;
|
|
|
|
// allows working with functions like `_.map` without using
|
|
// their `index` argument as a callback
|
|
if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
|
|
callback = null;
|
|
}
|
|
if (callback == null && isArray(collection)) {
|
|
var index = -1,
|
|
length = collection.length;
|
|
|
|
while (++index < length) {
|
|
var value = collection[index];
|
|
if (value < result) {
|
|
result = value;
|
|
}
|
|
}
|
|
} else {
|
|
callback = (callback == null && isString(collection))
|
|
? charAtCallback
|
|
: lodash.createCallback(callback, thisArg, 3);
|
|
|
|
forEach(collection, function(value, index, collection) {
|
|
var current = callback(value, index, collection);
|
|
if (current < computed) {
|
|
computed = current;
|
|
result = value;
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the value of a specified property from all elements in the collection.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {string} property The name of the property to pluck.
|
|
* @returns {Array} Returns a new array of property values.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* _.pluck(characters, 'name');
|
|
* // => ['barney', 'fred']
|
|
*/
|
|
var pluck = map;
|
|
|
|
/**
|
|
* Reduces a collection to a value which is the accumulated result of running
|
|
* each element in the collection through the callback, where each successive
|
|
* callback execution consumes the return value of the previous execution. If
|
|
* `accumulator` is not provided the first element of the collection will be
|
|
* used as the initial `accumulator` value. The callback is bound to `thisArg`
|
|
* and invoked with four arguments; (accumulator, value, index|key, collection).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias foldl, inject
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [accumulator] Initial value of the accumulator.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the accumulated value.
|
|
* @example
|
|
*
|
|
* var sum = _.reduce([1, 2, 3], function(sum, num) {
|
|
* return sum + num;
|
|
* });
|
|
* // => 6
|
|
*
|
|
* var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
|
|
* result[key] = num * 3;
|
|
* return result;
|
|
* }, {});
|
|
* // => { 'a': 3, 'b': 6, 'c': 9 }
|
|
*/
|
|
function reduce(collection, callback, accumulator, thisArg) {
|
|
if (!collection) return accumulator;
|
|
var noaccum = arguments.length < 3;
|
|
callback = lodash.createCallback(callback, thisArg, 4);
|
|
|
|
var index = -1,
|
|
length = collection.length;
|
|
|
|
if (typeof length == 'number') {
|
|
if (noaccum) {
|
|
accumulator = collection[++index];
|
|
}
|
|
while (++index < length) {
|
|
accumulator = callback(accumulator, collection[index], index, collection);
|
|
}
|
|
} else {
|
|
forOwn(collection, function(value, index, collection) {
|
|
accumulator = noaccum
|
|
? (noaccum = false, value)
|
|
: callback(accumulator, value, index, collection)
|
|
});
|
|
}
|
|
return accumulator;
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.reduce` except that it iterates over elements
|
|
* of a `collection` from right to left.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias foldr
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function} [callback=identity] The function called per iteration.
|
|
* @param {*} [accumulator] Initial value of the accumulator.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the accumulated value.
|
|
* @example
|
|
*
|
|
* var list = [[0, 1], [2, 3], [4, 5]];
|
|
* var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
|
|
* // => [4, 5, 2, 3, 0, 1]
|
|
*/
|
|
function reduceRight(collection, callback, accumulator, thisArg) {
|
|
var noaccum = arguments.length < 3;
|
|
callback = lodash.createCallback(callback, thisArg, 4);
|
|
forEachRight(collection, function(value, index, collection) {
|
|
accumulator = noaccum
|
|
? (noaccum = false, value)
|
|
: callback(accumulator, value, index, collection);
|
|
});
|
|
return accumulator;
|
|
}
|
|
|
|
/**
|
|
* The opposite of `_.filter` this method returns the elements of a
|
|
* collection that the callback does **not** return truey for.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new array of elements that failed the callback check.
|
|
* @example
|
|
*
|
|
* var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
|
|
* // => [1, 3, 5]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': false },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': true }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.reject(characters, 'blocked');
|
|
* // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.reject(characters, { 'age': 36 });
|
|
* // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
|
|
*/
|
|
function reject(collection, callback, thisArg) {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
return filter(collection, function(value, index, collection) {
|
|
return !callback(value, index, collection);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Retrieves a random element or `n` random elements from a collection.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to sample.
|
|
* @param {number} [n] The number of elements to sample.
|
|
* @param- {Object} [guard] Allows working with functions like `_.map`
|
|
* without using their `index` arguments as `n`.
|
|
* @returns {Array} Returns the random sample(s) of `collection`.
|
|
* @example
|
|
*
|
|
* _.sample([1, 2, 3, 4]);
|
|
* // => 2
|
|
*
|
|
* _.sample([1, 2, 3, 4], 2);
|
|
* // => [3, 1]
|
|
*/
|
|
function sample(collection, n, guard) {
|
|
if (collection && typeof collection.length != 'number') {
|
|
collection = values(collection);
|
|
}
|
|
if (n == null || guard) {
|
|
return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
|
|
}
|
|
var result = shuffle(collection);
|
|
result.length = nativeMin(nativeMax(0, n), result.length);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of shuffled values, using a version of the Fisher-Yates
|
|
* shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to shuffle.
|
|
* @returns {Array} Returns a new shuffled collection.
|
|
* @example
|
|
*
|
|
* _.shuffle([1, 2, 3, 4, 5, 6]);
|
|
* // => [4, 1, 6, 3, 5, 2]
|
|
*/
|
|
function shuffle(collection) {
|
|
var index = -1,
|
|
length = collection ? collection.length : 0,
|
|
result = Array(typeof length == 'number' ? length : 0);
|
|
|
|
forEach(collection, function(value) {
|
|
var rand = baseRandom(0, ++index);
|
|
result[index] = result[rand];
|
|
result[rand] = value;
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets the size of the `collection` by returning `collection.length` for arrays
|
|
* and array-like objects or the number of own enumerable properties for objects.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to inspect.
|
|
* @returns {number} Returns `collection.length` or number of own enumerable properties.
|
|
* @example
|
|
*
|
|
* _.size([1, 2]);
|
|
* // => 2
|
|
*
|
|
* _.size({ 'one': 1, 'two': 2, 'three': 3 });
|
|
* // => 3
|
|
*
|
|
* _.size('pebbles');
|
|
* // => 7
|
|
*/
|
|
function size(collection) {
|
|
var length = collection ? collection.length : 0;
|
|
return typeof length == 'number' ? length : keys(collection).length;
|
|
}
|
|
|
|
/**
|
|
* Checks if the callback returns a truey value for **any** element of a
|
|
* collection. The function returns as soon as it finds a passing value and
|
|
* does not iterate over the entire collection. The callback is bound to
|
|
* `thisArg` and invoked with three arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias any
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {boolean} Returns `true` if any element passed the callback check,
|
|
* else `false`.
|
|
* @example
|
|
*
|
|
* _.some([null, 0, 'yes', false], Boolean);
|
|
* // => true
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': false },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': true }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.some(characters, 'blocked');
|
|
* // => true
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.some(characters, { 'age': 1 });
|
|
* // => false
|
|
*/
|
|
function some(collection, callback, thisArg) {
|
|
var result;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
|
|
var index = -1,
|
|
length = collection ? collection.length : 0;
|
|
|
|
if (typeof length == 'number') {
|
|
while (++index < length) {
|
|
if ((result = callback(collection[index], index, collection))) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
forOwn(collection, function(value, index, collection) {
|
|
return !(result = callback(value, index, collection));
|
|
});
|
|
}
|
|
return !!result;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of elements, sorted in ascending order by the results of
|
|
* running each element in a collection through the callback. This method
|
|
* performs a stable sort, that is, it will preserve the original sort order
|
|
* of equal elements. The callback is bound to `thisArg` and invoked with
|
|
* three arguments; (value, index|key, collection).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an array of property names is provided for `callback` the collection
|
|
* will be sorted by each property value.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Array|Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new array of sorted elements.
|
|
* @example
|
|
*
|
|
* _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
|
|
* // => [3, 1, 2]
|
|
*
|
|
* _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
|
|
* // => [3, 1, 2]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 },
|
|
* { 'name': 'barney', 'age': 26 },
|
|
* { 'name': 'fred', 'age': 30 }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.map(_.sortBy(characters, 'age'), _.values);
|
|
* // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
|
|
*
|
|
* // sorting by multiple properties
|
|
* _.map(_.sortBy(characters, ['name', 'age']), _.values);
|
|
* // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
|
|
*/
|
|
function sortBy(collection, callback, thisArg) {
|
|
var index = -1,
|
|
isArr = isArray(callback),
|
|
length = collection ? collection.length : 0,
|
|
result = Array(typeof length == 'number' ? length : 0);
|
|
|
|
if (!isArr) {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
}
|
|
forEach(collection, function(value, key, collection) {
|
|
var object = result[++index] = getObject();
|
|
if (isArr) {
|
|
object.criteria = map(callback, function(key) { return value[key]; });
|
|
} else {
|
|
(object.criteria = getArray())[0] = callback(value, key, collection);
|
|
}
|
|
object.index = index;
|
|
object.value = value;
|
|
});
|
|
|
|
length = result.length;
|
|
result.sort(compareAscending);
|
|
while (length--) {
|
|
var object = result[length];
|
|
result[length] = object.value;
|
|
if (!isArr) {
|
|
releaseArray(object.criteria);
|
|
}
|
|
releaseObject(object);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Converts the `collection` to an array.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to convert.
|
|
* @returns {Array} Returns the new converted array.
|
|
* @example
|
|
*
|
|
* (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
|
|
* // => [2, 3, 4]
|
|
*/
|
|
function toArray(collection) {
|
|
if (collection && typeof collection.length == 'number') {
|
|
return slice(collection);
|
|
}
|
|
return values(collection);
|
|
}
|
|
|
|
/**
|
|
* Performs a deep comparison of each element in a `collection` to the given
|
|
* `properties` object, returning an array of all elements that have equivalent
|
|
* property values.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type Function
|
|
* @category Collections
|
|
* @param {Array|Object|string} collection The collection to iterate over.
|
|
* @param {Object} props The object of property values to filter by.
|
|
* @returns {Array} Returns a new array of elements that have the given properties.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
|
|
* { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
|
|
* ];
|
|
*
|
|
* _.where(characters, { 'age': 36 });
|
|
* // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
|
|
*
|
|
* _.where(characters, { 'pets': ['dino'] });
|
|
* // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
|
|
*/
|
|
var where = filter;
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates an array with all falsey values removed. The values `false`, `null`,
|
|
* `0`, `""`, `undefined`, and `NaN` are all falsey.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to compact.
|
|
* @returns {Array} Returns a new array of filtered values.
|
|
* @example
|
|
*
|
|
* _.compact([0, 1, false, 2, '', 3]);
|
|
* // => [1, 2, 3]
|
|
*/
|
|
function compact(array) {
|
|
var index = -1,
|
|
length = array ? array.length : 0,
|
|
result = [];
|
|
|
|
while (++index < length) {
|
|
var value = array[index];
|
|
if (value) {
|
|
result.push(value);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an array excluding all values of the provided arrays using strict
|
|
* equality for comparisons, i.e. `===`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to process.
|
|
* @param {...Array} [values] The arrays of values to exclude.
|
|
* @returns {Array} Returns a new array of filtered values.
|
|
* @example
|
|
*
|
|
* _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
|
|
* // => [1, 3, 4]
|
|
*/
|
|
function difference(array) {
|
|
return baseDifference(array, baseFlatten(arguments, true, true, 1));
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.find` except that it returns the index of the first
|
|
* element that passes the callback check, instead of the element itself.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to search.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {number} Returns the index of the found element, else `-1`.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': false },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': true },
|
|
* { 'name': 'pebbles', 'age': 1, 'blocked': false }
|
|
* ];
|
|
*
|
|
* _.findIndex(characters, function(chr) {
|
|
* return chr.age < 20;
|
|
* });
|
|
* // => 2
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.findIndex(characters, { 'age': 36 });
|
|
* // => 0
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.findIndex(characters, 'blocked');
|
|
* // => 1
|
|
*/
|
|
function findIndex(array, callback, thisArg) {
|
|
var index = -1,
|
|
length = array ? array.length : 0;
|
|
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (++index < length) {
|
|
if (callback(array[index], index, array)) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.findIndex` except that it iterates over elements
|
|
* of a `collection` from right to left.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to search.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {number} Returns the index of the found element, else `-1`.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36, 'blocked': true },
|
|
* { 'name': 'fred', 'age': 40, 'blocked': false },
|
|
* { 'name': 'pebbles', 'age': 1, 'blocked': true }
|
|
* ];
|
|
*
|
|
* _.findLastIndex(characters, function(chr) {
|
|
* return chr.age > 30;
|
|
* });
|
|
* // => 1
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.findLastIndex(characters, { 'age': 36 });
|
|
* // => 0
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.findLastIndex(characters, 'blocked');
|
|
* // => 2
|
|
*/
|
|
function findLastIndex(array, callback, thisArg) {
|
|
var length = array ? array.length : 0;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (length--) {
|
|
if (callback(array[length], length, array)) {
|
|
return length;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Gets the first element or first `n` elements of an array. If a callback
|
|
* is provided elements at the beginning of the array are returned as long
|
|
* as the callback returns truey. The callback is bound to `thisArg` and
|
|
* invoked with three arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias head, take
|
|
* @category Arrays
|
|
* @param {Array} array The array to query.
|
|
* @param {Function|Object|number|string} [callback] The function called
|
|
* per element or the number of elements to return. If a property name or
|
|
* object is provided it will be used to create a "_.pluck" or "_.where"
|
|
* style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the first element(s) of `array`.
|
|
* @example
|
|
*
|
|
* _.first([1, 2, 3]);
|
|
* // => 1
|
|
*
|
|
* _.first([1, 2, 3], 2);
|
|
* // => [1, 2]
|
|
*
|
|
* _.first([1, 2, 3], function(num) {
|
|
* return num < 3;
|
|
* });
|
|
* // => [1, 2]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
|
|
* { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
|
|
* { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.first(characters, 'blocked');
|
|
* // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
|
|
* // => ['barney', 'fred']
|
|
*/
|
|
function first(array, callback, thisArg) {
|
|
var n = 0,
|
|
length = array ? array.length : 0;
|
|
|
|
if (typeof callback != 'number' && callback != null) {
|
|
var index = -1;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (++index < length && callback(array[index], index, array)) {
|
|
n++;
|
|
}
|
|
} else {
|
|
n = callback;
|
|
if (n == null || thisArg) {
|
|
return array ? array[0] : undefined;
|
|
}
|
|
}
|
|
return slice(array, 0, nativeMin(nativeMax(0, n), length));
|
|
}
|
|
|
|
/**
|
|
* Flattens a nested array (the nesting can be to any depth). If `isShallow`
|
|
* is truey, the array will only be flattened a single level. If a callback
|
|
* is provided each element of the array is passed through the callback before
|
|
* flattening. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to flatten.
|
|
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new flattened array.
|
|
* @example
|
|
*
|
|
* _.flatten([1, [2], [3, [[4]]]]);
|
|
* // => [1, 2, 3, 4];
|
|
*
|
|
* _.flatten([1, [2], [3, [[4]]]], true);
|
|
* // => [1, 2, 3, [[4]]];
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
|
|
* { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.flatten(characters, 'pets');
|
|
* // => ['hoppy', 'baby puss', 'dino']
|
|
*/
|
|
function flatten(array, isShallow, callback, thisArg) {
|
|
// juggle arguments
|
|
if (typeof isShallow != 'boolean' && isShallow != null) {
|
|
thisArg = callback;
|
|
callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
|
|
isShallow = false;
|
|
}
|
|
if (callback != null) {
|
|
array = map(array, callback, thisArg);
|
|
}
|
|
return baseFlatten(array, isShallow);
|
|
}
|
|
|
|
/**
|
|
* Gets the index at which the first occurrence of `value` is found using
|
|
* strict equality for comparisons, i.e. `===`. If the array is already sorted
|
|
* providing `true` for `fromIndex` will run a faster binary search.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @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 or `-1`.
|
|
* @example
|
|
*
|
|
* _.indexOf([1, 2, 3, 1, 2, 3], 2);
|
|
* // => 1
|
|
*
|
|
* _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
|
|
* // => 4
|
|
*
|
|
* _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
|
|
* // => 2
|
|
*/
|
|
function indexOf(array, value, fromIndex) {
|
|
if (typeof fromIndex == 'number') {
|
|
var length = array ? array.length : 0;
|
|
fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
|
|
} else if (fromIndex) {
|
|
var index = sortedIndex(array, value);
|
|
return array[index] === value ? index : -1;
|
|
}
|
|
return baseIndexOf(array, value, fromIndex);
|
|
}
|
|
|
|
/**
|
|
* Gets all but the last element or last `n` elements of an array. If a
|
|
* callback is provided elements at the end of the array are excluded from
|
|
* the result as long as the callback returns truey. The callback is bound
|
|
* to `thisArg` and invoked with three arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to query.
|
|
* @param {Function|Object|number|string} [callback=1] The function called
|
|
* per element or the number of elements to exclude. If a property name or
|
|
* object is provided it will be used to create a "_.pluck" or "_.where"
|
|
* style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a slice of `array`.
|
|
* @example
|
|
*
|
|
* _.initial([1, 2, 3]);
|
|
* // => [1, 2]
|
|
*
|
|
* _.initial([1, 2, 3], 2);
|
|
* // => [1]
|
|
*
|
|
* _.initial([1, 2, 3], function(num) {
|
|
* return num > 1;
|
|
* });
|
|
* // => [1]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
|
|
* { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
|
|
* { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.initial(characters, 'blocked');
|
|
* // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
|
|
* // => ['barney', 'fred']
|
|
*/
|
|
function initial(array, callback, thisArg) {
|
|
var n = 0,
|
|
length = array ? array.length : 0;
|
|
|
|
if (typeof callback != 'number' && callback != null) {
|
|
var index = length;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (index-- && callback(array[index], index, array)) {
|
|
n++;
|
|
}
|
|
} else {
|
|
n = (callback == null || thisArg) ? 1 : callback || n;
|
|
}
|
|
return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
|
|
}
|
|
|
|
/**
|
|
* Creates an array of unique values present in all provided arrays using
|
|
* strict equality for comparisons, i.e. `===`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {...Array} [array] The arrays to inspect.
|
|
* @returns {Array} Returns an array of shared values.
|
|
* @example
|
|
*
|
|
* _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
|
|
* // => [1, 2]
|
|
*/
|
|
function intersection() {
|
|
var args = [],
|
|
argsIndex = -1,
|
|
argsLength = arguments.length,
|
|
caches = getArray(),
|
|
indexOf = getIndexOf(),
|
|
trustIndexOf = indexOf === baseIndexOf,
|
|
seen = getArray();
|
|
|
|
while (++argsIndex < argsLength) {
|
|
var value = arguments[argsIndex];
|
|
if (isArray(value) || isArguments(value)) {
|
|
args.push(value);
|
|
caches.push(trustIndexOf && value.length >= largeArraySize &&
|
|
createCache(argsIndex ? args[argsIndex] : seen));
|
|
}
|
|
}
|
|
var array = args[0],
|
|
index = -1,
|
|
length = array ? array.length : 0,
|
|
result = [];
|
|
|
|
outer:
|
|
while (++index < length) {
|
|
var cache = caches[0];
|
|
value = array[index];
|
|
|
|
if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
|
|
argsIndex = argsLength;
|
|
(cache || seen).push(value);
|
|
while (--argsIndex) {
|
|
cache = caches[argsIndex];
|
|
if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
|
|
continue outer;
|
|
}
|
|
}
|
|
result.push(value);
|
|
}
|
|
}
|
|
while (argsLength--) {
|
|
cache = caches[argsLength];
|
|
if (cache) {
|
|
releaseObject(cache);
|
|
}
|
|
}
|
|
releaseArray(caches);
|
|
releaseArray(seen);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets the last element or last `n` elements of an array. If a callback is
|
|
* provided elements at the end of the array are returned as long as the
|
|
* callback returns truey. The callback is bound to `thisArg` and invoked
|
|
* with three arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to query.
|
|
* @param {Function|Object|number|string} [callback] The function called
|
|
* per element or the number of elements to return. If a property name or
|
|
* object is provided it will be used to create a "_.pluck" or "_.where"
|
|
* style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {*} Returns the last element(s) of `array`.
|
|
* @example
|
|
*
|
|
* _.last([1, 2, 3]);
|
|
* // => 3
|
|
*
|
|
* _.last([1, 2, 3], 2);
|
|
* // => [2, 3]
|
|
*
|
|
* _.last([1, 2, 3], function(num) {
|
|
* return num > 1;
|
|
* });
|
|
* // => [2, 3]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'blocked': false, 'employer': 'slate' },
|
|
* { 'name': 'fred', 'blocked': true, 'employer': 'slate' },
|
|
* { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.pluck(_.last(characters, 'blocked'), 'name');
|
|
* // => ['fred', 'pebbles']
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.last(characters, { 'employer': 'na' });
|
|
* // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
|
|
*/
|
|
function last(array, callback, thisArg) {
|
|
var n = 0,
|
|
length = array ? array.length : 0;
|
|
|
|
if (typeof callback != 'number' && callback != null) {
|
|
var index = length;
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (index-- && callback(array[index], index, array)) {
|
|
n++;
|
|
}
|
|
} else {
|
|
n = callback;
|
|
if (n == null || thisArg) {
|
|
return array ? array[length - 1] : undefined;
|
|
}
|
|
}
|
|
return slice(array, nativeMax(0, length - n));
|
|
}
|
|
|
|
/**
|
|
* Gets the index at which the last occurrence of `value` is found using strict
|
|
* equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
|
|
* as the offset from the end of the collection.
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to search.
|
|
* @param {*} value The value to search for.
|
|
* @param {number} [fromIndex=array.length-1] The index to search from.
|
|
* @returns {number} Returns the index of the matched value or `-1`.
|
|
* @example
|
|
*
|
|
* _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
|
|
* // => 4
|
|
*
|
|
* _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
|
|
* // => 1
|
|
*/
|
|
function lastIndexOf(array, value, fromIndex) {
|
|
var index = array ? array.length : 0;
|
|
if (typeof fromIndex == 'number') {
|
|
index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
|
|
}
|
|
while (index--) {
|
|
if (array[index] === value) {
|
|
return index;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Removes all provided values from the given array using strict equality for
|
|
* comparisons, i.e. `===`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to modify.
|
|
* @param {...*} [value] The values to remove.
|
|
* @returns {Array} Returns `array`.
|
|
* @example
|
|
*
|
|
* var array = [1, 2, 3, 1, 2, 3];
|
|
* _.pull(array, 2, 3);
|
|
* console.log(array);
|
|
* // => [1, 1]
|
|
*/
|
|
function pull(array) {
|
|
var args = arguments,
|
|
argsIndex = 0,
|
|
argsLength = args.length,
|
|
length = array ? array.length : 0;
|
|
|
|
while (++argsIndex < argsLength) {
|
|
var index = -1,
|
|
value = args[argsIndex];
|
|
while (++index < length) {
|
|
if (array[index] === value) {
|
|
splice.call(array, index--, 1);
|
|
length--;
|
|
}
|
|
}
|
|
}
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of numbers (positive and/or negative) progressing from
|
|
* `start` up to but not including `end`. If `start` is less than `stop` a
|
|
* zero-length range is created unless a negative `step` is specified.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {number} [start=0] The start of the range.
|
|
* @param {number} end The end of the range.
|
|
* @param {number} [step=1] The value to increment or decrement by.
|
|
* @returns {Array} Returns a new range array.
|
|
* @example
|
|
*
|
|
* _.range(4);
|
|
* // => [0, 1, 2, 3]
|
|
*
|
|
* _.range(1, 5);
|
|
* // => [1, 2, 3, 4]
|
|
*
|
|
* _.range(0, 20, 5);
|
|
* // => [0, 5, 10, 15]
|
|
*
|
|
* _.range(0, -4, -1);
|
|
* // => [0, -1, -2, -3]
|
|
*
|
|
* _.range(1, 4, 0);
|
|
* // => [1, 1, 1]
|
|
*
|
|
* _.range(0);
|
|
* // => []
|
|
*/
|
|
function range(start, end, step) {
|
|
start = +start || 0;
|
|
step = typeof step == 'number' ? step : (+step || 1);
|
|
|
|
if (end == null) {
|
|
end = start;
|
|
start = 0;
|
|
}
|
|
// use `Array(length)` so engines like Chakra and V8 avoid slower modes
|
|
// http://youtu.be/XAqIpGU8ZZk#t=17m25s
|
|
var index = -1,
|
|
length = nativeMax(0, ceil((end - start) / (step || 1))),
|
|
result = Array(length);
|
|
|
|
while (++index < length) {
|
|
result[index] = start;
|
|
start += step;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Removes all elements from an array that the callback returns truey for
|
|
* and returns an array of removed elements. The callback is bound to `thisArg`
|
|
* and invoked with three arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to modify.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a new array of removed elements.
|
|
* @example
|
|
*
|
|
* var array = [1, 2, 3, 4, 5, 6];
|
|
* var evens = _.remove(array, function(num) { return num % 2 == 0; });
|
|
*
|
|
* console.log(array);
|
|
* // => [1, 3, 5]
|
|
*
|
|
* console.log(evens);
|
|
* // => [2, 4, 6]
|
|
*/
|
|
function remove(array, callback, thisArg) {
|
|
var index = -1,
|
|
length = array ? array.length : 0,
|
|
result = [];
|
|
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (++index < length) {
|
|
var value = array[index];
|
|
if (callback(value, index, array)) {
|
|
result.push(value);
|
|
splice.call(array, index--, 1);
|
|
length--;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The opposite of `_.initial` this method gets all but the first element or
|
|
* first `n` elements of an array. If a callback function is provided elements
|
|
* at the beginning of the array are excluded from the result as long as the
|
|
* callback returns truey. The callback is bound to `thisArg` and invoked
|
|
* with three arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias drop, tail
|
|
* @category Arrays
|
|
* @param {Array} array The array to query.
|
|
* @param {Function|Object|number|string} [callback=1] The function called
|
|
* per element or the number of elements to exclude. If a property name or
|
|
* object is provided it will be used to create a "_.pluck" or "_.where"
|
|
* style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a slice of `array`.
|
|
* @example
|
|
*
|
|
* _.rest([1, 2, 3]);
|
|
* // => [2, 3]
|
|
*
|
|
* _.rest([1, 2, 3], 2);
|
|
* // => [3]
|
|
*
|
|
* _.rest([1, 2, 3], function(num) {
|
|
* return num < 3;
|
|
* });
|
|
* // => [3]
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'blocked': true, 'employer': 'slate' },
|
|
* { 'name': 'fred', 'blocked': false, 'employer': 'slate' },
|
|
* { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
|
|
* ];
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.pluck(_.rest(characters, 'blocked'), 'name');
|
|
* // => ['fred', 'pebbles']
|
|
*
|
|
* // using "_.where" callback shorthand
|
|
* _.rest(characters, { 'employer': 'slate' });
|
|
* // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
|
|
*/
|
|
function rest(array, callback, thisArg) {
|
|
if (typeof callback != 'number' && callback != null) {
|
|
var n = 0,
|
|
index = -1,
|
|
length = array ? array.length : 0;
|
|
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
while (++index < length && callback(array[index], index, array)) {
|
|
n++;
|
|
}
|
|
} else {
|
|
n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
|
|
}
|
|
return slice(array, n);
|
|
}
|
|
|
|
/**
|
|
* Uses a binary search to determine the smallest index at which a value
|
|
* should be inserted into a given sorted array in order to maintain the sort
|
|
* order of the array. If a callback is provided it will be executed for
|
|
* `value` and each element of `array` to compute their sort ranking. The
|
|
* callback is bound to `thisArg` and invoked with one argument; (value).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to inspect.
|
|
* @param {*} value The value to evaluate.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {number} Returns the index at which `value` should be inserted
|
|
* into `array`.
|
|
* @example
|
|
*
|
|
* _.sortedIndex([20, 30, 50], 40);
|
|
* // => 2
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
|
|
* // => 2
|
|
*
|
|
* var dict = {
|
|
* 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
|
|
* };
|
|
*
|
|
* _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
|
|
* return dict.wordToNumber[word];
|
|
* });
|
|
* // => 2
|
|
*
|
|
* _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
|
|
* return this.wordToNumber[word];
|
|
* }, dict);
|
|
* // => 2
|
|
*/
|
|
function sortedIndex(array, value, callback, thisArg) {
|
|
var low = 0,
|
|
high = array ? array.length : low;
|
|
|
|
// explicitly reference `identity` for better inlining in Firefox
|
|
callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
|
|
value = callback(value);
|
|
|
|
while (low < high) {
|
|
var mid = (low + high) >>> 1;
|
|
(callback(array[mid]) < value)
|
|
? low = mid + 1
|
|
: high = mid;
|
|
}
|
|
return low;
|
|
}
|
|
|
|
/**
|
|
* Creates an array of unique values, in order, of the provided arrays using
|
|
* strict equality for comparisons, i.e. `===`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {...Array} [array] The arrays to inspect.
|
|
* @returns {Array} Returns an array of combined values.
|
|
* @example
|
|
*
|
|
* _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
|
|
* // => [1, 2, 3, 5, 4]
|
|
*/
|
|
function union() {
|
|
return baseUniq(baseFlatten(arguments, true, true));
|
|
}
|
|
|
|
/**
|
|
* Creates a duplicate-value-free version of an array using strict equality
|
|
* for comparisons, i.e. `===`. If the array is sorted, providing
|
|
* `true` for `isSorted` will use a faster algorithm. If a callback is provided
|
|
* each element of `array` is passed through the callback before uniqueness
|
|
* is computed. The callback is bound to `thisArg` and invoked with three
|
|
* arguments; (value, index, array).
|
|
*
|
|
* If a property name is provided for `callback` the created "_.pluck" style
|
|
* callback will return the property value of the given element.
|
|
*
|
|
* If an object is provided for `callback` the created "_.where" style callback
|
|
* will return `true` for elements that have the properties of the given object,
|
|
* else `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias unique
|
|
* @category Arrays
|
|
* @param {Array} array The array to process.
|
|
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
|
|
* @param {Function|Object|string} [callback=identity] The function called
|
|
* per iteration. If a property name or object is provided it will be used
|
|
* to create a "_.pluck" or "_.where" style callback, respectively.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns a duplicate-value-free array.
|
|
* @example
|
|
*
|
|
* _.uniq([1, 2, 1, 3, 1]);
|
|
* // => [1, 2, 3]
|
|
*
|
|
* _.uniq([1, 1, 2, 2, 3], true);
|
|
* // => [1, 2, 3]
|
|
*
|
|
* _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
|
|
* // => ['A', 'b', 'C']
|
|
*
|
|
* _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
|
|
* // => [1, 2.5, 3]
|
|
*
|
|
* // using "_.pluck" callback shorthand
|
|
* _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
|
|
* // => [{ 'x': 1 }, { 'x': 2 }]
|
|
*/
|
|
function uniq(array, isSorted, callback, thisArg) {
|
|
// juggle arguments
|
|
if (typeof isSorted != 'boolean' && isSorted != null) {
|
|
thisArg = callback;
|
|
callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
|
|
isSorted = false;
|
|
}
|
|
if (callback != null) {
|
|
callback = lodash.createCallback(callback, thisArg, 3);
|
|
}
|
|
return baseUniq(array, isSorted, callback);
|
|
}
|
|
|
|
/**
|
|
* Creates an array excluding all provided values using strict equality for
|
|
* comparisons, i.e. `===`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {Array} array The array to filter.
|
|
* @param {...*} [value] The values to exclude.
|
|
* @returns {Array} Returns a new array of filtered values.
|
|
* @example
|
|
*
|
|
* _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
|
|
* // => [2, 3, 4]
|
|
*/
|
|
function without(array) {
|
|
return baseDifference(array, slice(arguments, 1));
|
|
}
|
|
|
|
/**
|
|
* Creates an array that is the symmetric difference of the provided arrays.
|
|
* See http://en.wikipedia.org/wiki/Symmetric_difference.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Arrays
|
|
* @param {...Array} [array] The arrays to inspect.
|
|
* @returns {Array} Returns an array of values.
|
|
* @example
|
|
*
|
|
* _.xor([1, 2, 3], [5, 2, 1, 4]);
|
|
* // => [3, 5, 4]
|
|
*
|
|
* _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
|
|
* // => [1, 4, 5]
|
|
*/
|
|
function xor() {
|
|
var index = -1,
|
|
length = arguments.length;
|
|
|
|
while (++index < length) {
|
|
var array = arguments[index];
|
|
if (isArray(array) || isArguments(array)) {
|
|
var result = result
|
|
? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
|
|
: array;
|
|
}
|
|
}
|
|
return result || [];
|
|
}
|
|
|
|
/**
|
|
* Creates an array of grouped elements, the first of which contains the first
|
|
* elements of the given arrays, the second of which contains the second
|
|
* elements of the given arrays, and so on.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias unzip
|
|
* @category Arrays
|
|
* @param {...Array} [array] Arrays to process.
|
|
* @returns {Array} Returns a new array of grouped elements.
|
|
* @example
|
|
*
|
|
* _.zip(['fred', 'barney'], [30, 40], [true, false]);
|
|
* // => [['fred', 30, true], ['barney', 40, false]]
|
|
*/
|
|
function zip() {
|
|
var array = arguments.length > 1 ? arguments : arguments[0],
|
|
index = -1,
|
|
length = array ? max(pluck(array, 'length')) : 0,
|
|
result = Array(length < 0 ? 0 : length);
|
|
|
|
while (++index < length) {
|
|
result[index] = pluck(array, index);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Creates an object composed from arrays of `keys` and `values`. Provide
|
|
* either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
|
|
* or two arrays, one of `keys` and one of corresponding `values`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @alias object
|
|
* @category Arrays
|
|
* @param {Array} keys The array of keys.
|
|
* @param {Array} [values=[]] The array of values.
|
|
* @returns {Object} Returns an object composed of the given keys and
|
|
* corresponding values.
|
|
* @example
|
|
*
|
|
* _.zipObject(['fred', 'barney'], [30, 40]);
|
|
* // => { 'fred': 30, 'barney': 40 }
|
|
*/
|
|
function zipObject(keys, values) {
|
|
var index = -1,
|
|
length = keys ? keys.length : 0,
|
|
result = {};
|
|
|
|
if (!values && length && !isArray(keys[0])) {
|
|
values = [];
|
|
}
|
|
while (++index < length) {
|
|
var key = keys[index];
|
|
if (values) {
|
|
result[key] = values[index];
|
|
} else if (key) {
|
|
result[key[0]] = key[1];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates a function that executes `func`, with the `this` binding and
|
|
* arguments of the created function, only after being called `n` times.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {number} n The number of times the function must be called before
|
|
* `func` is executed.
|
|
* @param {Function} func The function to restrict.
|
|
* @returns {Function} Returns the new restricted function.
|
|
* @example
|
|
*
|
|
* var saves = ['profile', 'settings'];
|
|
*
|
|
* var done = _.after(saves.length, function() {
|
|
* console.log('Done saving!');
|
|
* });
|
|
*
|
|
* _.forEach(saves, function(type) {
|
|
* asyncSave({ 'type': type, 'complete': done });
|
|
* });
|
|
* // => logs 'Done saving!', after all saves have completed
|
|
*/
|
|
function after(n, func) {
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
return function() {
|
|
if (--n < 1) {
|
|
return func.apply(this, arguments);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a function that, when called, invokes `func` with the `this`
|
|
* binding of `thisArg` and prepends any additional `bind` arguments to those
|
|
* provided to the bound function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to bind.
|
|
* @param {*} [thisArg] The `this` binding of `func`.
|
|
* @param {...*} [arg] Arguments to be partially applied.
|
|
* @returns {Function} Returns the new bound function.
|
|
* @example
|
|
*
|
|
* var func = function(greeting) {
|
|
* return greeting + ' ' + this.name;
|
|
* };
|
|
*
|
|
* func = _.bind(func, { 'name': 'fred' }, 'hi');
|
|
* func();
|
|
* // => 'hi fred'
|
|
*/
|
|
function bind(func, thisArg) {
|
|
return arguments.length > 2
|
|
? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
|
|
: createWrapper(func, 1, null, null, thisArg);
|
|
}
|
|
|
|
/**
|
|
* Binds methods of an object to the object itself, overwriting the existing
|
|
* method. Method names may be specified as individual arguments or as arrays
|
|
* of method names. If no method names are provided all the function properties
|
|
* of `object` will be bound.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Object} object The object to bind and assign the bound methods to.
|
|
* @param {...string} [methodName] The object method names to
|
|
* bind, specified as individual method names or arrays of method names.
|
|
* @returns {Object} Returns `object`.
|
|
* @example
|
|
*
|
|
* var view = {
|
|
* 'label': 'docs',
|
|
* 'onClick': function() { console.log('clicked ' + this.label); }
|
|
* };
|
|
*
|
|
* _.bindAll(view);
|
|
* jQuery('#docs').on('click', view.onClick);
|
|
* // => logs 'clicked docs', when the button is clicked
|
|
*/
|
|
function bindAll(object) {
|
|
var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
|
|
index = -1,
|
|
length = funcs.length;
|
|
|
|
while (++index < length) {
|
|
var key = funcs[index];
|
|
object[key] = createWrapper(object[key], 1, null, null, object);
|
|
}
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Creates a function that, when called, invokes the method at `object[key]`
|
|
* and prepends any additional `bindKey` arguments to those provided to the bound
|
|
* function. This method differs from `_.bind` by allowing bound functions to
|
|
* reference methods that will be redefined or don't yet exist.
|
|
* See http://michaux.ca/articles/lazy-function-definition-pattern.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Object} object The object the method belongs to.
|
|
* @param {string} key The key of the method.
|
|
* @param {...*} [arg] Arguments to be partially applied.
|
|
* @returns {Function} Returns the new bound function.
|
|
* @example
|
|
*
|
|
* var object = {
|
|
* 'name': 'fred',
|
|
* 'greet': function(greeting) {
|
|
* return greeting + ' ' + this.name;
|
|
* }
|
|
* };
|
|
*
|
|
* var func = _.bindKey(object, 'greet', 'hi');
|
|
* func();
|
|
* // => 'hi fred'
|
|
*
|
|
* object.greet = function(greeting) {
|
|
* return greeting + 'ya ' + this.name + '!';
|
|
* };
|
|
*
|
|
* func();
|
|
* // => 'hiya fred!'
|
|
*/
|
|
function bindKey(object, key) {
|
|
return arguments.length > 2
|
|
? createWrapper(key, 19, slice(arguments, 2), null, object)
|
|
: createWrapper(key, 3, null, null, object);
|
|
}
|
|
|
|
/**
|
|
* Creates a function that is the composition of the provided functions,
|
|
* where each function consumes the return value of the function that follows.
|
|
* For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
|
|
* Each function is executed with the `this` binding of the composed function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {...Function} [func] Functions to compose.
|
|
* @returns {Function} Returns the new composed function.
|
|
* @example
|
|
*
|
|
* var realNameMap = {
|
|
* 'pebbles': 'penelope'
|
|
* };
|
|
*
|
|
* var format = function(name) {
|
|
* name = realNameMap[name.toLowerCase()] || name;
|
|
* return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
|
|
* };
|
|
*
|
|
* var greet = function(formatted) {
|
|
* return 'Hiya ' + formatted + '!';
|
|
* };
|
|
*
|
|
* var welcome = _.compose(greet, format);
|
|
* welcome('pebbles');
|
|
* // => 'Hiya Penelope!'
|
|
*/
|
|
function compose() {
|
|
var funcs = arguments,
|
|
length = funcs.length;
|
|
|
|
while (length--) {
|
|
if (!isFunction(funcs[length])) {
|
|
throw new TypeError;
|
|
}
|
|
}
|
|
return function() {
|
|
var args = arguments,
|
|
length = funcs.length;
|
|
|
|
while (length--) {
|
|
args = [funcs[length].apply(this, args)];
|
|
}
|
|
return args[0];
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a function which accepts one or more arguments of `func` that when
|
|
* invoked either executes `func` returning its result, if all `func` arguments
|
|
* have been provided, or returns a function that accepts one or more of the
|
|
* remaining `func` arguments, and so on. The arity of `func` can be specified
|
|
* if `func.length` is not sufficient.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to curry.
|
|
* @param {number} [arity=func.length] The arity of `func`.
|
|
* @returns {Function} Returns the new curried function.
|
|
* @example
|
|
*
|
|
* var curried = _.curry(function(a, b, c) {
|
|
* console.log(a + b + c);
|
|
* });
|
|
*
|
|
* curried(1)(2)(3);
|
|
* // => 6
|
|
*
|
|
* curried(1, 2)(3);
|
|
* // => 6
|
|
*
|
|
* curried(1, 2, 3);
|
|
* // => 6
|
|
*/
|
|
function curry(func, arity) {
|
|
arity = typeof arity == 'number' ? arity : (+arity || func.length);
|
|
return createWrapper(func, 4, null, null, null, arity);
|
|
}
|
|
|
|
/**
|
|
* Creates a function that will delay the execution of `func` until after
|
|
* `wait` milliseconds have elapsed since the last time it was invoked.
|
|
* Provide an options object to indicate that `func` should be invoked on
|
|
* the leading and/or trailing edge of the `wait` timeout. Subsequent calls
|
|
* to the debounced function will return the result of the last `func` call.
|
|
*
|
|
* Note: If `leading` and `trailing` options are `true` `func` will be called
|
|
* on the trailing edge of the timeout only if the the debounced function is
|
|
* invoked more than once during the `wait` timeout.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to debounce.
|
|
* @param {number} wait The number of milliseconds to delay.
|
|
* @param {Object} [options] The options object.
|
|
* @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
|
|
* @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
|
|
* @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
|
|
* @returns {Function} Returns the new debounced function.
|
|
* @example
|
|
*
|
|
* // avoid costly calculations while the window size is in flux
|
|
* var lazyLayout = _.debounce(calculateLayout, 150);
|
|
* jQuery(window).on('resize', lazyLayout);
|
|
*
|
|
* // execute `sendMail` when the click event is fired, debouncing subsequent calls
|
|
* jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
|
|
* 'leading': true,
|
|
* 'trailing': false
|
|
* });
|
|
*
|
|
* // ensure `batchLog` is executed once after 1 second of debounced calls
|
|
* var source = new EventSource('/stream');
|
|
* source.addEventListener('message', _.debounce(batchLog, 250, {
|
|
* 'maxWait': 1000
|
|
* }, false);
|
|
*/
|
|
function debounce(func, wait, options) {
|
|
var args,
|
|
maxTimeoutId,
|
|
result,
|
|
stamp,
|
|
thisArg,
|
|
timeoutId,
|
|
trailingCall,
|
|
lastCalled = 0,
|
|
maxWait = false,
|
|
trailing = true;
|
|
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
wait = nativeMax(0, wait) || 0;
|
|
if (options === true) {
|
|
var leading = true;
|
|
trailing = false;
|
|
} else if (isObject(options)) {
|
|
leading = options.leading;
|
|
maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
|
|
trailing = 'trailing' in options ? options.trailing : trailing;
|
|
}
|
|
var delayed = function() {
|
|
var remaining = wait - (now() - stamp);
|
|
if (remaining <= 0) {
|
|
if (maxTimeoutId) {
|
|
clearTimeout(maxTimeoutId);
|
|
}
|
|
var isCalled = trailingCall;
|
|
maxTimeoutId = timeoutId = trailingCall = undefined;
|
|
if (isCalled) {
|
|
lastCalled = now();
|
|
result = func.apply(thisArg, args);
|
|
if (!timeoutId && !maxTimeoutId) {
|
|
args = thisArg = null;
|
|
}
|
|
}
|
|
} else {
|
|
timeoutId = setTimeout(delayed, remaining);
|
|
}
|
|
};
|
|
|
|
var maxDelayed = function() {
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
maxTimeoutId = timeoutId = trailingCall = undefined;
|
|
if (trailing || (maxWait !== wait)) {
|
|
lastCalled = now();
|
|
result = func.apply(thisArg, args);
|
|
if (!timeoutId && !maxTimeoutId) {
|
|
args = thisArg = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
return function() {
|
|
args = arguments;
|
|
stamp = now();
|
|
thisArg = this;
|
|
trailingCall = trailing && (timeoutId || !leading);
|
|
|
|
if (maxWait === false) {
|
|
var leadingCall = leading && !timeoutId;
|
|
} else {
|
|
if (!maxTimeoutId && !leading) {
|
|
lastCalled = stamp;
|
|
}
|
|
var remaining = maxWait - (stamp - lastCalled),
|
|
isCalled = remaining <= 0;
|
|
|
|
if (isCalled) {
|
|
if (maxTimeoutId) {
|
|
maxTimeoutId = clearTimeout(maxTimeoutId);
|
|
}
|
|
lastCalled = stamp;
|
|
result = func.apply(thisArg, args);
|
|
}
|
|
else if (!maxTimeoutId) {
|
|
maxTimeoutId = setTimeout(maxDelayed, remaining);
|
|
}
|
|
}
|
|
if (isCalled && timeoutId) {
|
|
timeoutId = clearTimeout(timeoutId);
|
|
}
|
|
else if (!timeoutId && wait !== maxWait) {
|
|
timeoutId = setTimeout(delayed, wait);
|
|
}
|
|
if (leadingCall) {
|
|
isCalled = true;
|
|
result = func.apply(thisArg, args);
|
|
}
|
|
if (isCalled && !timeoutId && !maxTimeoutId) {
|
|
args = thisArg = null;
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Defers executing the `func` function until the current call stack has cleared.
|
|
* Additional arguments will be provided to `func` when it is invoked.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to defer.
|
|
* @param {...*} [arg] 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
|
|
*/
|
|
function defer(func) {
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
var args = slice(arguments, 1);
|
|
return setTimeout(function() { func.apply(undefined, args); }, 1);
|
|
}
|
|
|
|
/**
|
|
* Executes the `func` function after `wait` milliseconds. Additional arguments
|
|
* will be provided to `func` when it is invoked.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to delay.
|
|
* @param {number} wait The number of milliseconds to delay execution.
|
|
* @param {...*} [arg] Arguments to invoke the function with.
|
|
* @returns {number} Returns the timer id.
|
|
* @example
|
|
*
|
|
* _.delay(function(text) { console.log(text); }, 1000, 'later');
|
|
* // => logs 'later' after one second
|
|
*/
|
|
function delay(func, wait) {
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
var args = slice(arguments, 2);
|
|
return setTimeout(function() { func.apply(undefined, args); }, wait);
|
|
}
|
|
|
|
/**
|
|
* Creates a function that memoizes the result of `func`. If `resolver` is
|
|
* provided it will be used to determine the cache key for storing the result
|
|
* based on the arguments provided to the memoized function. By default, the
|
|
* first argument provided to the memoized function is used as the cache key.
|
|
* The `func` is executed with the `this` binding of the memoized function.
|
|
* The result cache is exposed as the `cache` property on the memoized function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to have its output memoized.
|
|
* @param {Function} [resolver] A function used to resolve the cache key.
|
|
* @returns {Function} Returns the new memoizing function.
|
|
* @example
|
|
*
|
|
* var fibonacci = _.memoize(function(n) {
|
|
* return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
|
|
* });
|
|
*
|
|
* fibonacci(9)
|
|
* // => 34
|
|
*
|
|
* var data = {
|
|
* 'fred': { 'name': 'fred', 'age': 40 },
|
|
* 'pebbles': { 'name': 'pebbles', 'age': 1 }
|
|
* };
|
|
*
|
|
* // modifying the result cache
|
|
* var get = _.memoize(function(name) { return data[name]; }, _.identity);
|
|
* get('pebbles');
|
|
* // => { 'name': 'pebbles', 'age': 1 }
|
|
*
|
|
* get.cache.pebbles.name = 'penelope';
|
|
* get('pebbles');
|
|
* // => { 'name': 'penelope', 'age': 1 }
|
|
*/
|
|
function memoize(func, resolver) {
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
var memoized = function() {
|
|
var cache = memoized.cache,
|
|
key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
|
|
|
|
return hasOwnProperty.call(cache, key)
|
|
? cache[key]
|
|
: (cache[key] = func.apply(this, arguments));
|
|
}
|
|
memoized.cache = {};
|
|
return memoized;
|
|
}
|
|
|
|
/**
|
|
* Creates a function that is restricted to execute `func` once. Repeat calls to
|
|
* the function will return the value of the first call. The `func` is executed
|
|
* with the `this` binding of the created function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to restrict.
|
|
* @returns {Function} Returns the new restricted function.
|
|
* @example
|
|
*
|
|
* var initialize = _.once(createApplication);
|
|
* initialize();
|
|
* initialize();
|
|
* // `initialize` executes `createApplication` once
|
|
*/
|
|
function once(func) {
|
|
var ran,
|
|
result;
|
|
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
return function() {
|
|
if (ran) {
|
|
return result;
|
|
}
|
|
ran = true;
|
|
result = func.apply(this, arguments);
|
|
|
|
// clear the `func` variable so the function may be garbage collected
|
|
func = null;
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a function that, when called, invokes `func` with any additional
|
|
* `partial` arguments prepended to those provided to the new function. This
|
|
* method is similar to `_.bind` except it does **not** alter the `this` binding.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to partially apply arguments to.
|
|
* @param {...*} [arg] Arguments to be partially applied.
|
|
* @returns {Function} Returns the new partially applied function.
|
|
* @example
|
|
*
|
|
* var greet = function(greeting, name) { return greeting + ' ' + name; };
|
|
* var hi = _.partial(greet, 'hi');
|
|
* hi('fred');
|
|
* // => 'hi fred'
|
|
*/
|
|
function partial(func) {
|
|
return createWrapper(func, 16, slice(arguments, 1));
|
|
}
|
|
|
|
/**
|
|
* This method is like `_.partial` except that `partial` arguments are
|
|
* appended to those provided to the new function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to partially apply arguments to.
|
|
* @param {...*} [arg] Arguments to be partially applied.
|
|
* @returns {Function} Returns the new partially applied function.
|
|
* @example
|
|
*
|
|
* var defaultsDeep = _.partialRight(_.merge, _.defaults);
|
|
*
|
|
* var options = {
|
|
* 'variable': 'data',
|
|
* 'imports': { 'jq': $ }
|
|
* };
|
|
*
|
|
* defaultsDeep(options, _.templateSettings);
|
|
*
|
|
* options.variable
|
|
* // => 'data'
|
|
*
|
|
* options.imports
|
|
* // => { '_': _, 'jq': $ }
|
|
*/
|
|
function partialRight(func) {
|
|
return createWrapper(func, 32, null, slice(arguments, 1));
|
|
}
|
|
|
|
/**
|
|
* Creates a function that, when executed, will only call the `func` function
|
|
* at most once per every `wait` milliseconds. Provide an options object to
|
|
* indicate that `func` should be invoked on the leading and/or trailing edge
|
|
* of the `wait` timeout. Subsequent calls to the throttled function will
|
|
* return the result of the last `func` call.
|
|
*
|
|
* Note: If `leading` and `trailing` options are `true` `func` will be called
|
|
* on the trailing edge of the timeout only if the the throttled function is
|
|
* invoked more than once during the `wait` timeout.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {Function} func The function to throttle.
|
|
* @param {number} wait The number of milliseconds to throttle executions to.
|
|
* @param {Object} [options] The options object.
|
|
* @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
|
|
* @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
|
|
* @returns {Function} Returns the new throttled function.
|
|
* @example
|
|
*
|
|
* // avoid excessively updating the position while scrolling
|
|
* var throttled = _.throttle(updatePosition, 100);
|
|
* jQuery(window).on('scroll', throttled);
|
|
*
|
|
* // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
|
|
* jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
|
|
* 'trailing': false
|
|
* }));
|
|
*/
|
|
function throttle(func, wait, options) {
|
|
var leading = true,
|
|
trailing = true;
|
|
|
|
if (!isFunction(func)) {
|
|
throw new TypeError;
|
|
}
|
|
if (options === false) {
|
|
leading = false;
|
|
} else if (isObject(options)) {
|
|
leading = 'leading' in options ? options.leading : leading;
|
|
trailing = 'trailing' in options ? options.trailing : trailing;
|
|
}
|
|
debounceOptions.leading = leading;
|
|
debounceOptions.maxWait = wait;
|
|
debounceOptions.trailing = trailing;
|
|
|
|
return debounce(func, wait, debounceOptions);
|
|
}
|
|
|
|
/**
|
|
* Creates a function that provides `value` to the wrapper function as its
|
|
* first argument. Additional arguments provided to the function are appended
|
|
* to those provided to the wrapper function. The wrapper is executed with
|
|
* the `this` binding of the created function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Functions
|
|
* @param {*} value The value to wrap.
|
|
* @param {Function} wrapper The wrapper function.
|
|
* @returns {Function} Returns the new function.
|
|
* @example
|
|
*
|
|
* var p = _.wrap(_.escape, function(func, text) {
|
|
* return '<p>' + func(text) + '</p>';
|
|
* });
|
|
*
|
|
* p('Fred, Wilma, & Pebbles');
|
|
* // => '<p>Fred, Wilma, & Pebbles</p>'
|
|
*/
|
|
function wrap(value, wrapper) {
|
|
return createWrapper(wrapper, 16, [value]);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates a function that returns `value`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {*} value The value to return from the new function.
|
|
* @returns {Function} Returns the new function.
|
|
* @example
|
|
*
|
|
* var object = { 'name': 'fred' };
|
|
* var getter = _.constant(object);
|
|
* getter() === object;
|
|
* // => true
|
|
*/
|
|
function constant(value) {
|
|
return function() {
|
|
return value;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Produces a callback bound to an optional `thisArg`. If `func` is a property
|
|
* name the created callback will return the property value for a given element.
|
|
* If `func` is an object the created callback will return `true` for elements
|
|
* that contain the equivalent object properties, otherwise it will return `false`.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {*} [func=identity] The value to convert to a callback.
|
|
* @param {*} [thisArg] The `this` binding of the created callback.
|
|
* @param {number} [argCount] The number of arguments the callback accepts.
|
|
* @returns {Function} Returns a callback function.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* // wrap to create custom callback shorthands
|
|
* _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
|
|
* var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
|
|
* return !match ? func(callback, thisArg) : function(object) {
|
|
* return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
|
|
* };
|
|
* });
|
|
*
|
|
* _.filter(characters, 'age__gt38');
|
|
* // => [{ 'name': 'fred', 'age': 40 }]
|
|
*/
|
|
function createCallback(func, thisArg, argCount) {
|
|
var type = typeof func;
|
|
if (func == null || type == 'function') {
|
|
return baseCreateCallback(func, thisArg, argCount);
|
|
}
|
|
// handle "_.pluck" style callback shorthands
|
|
if (type != 'object') {
|
|
return property(func);
|
|
}
|
|
var props = keys(func),
|
|
key = props[0],
|
|
a = func[key];
|
|
|
|
// handle "_.where" style callback shorthands
|
|
if (props.length == 1 && a === a && !isObject(a)) {
|
|
// fast path the common case of providing an object with a single
|
|
// property containing a primitive value
|
|
return function(object) {
|
|
var b = object[key];
|
|
return a === b && (a !== 0 || (1 / a == 1 / b));
|
|
};
|
|
}
|
|
return function(object) {
|
|
var length = props.length,
|
|
result = false;
|
|
|
|
while (length--) {
|
|
if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
|
|
* corresponding HTML entities.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {string} string The string to escape.
|
|
* @returns {string} Returns the escaped string.
|
|
* @example
|
|
*
|
|
* _.escape('Fred, Wilma, & Pebbles');
|
|
* // => 'Fred, Wilma, & Pebbles'
|
|
*/
|
|
function escape(string) {
|
|
return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
|
|
}
|
|
|
|
/**
|
|
* This method returns the first argument provided to it.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {*} value Any value.
|
|
* @returns {*} Returns `value`.
|
|
* @example
|
|
*
|
|
* var object = { 'name': 'fred' };
|
|
* _.identity(object) === object;
|
|
* // => true
|
|
*/
|
|
function identity(value) {
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Adds function properties of a source object to the destination object.
|
|
* If `object` is a function methods will be added to its prototype as well.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {Function|Object} [object=lodash] object The destination object.
|
|
* @param {Object} source The object of functions to add.
|
|
* @param {Object} [options] The options object.
|
|
* @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
|
|
* @example
|
|
*
|
|
* function capitalize(string) {
|
|
* return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
|
|
* }
|
|
*
|
|
* _.mixin({ 'capitalize': capitalize });
|
|
* _.capitalize('fred');
|
|
* // => 'Fred'
|
|
*
|
|
* _('fred').capitalize().value();
|
|
* // => 'Fred'
|
|
*
|
|
* _.mixin({ 'capitalize': capitalize }, { 'chain': false });
|
|
* _('fred').capitalize();
|
|
* // => 'Fred'
|
|
*/
|
|
function mixin(object, source, options) {
|
|
var chain = true,
|
|
methodNames = source && functions(source);
|
|
|
|
if (!source || (!options && !methodNames.length)) {
|
|
if (options == null) {
|
|
options = source;
|
|
}
|
|
ctor = lodashWrapper;
|
|
source = object;
|
|
object = lodash;
|
|
methodNames = functions(source);
|
|
}
|
|
if (options === false) {
|
|
chain = false;
|
|
} else if (isObject(options) && 'chain' in options) {
|
|
chain = options.chain;
|
|
}
|
|
var ctor = object,
|
|
isFunc = isFunction(ctor);
|
|
|
|
forEach(methodNames, function(methodName) {
|
|
var func = object[methodName] = source[methodName];
|
|
if (isFunc) {
|
|
ctor.prototype[methodName] = function() {
|
|
var chainAll = this.__chain__,
|
|
value = this.__wrapped__,
|
|
args = [value];
|
|
|
|
push.apply(args, arguments);
|
|
var result = func.apply(object, args);
|
|
if (chain || chainAll) {
|
|
if (value === result && isObject(result)) {
|
|
return this;
|
|
}
|
|
result = new ctor(result);
|
|
result.__chain__ = chainAll;
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Reverts the '_' variable to its previous value and returns a reference to
|
|
* the `lodash` function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @returns {Function} Returns the `lodash` function.
|
|
* @example
|
|
*
|
|
* var lodash = _.noConflict();
|
|
*/
|
|
function noConflict() {
|
|
context._ = oldDash;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* A no-operation function.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @example
|
|
*
|
|
* var object = { 'name': 'fred' };
|
|
* _.noop(object) === undefined;
|
|
* // => true
|
|
*/
|
|
function noop() {
|
|
// no operation performed
|
|
}
|
|
|
|
/**
|
|
* Gets the number of milliseconds that have elapsed since the Unix epoch
|
|
* (1 January 1970 00:00:00 UTC).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @example
|
|
*
|
|
* var stamp = _.now();
|
|
* _.defer(function() { console.log(_.now() - stamp); });
|
|
* // => logs the number of milliseconds it took for the deferred function to be called
|
|
*/
|
|
var now = isNative(now = Date.now) && now || function() {
|
|
return new Date().getTime();
|
|
};
|
|
|
|
/**
|
|
* Converts the given value into an integer of the specified radix.
|
|
* If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
|
|
* `value` is a hexadecimal, in which case a `radix` of `16` is used.
|
|
*
|
|
* Note: This method avoids differences in native ES3 and ES5 `parseInt`
|
|
* implementations. See http://es5.github.io/#E.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {string} value The value to parse.
|
|
* @param {number} [radix] The radix used to interpret the value to parse.
|
|
* @returns {number} Returns the new integer value.
|
|
* @example
|
|
*
|
|
* _.parseInt('08');
|
|
* // => 8
|
|
*/
|
|
var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
|
|
// Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
|
|
return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
|
|
};
|
|
|
|
/**
|
|
* Creates a "_.pluck" style function, which returns the `key` value of a
|
|
* given object.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {string} key The name of the property to retrieve.
|
|
* @returns {Function} Returns the new function.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'fred', 'age': 40 },
|
|
* { 'name': 'barney', 'age': 36 }
|
|
* ];
|
|
*
|
|
* var getName = _.property('name');
|
|
*
|
|
* _.map(characters, getName);
|
|
* // => ['barney', 'fred']
|
|
*
|
|
* _.sortBy(characters, getName);
|
|
* // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }]
|
|
*/
|
|
function property(key) {
|
|
return function(object) {
|
|
return object[key];
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Produces a random number between `min` and `max` (inclusive). If only one
|
|
* argument is provided a number between `0` and the given number will be
|
|
* returned. If `floating` is truey or either `min` or `max` are floats a
|
|
* floating-point number will be returned instead of an integer.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {number} [min=0] The minimum possible value.
|
|
* @param {number} [max=1] The maximum possible value.
|
|
* @param {boolean} [floating=false] Specify returning a floating-point number.
|
|
* @returns {number} Returns a random number.
|
|
* @example
|
|
*
|
|
* _.random(0, 5);
|
|
* // => an integer between 0 and 5
|
|
*
|
|
* _.random(5);
|
|
* // => also an integer between 0 and 5
|
|
*
|
|
* _.random(5, true);
|
|
* // => a floating-point number between 0 and 5
|
|
*
|
|
* _.random(1.2, 5.2);
|
|
* // => a floating-point number between 1.2 and 5.2
|
|
*/
|
|
function random(min, max, floating) {
|
|
var noMin = min == null,
|
|
noMax = max == null;
|
|
|
|
if (floating == null) {
|
|
if (typeof min == 'boolean' && noMax) {
|
|
floating = min;
|
|
min = 1;
|
|
}
|
|
else if (!noMax && typeof max == 'boolean') {
|
|
floating = max;
|
|
noMax = true;
|
|
}
|
|
}
|
|
if (noMin && noMax) {
|
|
max = 1;
|
|
}
|
|
min = +min || 0;
|
|
if (noMax) {
|
|
max = min;
|
|
min = 0;
|
|
} else {
|
|
max = +max || 0;
|
|
}
|
|
if (floating || min % 1 || max % 1) {
|
|
var rand = nativeRandom();
|
|
return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
|
|
}
|
|
return baseRandom(min, max);
|
|
}
|
|
|
|
/**
|
|
* Resolves the value of property `key` on `object`. If `key` is a function
|
|
* it will be invoked with the `this` binding of `object` and its result returned,
|
|
* else the property value is returned. If `object` is falsey then `undefined`
|
|
* is returned.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {Object} object The object to inspect.
|
|
* @param {string} key The name of the property to resolve.
|
|
* @returns {*} Returns the resolved value.
|
|
* @example
|
|
*
|
|
* var object = {
|
|
* 'cheese': 'crumpets',
|
|
* 'stuff': function() {
|
|
* return 'nonsense';
|
|
* }
|
|
* };
|
|
*
|
|
* _.result(object, 'cheese');
|
|
* // => 'crumpets'
|
|
*
|
|
* _.result(object, 'stuff');
|
|
* // => 'nonsense'
|
|
*/
|
|
function result(object, key) {
|
|
if (object) {
|
|
var value = object[key];
|
|
return isFunction(value) ? object[key]() : value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A micro-templating method that handles arbitrary delimiters, preserves
|
|
* whitespace, and correctly escapes quotes within interpolated code.
|
|
*
|
|
* Note: In the development build, `_.template` utilizes sourceURLs for easier
|
|
* debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
|
|
*
|
|
* For more information on precompiling templates see:
|
|
* http://lodash.com/custom-builds
|
|
*
|
|
* For more information on Chrome extension sandboxes see:
|
|
* http://developer.chrome.com/stable/extensions/sandboxingEval.html
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {string} text The template text.
|
|
* @param {Object} data The data object used to populate the text.
|
|
* @param {Object} [options] The options object.
|
|
* @param {RegExp} [options.escape] The "escape" delimiter.
|
|
* @param {RegExp} [options.evaluate] The "evaluate" delimiter.
|
|
* @param {Object} [options.imports] An object to import into the template as local variables.
|
|
* @param {RegExp} [options.interpolate] The "interpolate" delimiter.
|
|
* @param {string} [sourceURL] The sourceURL of the template's compiled source.
|
|
* @param {string} [variable] The data object variable name.
|
|
* @returns {Function|string} Returns a compiled function when no `data` object
|
|
* is given, else it returns the interpolated text.
|
|
* @example
|
|
*
|
|
* // using the "interpolate" delimiter to create a compiled template
|
|
* var compiled = _.template('hello <%= name %>');
|
|
* compiled({ 'name': 'fred' });
|
|
* // => 'hello fred'
|
|
*
|
|
* // using the "escape" delimiter to escape HTML in data property values
|
|
* _.template('<b><%- value %></b>', { 'value': '<script>' });
|
|
* // => '<b><script></b>'
|
|
*
|
|
* // using the "evaluate" delimiter to generate HTML
|
|
* var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
|
|
* _.template(list, { 'people': ['fred', 'barney'] });
|
|
* // => '<li>fred</li><li>barney</li>'
|
|
*
|
|
* // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
|
|
* _.template('hello ${ name }', { 'name': 'pebbles' });
|
|
* // => 'hello pebbles'
|
|
*
|
|
* // using the internal `print` function in "evaluate" delimiters
|
|
* _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
|
|
* // => 'hello barney!'
|
|
*
|
|
* // using a custom template delimiters
|
|
* _.templateSettings = {
|
|
* 'interpolate': /{{([\s\S]+?)}}/g
|
|
* };
|
|
*
|
|
* _.template('hello {{ name }}!', { 'name': 'mustache' });
|
|
* // => 'hello mustache!'
|
|
*
|
|
* // using the `imports` option to import jQuery
|
|
* var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
|
|
* _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
|
|
* // => '<li>fred</li><li>barney</li>'
|
|
*
|
|
* // using the `sourceURL` option to specify a custom sourceURL for the template
|
|
* var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
|
|
* compiled(data);
|
|
* // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
|
|
*
|
|
* // using the `variable` option to ensure a with-statement isn't used in the compiled template
|
|
* var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
|
|
* compiled.source;
|
|
* // => function(data) {
|
|
* var __t, __p = '', __e = _.escape;
|
|
* __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
|
|
* return __p;
|
|
* }
|
|
*
|
|
* // using the `source` property to inline compiled templates for meaningful
|
|
* // line numbers in error messages and a stack trace
|
|
* fs.writeFileSync(path.join(cwd, 'jst.js'), '\
|
|
* var JST = {\
|
|
* "main": ' + _.template(mainText).source + '\
|
|
* };\
|
|
* ');
|
|
*/
|
|
function template(text, data, options) {
|
|
// based on John Resig's `tmpl` implementation
|
|
// http://ejohn.org/blog/javascript-micro-templating/
|
|
// and Laura Doktorova's doT.js
|
|
// https://github.com/olado/doT
|
|
var settings = lodash.templateSettings;
|
|
text = String(text || '');
|
|
|
|
// avoid missing dependencies when `iteratorTemplate` is not defined
|
|
options = defaults({}, options, settings);
|
|
|
|
var imports = defaults({}, options.imports, settings.imports),
|
|
importsKeys = keys(imports),
|
|
importsValues = values(imports);
|
|
|
|
var isEvaluating,
|
|
index = 0,
|
|
interpolate = options.interpolate || reNoMatch,
|
|
source = "__p += '";
|
|
|
|
// compile the regexp to match each delimiter
|
|
var reDelimiters = RegExp(
|
|
(options.escape || reNoMatch).source + '|' +
|
|
interpolate.source + '|' +
|
|
(interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
|
|
(options.evaluate || reNoMatch).source + '|$'
|
|
, 'g');
|
|
|
|
text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
|
|
interpolateValue || (interpolateValue = esTemplateValue);
|
|
|
|
// escape characters that cannot be included in string literals
|
|
source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
|
|
|
|
// replace delimiters with snippets
|
|
if (escapeValue) {
|
|
source += "' +\n__e(" + escapeValue + ") +\n'";
|
|
}
|
|
if (evaluateValue) {
|
|
isEvaluating = true;
|
|
source += "';\n" + evaluateValue + ";\n__p += '";
|
|
}
|
|
if (interpolateValue) {
|
|
source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
|
|
}
|
|
index = offset + match.length;
|
|
|
|
// the JS engine embedded in Adobe products requires returning the `match`
|
|
// string in order to produce the correct `offset` value
|
|
return match;
|
|
});
|
|
|
|
source += "';\n";
|
|
|
|
// if `variable` is not specified, wrap a with-statement around the generated
|
|
// code to add the data object to the top of the scope chain
|
|
var variable = options.variable,
|
|
hasVariable = variable;
|
|
|
|
if (!hasVariable) {
|
|
variable = 'obj';
|
|
source = 'with (' + variable + ') {\n' + source + '\n}\n';
|
|
}
|
|
// cleanup code by stripping empty strings
|
|
source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
|
|
.replace(reEmptyStringMiddle, '$1')
|
|
.replace(reEmptyStringTrailing, '$1;');
|
|
|
|
// frame code as the function body
|
|
source = 'function(' + variable + ') {\n' +
|
|
(hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
|
|
"var __t, __p = '', __e = _.escape" +
|
|
(isEvaluating
|
|
? ', __j = Array.prototype.join;\n' +
|
|
"function print() { __p += __j.call(arguments, '') }\n"
|
|
: ';\n'
|
|
) +
|
|
source +
|
|
'return __p\n}';
|
|
|
|
// Use a sourceURL for easier debugging.
|
|
// http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
|
|
var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
|
|
|
|
try {
|
|
var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
|
|
} catch(e) {
|
|
e.source = source;
|
|
throw e;
|
|
}
|
|
if (data) {
|
|
return result(data);
|
|
}
|
|
// provide the compiled function's source by its `toString` method, in
|
|
// supported environments, or the `source` property as a convenience for
|
|
// inlining compiled templates during the build process
|
|
result.source = source;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Executes the callback `n` times, returning an array of the results
|
|
* of each callback execution. The callback is bound to `thisArg` and invoked
|
|
* with one argument; (index).
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {number} n The number of times to execute the callback.
|
|
* @param {Function} callback The function called per iteration.
|
|
* @param {*} [thisArg] The `this` binding of `callback`.
|
|
* @returns {Array} Returns an array of the results of each `callback` execution.
|
|
* @example
|
|
*
|
|
* var diceRolls = _.times(3, _.partial(_.random, 1, 6));
|
|
* // => [3, 6, 4]
|
|
*
|
|
* _.times(3, function(n) { mage.castSpell(n); });
|
|
* // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
|
|
*
|
|
* _.times(3, function(n) { this.cast(n); }, mage);
|
|
* // => also calls `mage.castSpell(n)` three times
|
|
*/
|
|
function times(n, callback, thisArg) {
|
|
n = (n = +n) > -1 ? n : 0;
|
|
var index = -1,
|
|
result = Array(n);
|
|
|
|
callback = baseCreateCallback(callback, thisArg, 1);
|
|
while (++index < n) {
|
|
result[index] = callback(index);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* The inverse of `_.escape` this method converts the HTML entities
|
|
* `&`, `<`, `>`, `"`, and `'` in `string` to their
|
|
* corresponding characters.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @param {string} string The string to unescape.
|
|
* @returns {string} Returns the unescaped string.
|
|
* @example
|
|
*
|
|
* _.unescape('Fred, Barney & Pebbles');
|
|
* // => 'Fred, Barney & Pebbles'
|
|
*/
|
|
function unescape(string) {
|
|
return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
|
|
}
|
|
|
|
/**
|
|
* Generates a unique ID. If `prefix` is provided the ID will be appended to it.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Utilities
|
|
* @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 String(prefix == null ? '' : prefix) + id;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* Creates a `lodash` object that wraps the given value with explicit
|
|
* method chaining enabled.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Chaining
|
|
* @param {*} value The value to wrap.
|
|
* @returns {Object} Returns the wrapper object.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 },
|
|
* { 'name': 'pebbles', 'age': 1 }
|
|
* ];
|
|
*
|
|
* var youngest = _.chain(characters)
|
|
* .sortBy('age')
|
|
* .map(function(chr) { return chr.name + ' is ' + chr.age; })
|
|
* .first()
|
|
* .value();
|
|
* // => 'pebbles is 1'
|
|
*/
|
|
function chain(value) {
|
|
value = new lodashWrapper(value);
|
|
value.__chain__ = true;
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Invokes `interceptor` with the `value` as the first argument and then
|
|
* returns `value`. The purpose of this method is to "tap into" a method
|
|
* chain in order to perform operations on intermediate results within
|
|
* the chain.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @category Chaining
|
|
* @param {*} value The value to provide to `interceptor`.
|
|
* @param {Function} interceptor The function to invoke.
|
|
* @returns {*} Returns `value`.
|
|
* @example
|
|
*
|
|
* _([1, 2, 3, 4])
|
|
* .tap(function(array) { array.pop(); })
|
|
* .reverse()
|
|
* .value();
|
|
* // => [3, 2, 1]
|
|
*/
|
|
function tap(value, interceptor) {
|
|
interceptor(value);
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Enables explicit method chaining on the wrapper object.
|
|
*
|
|
* @name chain
|
|
* @memberOf _
|
|
* @category Chaining
|
|
* @returns {*} Returns the wrapper object.
|
|
* @example
|
|
*
|
|
* var characters = [
|
|
* { 'name': 'barney', 'age': 36 },
|
|
* { 'name': 'fred', 'age': 40 }
|
|
* ];
|
|
*
|
|
* // without explicit chaining
|
|
* _(characters).first();
|
|
* // => { 'name': 'barney', 'age': 36 }
|
|
*
|
|
* // with explicit chaining
|
|
* _(characters).chain()
|
|
* .first()
|
|
* .pick('age')
|
|
* .value();
|
|
* // => { 'age': 36 }
|
|
*/
|
|
function wrapperChain() {
|
|
this.__chain__ = true;
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Produces the `toString` result of the wrapped value.
|
|
*
|
|
* @name toString
|
|
* @memberOf _
|
|
* @category Chaining
|
|
* @returns {string} Returns the string result.
|
|
* @example
|
|
*
|
|
* _([1, 2, 3]).toString();
|
|
* // => '1,2,3'
|
|
*/
|
|
function wrapperToString() {
|
|
return String(this.__wrapped__);
|
|
}
|
|
|
|
/**
|
|
* Extracts the wrapped value.
|
|
*
|
|
* @name valueOf
|
|
* @memberOf _
|
|
* @alias value
|
|
* @category Chaining
|
|
* @returns {*} Returns the wrapped value.
|
|
* @example
|
|
*
|
|
* _([1, 2, 3]).valueOf();
|
|
* // => [1, 2, 3]
|
|
*/
|
|
function wrapperValueOf() {
|
|
return this.__wrapped__;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// add functions that return wrapped values when chaining
|
|
lodash.after = after;
|
|
lodash.assign = assign;
|
|
lodash.at = at;
|
|
lodash.bind = bind;
|
|
lodash.bindAll = bindAll;
|
|
lodash.bindKey = bindKey;
|
|
lodash.chain = chain;
|
|
lodash.compact = compact;
|
|
lodash.compose = compose;
|
|
lodash.constant = constant;
|
|
lodash.countBy = countBy;
|
|
lodash.create = create;
|
|
lodash.createCallback = createCallback;
|
|
lodash.curry = curry;
|
|
lodash.debounce = debounce;
|
|
lodash.defaults = defaults;
|
|
lodash.defer = defer;
|
|
lodash.delay = delay;
|
|
lodash.difference = difference;
|
|
lodash.filter = filter;
|
|
lodash.flatten = flatten;
|
|
lodash.forEach = forEach;
|
|
lodash.forEachRight = forEachRight;
|
|
lodash.forIn = forIn;
|
|
lodash.forInRight = forInRight;
|
|
lodash.forOwn = forOwn;
|
|
lodash.forOwnRight = forOwnRight;
|
|
lodash.functions = functions;
|
|
lodash.groupBy = groupBy;
|
|
lodash.indexBy = indexBy;
|
|
lodash.initial = initial;
|
|
lodash.intersection = intersection;
|
|
lodash.invert = invert;
|
|
lodash.invoke = invoke;
|
|
lodash.keys = keys;
|
|
lodash.map = map;
|
|
lodash.mapValues = mapValues;
|
|
lodash.max = max;
|
|
lodash.memoize = memoize;
|
|
lodash.merge = merge;
|
|
lodash.min = min;
|
|
lodash.omit = omit;
|
|
lodash.once = once;
|
|
lodash.pairs = pairs;
|
|
lodash.partial = partial;
|
|
lodash.partialRight = partialRight;
|
|
lodash.pick = pick;
|
|
lodash.pluck = pluck;
|
|
lodash.property = property;
|
|
lodash.pull = pull;
|
|
lodash.range = range;
|
|
lodash.reject = reject;
|
|
lodash.remove = remove;
|
|
lodash.rest = rest;
|
|
lodash.shuffle = shuffle;
|
|
lodash.sortBy = sortBy;
|
|
lodash.tap = tap;
|
|
lodash.throttle = throttle;
|
|
lodash.times = times;
|
|
lodash.toArray = toArray;
|
|
lodash.transform = transform;
|
|
lodash.union = union;
|
|
lodash.uniq = uniq;
|
|
lodash.values = values;
|
|
lodash.where = where;
|
|
lodash.without = without;
|
|
lodash.wrap = wrap;
|
|
lodash.xor = xor;
|
|
lodash.zip = zip;
|
|
lodash.zipObject = zipObject;
|
|
|
|
// add aliases
|
|
lodash.collect = map;
|
|
lodash.drop = rest;
|
|
lodash.each = forEach;
|
|
lodash.eachRight = forEachRight;
|
|
lodash.extend = assign;
|
|
lodash.methods = functions;
|
|
lodash.object = zipObject;
|
|
lodash.select = filter;
|
|
lodash.tail = rest;
|
|
lodash.unique = uniq;
|
|
lodash.unzip = zip;
|
|
|
|
// add functions to `lodash.prototype`
|
|
mixin(lodash);
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// add functions that return unwrapped values when chaining
|
|
lodash.clone = clone;
|
|
lodash.cloneDeep = cloneDeep;
|
|
lodash.contains = contains;
|
|
lodash.escape = escape;
|
|
lodash.every = every;
|
|
lodash.find = find;
|
|
lodash.findIndex = findIndex;
|
|
lodash.findKey = findKey;
|
|
lodash.findLast = findLast;
|
|
lodash.findLastIndex = findLastIndex;
|
|
lodash.findLastKey = findLastKey;
|
|
lodash.has = has;
|
|
lodash.identity = identity;
|
|
lodash.indexOf = indexOf;
|
|
lodash.isArguments = isArguments;
|
|
lodash.isArray = isArray;
|
|
lodash.isBoolean = isBoolean;
|
|
lodash.isDate = isDate;
|
|
lodash.isElement = isElement;
|
|
lodash.isEmpty = isEmpty;
|
|
lodash.isEqual = isEqual;
|
|
lodash.isFinite = isFinite;
|
|
lodash.isFunction = isFunction;
|
|
lodash.isNaN = isNaN;
|
|
lodash.isNull = isNull;
|
|
lodash.isNumber = isNumber;
|
|
lodash.isObject = isObject;
|
|
lodash.isPlainObject = isPlainObject;
|
|
lodash.isRegExp = isRegExp;
|
|
lodash.isString = isString;
|
|
lodash.isUndefined = isUndefined;
|
|
lodash.lastIndexOf = lastIndexOf;
|
|
lodash.mixin = mixin;
|
|
lodash.noConflict = noConflict;
|
|
lodash.noop = noop;
|
|
lodash.now = now;
|
|
lodash.parseInt = parseInt;
|
|
lodash.random = random;
|
|
lodash.reduce = reduce;
|
|
lodash.reduceRight = reduceRight;
|
|
lodash.result = result;
|
|
lodash.runInContext = runInContext;
|
|
lodash.size = size;
|
|
lodash.some = some;
|
|
lodash.sortedIndex = sortedIndex;
|
|
lodash.template = template;
|
|
lodash.unescape = unescape;
|
|
lodash.uniqueId = uniqueId;
|
|
|
|
// add aliases
|
|
lodash.all = every;
|
|
lodash.any = some;
|
|
lodash.detect = find;
|
|
lodash.findWhere = find;
|
|
lodash.foldl = reduce;
|
|
lodash.foldr = reduceRight;
|
|
lodash.include = contains;
|
|
lodash.inject = reduce;
|
|
|
|
mixin(function() {
|
|
var source = {}
|
|
forOwn(lodash, function(func, methodName) {
|
|
if (!lodash.prototype[methodName]) {
|
|
source[methodName] = func;
|
|
}
|
|
});
|
|
return source;
|
|
}(), false);
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// add functions capable of returning wrapped and unwrapped values when chaining
|
|
lodash.first = first;
|
|
lodash.last = last;
|
|
lodash.sample = sample;
|
|
|
|
// add aliases
|
|
lodash.take = first;
|
|
lodash.head = first;
|
|
|
|
forOwn(lodash, function(func, methodName) {
|
|
var callbackable = methodName !== 'sample';
|
|
if (!lodash.prototype[methodName]) {
|
|
lodash.prototype[methodName]= function(n, guard) {
|
|
var chainAll = this.__chain__,
|
|
result = func(this.__wrapped__, n, guard);
|
|
|
|
return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
|
|
? result
|
|
: new lodashWrapper(result, chainAll);
|
|
};
|
|
}
|
|
});
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
/**
|
|
* The semantic version number.
|
|
*
|
|
* @static
|
|
* @memberOf _
|
|
* @type string
|
|
*/
|
|
lodash.VERSION = '2.4.1';
|
|
|
|
// add "Chaining" functions to the wrapper
|
|
lodash.prototype.chain = wrapperChain;
|
|
lodash.prototype.toString = wrapperToString;
|
|
lodash.prototype.value = wrapperValueOf;
|
|
lodash.prototype.valueOf = wrapperValueOf;
|
|
|
|
// add `Array` functions that return unwrapped values
|
|
forEach(['join', 'pop', 'shift'], function(methodName) {
|
|
var func = arrayRef[methodName];
|
|
lodash.prototype[methodName] = function() {
|
|
var chainAll = this.__chain__,
|
|
result = func.apply(this.__wrapped__, arguments);
|
|
|
|
return chainAll
|
|
? new lodashWrapper(result, chainAll)
|
|
: result;
|
|
};
|
|
});
|
|
|
|
// add `Array` functions that return the existing wrapped value
|
|
forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
|
|
var func = arrayRef[methodName];
|
|
lodash.prototype[methodName] = function() {
|
|
func.apply(this.__wrapped__, arguments);
|
|
return this;
|
|
};
|
|
});
|
|
|
|
// add `Array` functions that return new wrapped values
|
|
forEach(['concat', 'slice', 'splice'], function(methodName) {
|
|
var func = arrayRef[methodName];
|
|
lodash.prototype[methodName] = function() {
|
|
return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
|
|
};
|
|
});
|
|
|
|
return lodash;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
|
|
// expose Lo-Dash
|
|
var _ = runInContext();
|
|
|
|
// 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 Lo-Dash to the global object even when an AMD loader is present in
|
|
// case Lo-Dash is loaded with a RequireJS shim config.
|
|
// See http://requirejs.org/docs/api.html#config-shim
|
|
root._ = _;
|
|
|
|
// define as an anonymous module so, through path mapping, it can be
|
|
// referenced as the "underscore" module
|
|
define('lodash',[],function() {
|
|
return _;
|
|
});
|
|
}
|
|
// check for `exports` after `define` in case a build optimizer adds an `exports` object
|
|
else if (freeExports && freeModule) {
|
|
// in Node.js or RingoJS
|
|
if (moduleExports) {
|
|
(freeModule.exports = _)._ = _;
|
|
}
|
|
// in Narwhal or Rhino -require
|
|
else {
|
|
freeExports._ = _;
|
|
}
|
|
}
|
|
else {
|
|
// in a browser or Rhino
|
|
root._ = _;
|
|
}
|
|
}.call(this));
|
|
|
|
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" ");
|
|
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
|
define("mixpanel", (function (global) {
|
|
return function () {
|
|
var ret, fn;
|
|
return ret || global.mixpanel;
|
|
};
|
|
}(this)));
|
|
|
|
define('utils/analytic',[
|
|
"lodash",
|
|
"mixpanel"
|
|
], function(_, mixpanel) {
|
|
mixpanel.init("01eb2b950ae09a5fdb15a98dcc5ff20e", {
|
|
loaded: function() {
|
|
track("View");
|
|
}
|
|
});
|
|
|
|
var isAvailable = function() {
|
|
return (
|
|
typeof mixpanel !== "undefined" &&
|
|
typeof mixpanel.track === "function"
|
|
);
|
|
};
|
|
|
|
var track = function(e, data) {
|
|
if (!isAvailable()) {
|
|
console.warn("tracking not available!");
|
|
return;
|
|
}
|
|
console.log("track", e);
|
|
mixpanel.track(e, _.extend(data || {}, {
|
|
'domain': window.location.host
|
|
}));
|
|
};
|
|
|
|
return {
|
|
isAvailable: isAvailable,
|
|
track: track
|
|
};
|
|
});
|
|
define('utils/sharing',[
|
|
"jQuery"
|
|
], function($) {
|
|
var url = location.href;
|
|
var title = $("title").text();
|
|
|
|
var types = {
|
|
"twitter": function($el) {
|
|
window.open("http://twitter.com/home?status="+encodeURIComponent(title+" "+url))
|
|
},
|
|
"facebook": function($el) {
|
|
window.open("http://www.facebook.com/sharer/sharer.php?s=100&p[url]="+encodeURIComponent(url))
|
|
},
|
|
"google-plus": function($el) {
|
|
window.open("https://plus.google.com/share?url="+encodeURIComponent(url))
|
|
}
|
|
};
|
|
|
|
|
|
// Bind all sharing button
|
|
var init = function() {
|
|
$("a[data-sharing]").click(function(e) {
|
|
if (e) e.preventDefault();
|
|
var type = $(this).data("sharing");
|
|
|
|
types[type]($(this));
|
|
})
|
|
};
|
|
|
|
return {
|
|
init: init
|
|
};
|
|
});
|
|
define('core/state',[
|
|
"jQuery"
|
|
], function() {
|
|
var $book = $(".book");
|
|
|
|
return {
|
|
'$book': $book,
|
|
|
|
'githubId': $book.data("github"),
|
|
'level': $book.data("level"),
|
|
'basePath': $book.data("basepath"),
|
|
'revision': $book.data("revision")
|
|
};
|
|
});
|
|
/*global define:false */
|
|
/**
|
|
* Copyright 2013 Craig Campbell
|
|
*
|
|
* 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.
|
|
*
|
|
* Mousetrap is a simple keyboard shortcut library for Javascript with
|
|
* no external dependencies
|
|
*
|
|
* @version 1.4.6
|
|
* @url craig.is/killing/mice
|
|
*/
|
|
(function(window, document, undefined) {
|
|
|
|
/**
|
|
* mapping of special keycodes to their corresponding keys
|
|
*
|
|
* everything in this dictionary cannot use keypress events
|
|
* so it has to be here to map to the correct keycodes for
|
|
* keyup/keydown events
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
var _MAP = {
|
|
8: 'backspace',
|
|
9: 'tab',
|
|
13: 'enter',
|
|
16: 'shift',
|
|
17: 'ctrl',
|
|
18: 'alt',
|
|
20: 'capslock',
|
|
27: 'esc',
|
|
32: 'space',
|
|
33: 'pageup',
|
|
34: 'pagedown',
|
|
35: 'end',
|
|
36: 'home',
|
|
37: 'left',
|
|
38: 'up',
|
|
39: 'right',
|
|
40: 'down',
|
|
45: 'ins',
|
|
46: 'del',
|
|
91: 'meta',
|
|
93: 'meta',
|
|
224: 'meta'
|
|
},
|
|
|
|
/**
|
|
* mapping for special characters so they can support
|
|
*
|
|
* this dictionary is only used incase you want to bind a
|
|
* keyup or keydown event to one of these keys
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_KEYCODE_MAP = {
|
|
106: '*',
|
|
107: '+',
|
|
109: '-',
|
|
110: '.',
|
|
111 : '/',
|
|
186: ';',
|
|
187: '=',
|
|
188: ',',
|
|
189: '-',
|
|
190: '.',
|
|
191: '/',
|
|
192: '`',
|
|
219: '[',
|
|
220: '\\',
|
|
221: ']',
|
|
222: '\''
|
|
},
|
|
|
|
/**
|
|
* this is a mapping of keys that require shift on a US keypad
|
|
* back to the non shift equivelents
|
|
*
|
|
* this is so you can use keyup events with these keys
|
|
*
|
|
* note that this will only work reliably on US keyboards
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_SHIFT_MAP = {
|
|
'~': '`',
|
|
'!': '1',
|
|
'@': '2',
|
|
'#': '3',
|
|
'$': '4',
|
|
'%': '5',
|
|
'^': '6',
|
|
'&': '7',
|
|
'*': '8',
|
|
'(': '9',
|
|
')': '0',
|
|
'_': '-',
|
|
'+': '=',
|
|
':': ';',
|
|
'\"': '\'',
|
|
'<': ',',
|
|
'>': '.',
|
|
'?': '/',
|
|
'|': '\\'
|
|
},
|
|
|
|
/**
|
|
* this is a list of special strings you can use to map
|
|
* to modifier keys when you specify your keyboard shortcuts
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_SPECIAL_ALIASES = {
|
|
'option': 'alt',
|
|
'command': 'meta',
|
|
'return': 'enter',
|
|
'escape': 'esc',
|
|
'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
|
|
},
|
|
|
|
/**
|
|
* variable to store the flipped version of _MAP from above
|
|
* needed to check if we should use keypress or not when no action
|
|
* is specified
|
|
*
|
|
* @type {Object|undefined}
|
|
*/
|
|
_REVERSE_MAP,
|
|
|
|
/**
|
|
* a list of all the callbacks setup via Mousetrap.bind()
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_callbacks = {},
|
|
|
|
/**
|
|
* direct map of string combinations to callbacks used for trigger()
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_directMap = {},
|
|
|
|
/**
|
|
* keeps track of what level each sequence is at since multiple
|
|
* sequences can start out with the same sequence
|
|
*
|
|
* @type {Object}
|
|
*/
|
|
_sequenceLevels = {},
|
|
|
|
/**
|
|
* variable to store the setTimeout call
|
|
*
|
|
* @type {null|number}
|
|
*/
|
|
_resetTimer,
|
|
|
|
/**
|
|
* temporary state where we will ignore the next keyup
|
|
*
|
|
* @type {boolean|string}
|
|
*/
|
|
_ignoreNextKeyup = false,
|
|
|
|
/**
|
|
* temporary state where we will ignore the next keypress
|
|
*
|
|
* @type {boolean}
|
|
*/
|
|
_ignoreNextKeypress = false,
|
|
|
|
/**
|
|
* are we currently inside of a sequence?
|
|
* type of action ("keyup" or "keydown" or "keypress") or false
|
|
*
|
|
* @type {boolean|string}
|
|
*/
|
|
_nextExpectedAction = false;
|
|
|
|
/**
|
|
* loop through the f keys, f1 to f19 and add them to the map
|
|
* programatically
|
|
*/
|
|
for (var i = 1; i < 20; ++i) {
|
|
_MAP[111 + i] = 'f' + i;
|
|
}
|
|
|
|
/**
|
|
* loop through to map numbers on the numeric keypad
|
|
*/
|
|
for (i = 0; i <= 9; ++i) {
|
|
_MAP[i + 96] = i;
|
|
}
|
|
|
|
/**
|
|
* cross browser add event method
|
|
*
|
|
* @param {Element|HTMLDocument} object
|
|
* @param {string} type
|
|
* @param {Function} callback
|
|
* @returns void
|
|
*/
|
|
function _addEvent(object, type, callback) {
|
|
if (object.addEventListener) {
|
|
object.addEventListener(type, callback, false);
|
|
return;
|
|
}
|
|
|
|
object.attachEvent('on' + type, callback);
|
|
}
|
|
|
|
/**
|
|
* takes the event and returns the key character
|
|
*
|
|
* @param {Event} e
|
|
* @return {string}
|
|
*/
|
|
function _characterFromEvent(e) {
|
|
|
|
// for keypress events we should return the character as is
|
|
if (e.type == 'keypress') {
|
|
var character = String.fromCharCode(e.which);
|
|
|
|
// if the shift key is not pressed then it is safe to assume
|
|
// that we want the character to be lowercase. this means if
|
|
// you accidentally have caps lock on then your key bindings
|
|
// will continue to work
|
|
//
|
|
// the only side effect that might not be desired is if you
|
|
// bind something like 'A' cause you want to trigger an
|
|
// event when capital A is pressed caps lock will no longer
|
|
// trigger the event. shift+a will though.
|
|
if (!e.shiftKey) {
|
|
character = character.toLowerCase();
|
|
}
|
|
|
|
return character;
|
|
}
|
|
|
|
// for non keypress events the special maps are needed
|
|
if (_MAP[e.which]) {
|
|
return _MAP[e.which];
|
|
}
|
|
|
|
if (_KEYCODE_MAP[e.which]) {
|
|
return _KEYCODE_MAP[e.which];
|
|
}
|
|
|
|
// if it is not in the special map
|
|
|
|
// with keydown and keyup events the character seems to always
|
|
// come in as an uppercase character whether you are pressing shift
|
|
// or not. we should make sure it is always lowercase for comparisons
|
|
return String.fromCharCode(e.which).toLowerCase();
|
|
}
|
|
|
|
/**
|
|
* checks if two arrays are equal
|
|
*
|
|
* @param {Array} modifiers1
|
|
* @param {Array} modifiers2
|
|
* @returns {boolean}
|
|
*/
|
|
function _modifiersMatch(modifiers1, modifiers2) {
|
|
return modifiers1.sort().join(',') === modifiers2.sort().join(',');
|
|
}
|
|
|
|
/**
|
|
* resets all sequence counters except for the ones passed in
|
|
*
|
|
* @param {Object} doNotReset
|
|
* @returns void
|
|
*/
|
|
function _resetSequences(doNotReset) {
|
|
doNotReset = doNotReset || {};
|
|
|
|
var activeSequences = false,
|
|
key;
|
|
|
|
for (key in _sequenceLevels) {
|
|
if (doNotReset[key]) {
|
|
activeSequences = true;
|
|
continue;
|
|
}
|
|
_sequenceLevels[key] = 0;
|
|
}
|
|
|
|
if (!activeSequences) {
|
|
_nextExpectedAction = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* finds all callbacks that match based on the keycode, modifiers,
|
|
* and action
|
|
*
|
|
* @param {string} character
|
|
* @param {Array} modifiers
|
|
* @param {Event|Object} e
|
|
* @param {string=} sequenceName - name of the sequence we are looking for
|
|
* @param {string=} combination
|
|
* @param {number=} level
|
|
* @returns {Array}
|
|
*/
|
|
function _getMatches(character, modifiers, e, sequenceName, combination, level) {
|
|
var i,
|
|
callback,
|
|
matches = [],
|
|
action = e.type;
|
|
|
|
// if there are no events related to this keycode
|
|
if (!_callbacks[character]) {
|
|
return [];
|
|
}
|
|
|
|
// if a modifier key is coming up on its own we should allow it
|
|
if (action == 'keyup' && _isModifier(character)) {
|
|
modifiers = [character];
|
|
}
|
|
|
|
// loop through all callbacks for the key that was pressed
|
|
// and see if any of them match
|
|
for (i = 0; i < _callbacks[character].length; ++i) {
|
|
callback = _callbacks[character][i];
|
|
|
|
// if a sequence name is not specified, but this is a sequence at
|
|
// the wrong level then move onto the next match
|
|
if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
|
|
continue;
|
|
}
|
|
|
|
// if the action we are looking for doesn't match the action we got
|
|
// then we should keep going
|
|
if (action != callback.action) {
|
|
continue;
|
|
}
|
|
|
|
// if this is a keypress event and the meta key and control key
|
|
// are not pressed that means that we need to only look at the
|
|
// character, otherwise check the modifiers as well
|
|
//
|
|
// chrome will not fire a keypress if meta or control is down
|
|
// safari will fire a keypress if meta or meta+shift is down
|
|
// firefox will fire a keypress if meta or control is down
|
|
if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
|
|
|
|
// when you bind a combination or sequence a second time it
|
|
// should overwrite the first one. if a sequenceName or
|
|
// combination is specified in this call it does just that
|
|
//
|
|
// @todo make deleting its own method?
|
|
var deleteCombo = !sequenceName && callback.combo == combination;
|
|
var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
|
|
if (deleteCombo || deleteSequence) {
|
|
_callbacks[character].splice(i, 1);
|
|
}
|
|
|
|
matches.push(callback);
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
/**
|
|
* takes a key event and figures out what the modifiers are
|
|
*
|
|
* @param {Event} e
|
|
* @returns {Array}
|
|
*/
|
|
function _eventModifiers(e) {
|
|
var modifiers = [];
|
|
|
|
if (e.shiftKey) {
|
|
modifiers.push('shift');
|
|
}
|
|
|
|
if (e.altKey) {
|
|
modifiers.push('alt');
|
|
}
|
|
|
|
if (e.ctrlKey) {
|
|
modifiers.push('ctrl');
|
|
}
|
|
|
|
if (e.metaKey) {
|
|
modifiers.push('meta');
|
|
}
|
|
|
|
return modifiers;
|
|
}
|
|
|
|
/**
|
|
* prevents default for this event
|
|
*
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _preventDefault(e) {
|
|
if (e.preventDefault) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
|
|
e.returnValue = false;
|
|
}
|
|
|
|
/**
|
|
* stops propogation for this event
|
|
*
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _stopPropagation(e) {
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
e.cancelBubble = true;
|
|
}
|
|
|
|
/**
|
|
* actually calls the callback function
|
|
*
|
|
* if your callback function returns false this will use the jquery
|
|
* convention - prevent default and stop propogation on the event
|
|
*
|
|
* @param {Function} callback
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _fireCallback(callback, e, combo, sequence) {
|
|
|
|
// if this event should not happen stop here
|
|
if (Mousetrap.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
|
|
return;
|
|
}
|
|
|
|
if (callback(e, combo) === false) {
|
|
_preventDefault(e);
|
|
_stopPropagation(e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* handles a character key event
|
|
*
|
|
* @param {string} character
|
|
* @param {Array} modifiers
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _handleKey(character, modifiers, e) {
|
|
var callbacks = _getMatches(character, modifiers, e),
|
|
i,
|
|
doNotReset = {},
|
|
maxLevel = 0,
|
|
processedSequenceCallback = false;
|
|
|
|
// Calculate the maxLevel for sequences so we can only execute the longest callback sequence
|
|
for (i = 0; i < callbacks.length; ++i) {
|
|
if (callbacks[i].seq) {
|
|
maxLevel = Math.max(maxLevel, callbacks[i].level);
|
|
}
|
|
}
|
|
|
|
// loop through matching callbacks for this key event
|
|
for (i = 0; i < callbacks.length; ++i) {
|
|
|
|
// fire for all sequence callbacks
|
|
// this is because if for example you have multiple sequences
|
|
// bound such as "g i" and "g t" they both need to fire the
|
|
// callback for matching g cause otherwise you can only ever
|
|
// match the first one
|
|
if (callbacks[i].seq) {
|
|
|
|
// only fire callbacks for the maxLevel to prevent
|
|
// subsequences from also firing
|
|
//
|
|
// for example 'a option b' should not cause 'option b' to fire
|
|
// even though 'option b' is part of the other sequence
|
|
//
|
|
// any sequences that do not match here will be discarded
|
|
// below by the _resetSequences call
|
|
if (callbacks[i].level != maxLevel) {
|
|
continue;
|
|
}
|
|
|
|
processedSequenceCallback = true;
|
|
|
|
// keep a list of which sequences were matches for later
|
|
doNotReset[callbacks[i].seq] = 1;
|
|
_fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
|
|
continue;
|
|
}
|
|
|
|
// if there were no sequence matches but we are still here
|
|
// that means this is a regular match so we should fire that
|
|
if (!processedSequenceCallback) {
|
|
_fireCallback(callbacks[i].callback, e, callbacks[i].combo);
|
|
}
|
|
}
|
|
|
|
// if the key you pressed matches the type of sequence without
|
|
// being a modifier (ie "keyup" or "keypress") then we should
|
|
// reset all sequences that were not matched by this event
|
|
//
|
|
// this is so, for example, if you have the sequence "h a t" and you
|
|
// type "h e a r t" it does not match. in this case the "e" will
|
|
// cause the sequence to reset
|
|
//
|
|
// modifier keys are ignored because you can have a sequence
|
|
// that contains modifiers such as "enter ctrl+space" and in most
|
|
// cases the modifier key will be pressed before the next key
|
|
//
|
|
// also if you have a sequence such as "ctrl+b a" then pressing the
|
|
// "b" key will trigger a "keypress" and a "keydown"
|
|
//
|
|
// the "keydown" is expected when there is a modifier, but the
|
|
// "keypress" ends up matching the _nextExpectedAction since it occurs
|
|
// after and that causes the sequence to reset
|
|
//
|
|
// we ignore keypresses in a sequence that directly follow a keydown
|
|
// for the same character
|
|
var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
|
|
if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
|
|
_resetSequences(doNotReset);
|
|
}
|
|
|
|
_ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
|
|
}
|
|
|
|
/**
|
|
* handles a keydown event
|
|
*
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _handleKeyEvent(e) {
|
|
|
|
// normalize e.which for key events
|
|
// @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
|
|
if (typeof e.which !== 'number') {
|
|
e.which = e.keyCode;
|
|
}
|
|
|
|
var character = _characterFromEvent(e);
|
|
|
|
// no character found then stop
|
|
if (!character) {
|
|
return;
|
|
}
|
|
|
|
// need to use === for the character check because the character can be 0
|
|
if (e.type == 'keyup' && _ignoreNextKeyup === character) {
|
|
_ignoreNextKeyup = false;
|
|
return;
|
|
}
|
|
|
|
Mousetrap.handleKey(character, _eventModifiers(e), e);
|
|
}
|
|
|
|
/**
|
|
* determines if the keycode specified is a modifier key or not
|
|
*
|
|
* @param {string} key
|
|
* @returns {boolean}
|
|
*/
|
|
function _isModifier(key) {
|
|
return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
|
|
}
|
|
|
|
/**
|
|
* called to set a 1 second timeout on the specified sequence
|
|
*
|
|
* this is so after each key press in the sequence you have 1 second
|
|
* to press the next key before you have to start over
|
|
*
|
|
* @returns void
|
|
*/
|
|
function _resetSequenceTimer() {
|
|
clearTimeout(_resetTimer);
|
|
_resetTimer = setTimeout(_resetSequences, 1000);
|
|
}
|
|
|
|
/**
|
|
* reverses the map lookup so that we can look for specific keys
|
|
* to see what can and can't use keypress
|
|
*
|
|
* @return {Object}
|
|
*/
|
|
function _getReverseMap() {
|
|
if (!_REVERSE_MAP) {
|
|
_REVERSE_MAP = {};
|
|
for (var key in _MAP) {
|
|
|
|
// pull out the numeric keypad from here cause keypress should
|
|
// be able to detect the keys from the character
|
|
if (key > 95 && key < 112) {
|
|
continue;
|
|
}
|
|
|
|
if (_MAP.hasOwnProperty(key)) {
|
|
_REVERSE_MAP[_MAP[key]] = key;
|
|
}
|
|
}
|
|
}
|
|
return _REVERSE_MAP;
|
|
}
|
|
|
|
/**
|
|
* picks the best action based on the key combination
|
|
*
|
|
* @param {string} key - character for key
|
|
* @param {Array} modifiers
|
|
* @param {string=} action passed in
|
|
*/
|
|
function _pickBestAction(key, modifiers, action) {
|
|
|
|
// if no action was picked in we should try to pick the one
|
|
// that we think would work best for this key
|
|
if (!action) {
|
|
action = _getReverseMap()[key] ? 'keydown' : 'keypress';
|
|
}
|
|
|
|
// modifier keys don't work as expected with keypress,
|
|
// switch to keydown
|
|
if (action == 'keypress' && modifiers.length) {
|
|
action = 'keydown';
|
|
}
|
|
|
|
return action;
|
|
}
|
|
|
|
/**
|
|
* binds a key sequence to an event
|
|
*
|
|
* @param {string} combo - combo specified in bind call
|
|
* @param {Array} keys
|
|
* @param {Function} callback
|
|
* @param {string=} action
|
|
* @returns void
|
|
*/
|
|
function _bindSequence(combo, keys, callback, action) {
|
|
|
|
// start off by adding a sequence level record for this combination
|
|
// and setting the level to 0
|
|
_sequenceLevels[combo] = 0;
|
|
|
|
/**
|
|
* callback to increase the sequence level for this sequence and reset
|
|
* all other sequences that were active
|
|
*
|
|
* @param {string} nextAction
|
|
* @returns {Function}
|
|
*/
|
|
function _increaseSequence(nextAction) {
|
|
return function() {
|
|
_nextExpectedAction = nextAction;
|
|
++_sequenceLevels[combo];
|
|
_resetSequenceTimer();
|
|
};
|
|
}
|
|
|
|
/**
|
|
* wraps the specified callback inside of another function in order
|
|
* to reset all sequence counters as soon as this sequence is done
|
|
*
|
|
* @param {Event} e
|
|
* @returns void
|
|
*/
|
|
function _callbackAndReset(e) {
|
|
_fireCallback(callback, e, combo);
|
|
|
|
// we should ignore the next key up if the action is key down
|
|
// or keypress. this is so if you finish a sequence and
|
|
// release the key the final key will not trigger a keyup
|
|
if (action !== 'keyup') {
|
|
_ignoreNextKeyup = _characterFromEvent(e);
|
|
}
|
|
|
|
// weird race condition if a sequence ends with the key
|
|
// another sequence begins with
|
|
setTimeout(_resetSequences, 10);
|
|
}
|
|
|
|
// loop through keys one at a time and bind the appropriate callback
|
|
// function. for any key leading up to the final one it should
|
|
// increase the sequence. after the final, it should reset all sequences
|
|
//
|
|
// if an action is specified in the original bind call then that will
|
|
// be used throughout. otherwise we will pass the action that the
|
|
// next key in the sequence should match. this allows a sequence
|
|
// to mix and match keypress and keydown events depending on which
|
|
// ones are better suited to the key provided
|
|
for (var i = 0; i < keys.length; ++i) {
|
|
var isFinal = i + 1 === keys.length;
|
|
var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
|
|
_bindSingle(keys[i], wrappedCallback, action, combo, i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts from a string key combination to an array
|
|
*
|
|
* @param {string} combination like "command+shift+l"
|
|
* @return {Array}
|
|
*/
|
|
function _keysFromString(combination) {
|
|
if (combination === '+') {
|
|
return ['+'];
|
|
}
|
|
|
|
return combination.split('+');
|
|
}
|
|
|
|
/**
|
|
* Gets info for a specific key combination
|
|
*
|
|
* @param {string} combination key combination ("command+s" or "a" or "*")
|
|
* @param {string=} action
|
|
* @returns {Object}
|
|
*/
|
|
function _getKeyInfo(combination, action) {
|
|
var keys,
|
|
key,
|
|
i,
|
|
modifiers = [];
|
|
|
|
// take the keys from this pattern and figure out what the actual
|
|
// pattern is all about
|
|
keys = _keysFromString(combination);
|
|
|
|
for (i = 0; i < keys.length; ++i) {
|
|
key = keys[i];
|
|
|
|
// normalize key names
|
|
if (_SPECIAL_ALIASES[key]) {
|
|
key = _SPECIAL_ALIASES[key];
|
|
}
|
|
|
|
// if this is not a keypress event then we should
|
|
// be smart about using shift keys
|
|
// this will only work for US keyboards however
|
|
if (action && action != 'keypress' && _SHIFT_MAP[key]) {
|
|
key = _SHIFT_MAP[key];
|
|
modifiers.push('shift');
|
|
}
|
|
|
|
// if this key is a modifier then add it to the list of modifiers
|
|
if (_isModifier(key)) {
|
|
modifiers.push(key);
|
|
}
|
|
}
|
|
|
|
// depending on what the key combination is
|
|
// we will try to pick the best event for it
|
|
action = _pickBestAction(key, modifiers, action);
|
|
|
|
return {
|
|
key: key,
|
|
modifiers: modifiers,
|
|
action: action
|
|
};
|
|
}
|
|
|
|
/**
|
|
* binds a single keyboard combination
|
|
*
|
|
* @param {string} combination
|
|
* @param {Function} callback
|
|
* @param {string=} action
|
|
* @param {string=} sequenceName - name of sequence if part of sequence
|
|
* @param {number=} level - what part of the sequence the command is
|
|
* @returns void
|
|
*/
|
|
function _bindSingle(combination, callback, action, sequenceName, level) {
|
|
|
|
// store a direct mapped reference for use with Mousetrap.trigger
|
|
_directMap[combination + ':' + action] = callback;
|
|
|
|
// make sure multiple spaces in a row become a single space
|
|
combination = combination.replace(/\s+/g, ' ');
|
|
|
|
var sequence = combination.split(' '),
|
|
info;
|
|
|
|
// if this pattern is a sequence of keys then run through this method
|
|
// to reprocess each pattern one key at a time
|
|
if (sequence.length > 1) {
|
|
_bindSequence(combination, sequence, callback, action);
|
|
return;
|
|
}
|
|
|
|
info = _getKeyInfo(combination, action);
|
|
|
|
// make sure to initialize array if this is the first time
|
|
// a callback is added for this key
|
|
_callbacks[info.key] = _callbacks[info.key] || [];
|
|
|
|
// remove an existing match if there is one
|
|
_getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
|
|
|
|
// add this call back to the array
|
|
// if it is a sequence put it at the beginning
|
|
// if not put it at the end
|
|
//
|
|
// this is important because the way these are processed expects
|
|
// the sequence ones to come first
|
|
_callbacks[info.key][sequenceName ? 'unshift' : 'push']({
|
|
callback: callback,
|
|
modifiers: info.modifiers,
|
|
action: info.action,
|
|
seq: sequenceName,
|
|
level: level,
|
|
combo: combination
|
|
});
|
|
}
|
|
|
|
/**
|
|
* binds multiple combinations to the same callback
|
|
*
|
|
* @param {Array} combinations
|
|
* @param {Function} callback
|
|
* @param {string|undefined} action
|
|
* @returns void
|
|
*/
|
|
function _bindMultiple(combinations, callback, action) {
|
|
for (var i = 0; i < combinations.length; ++i) {
|
|
_bindSingle(combinations[i], callback, action);
|
|
}
|
|
}
|
|
|
|
// start!
|
|
_addEvent(document, 'keypress', _handleKeyEvent);
|
|
_addEvent(document, 'keydown', _handleKeyEvent);
|
|
_addEvent(document, 'keyup', _handleKeyEvent);
|
|
|
|
var Mousetrap = {
|
|
|
|
/**
|
|
* binds an event to mousetrap
|
|
*
|
|
* can be a single key, a combination of keys separated with +,
|
|
* an array of keys, or a sequence of keys separated by spaces
|
|
*
|
|
* be sure to list the modifier keys first to make sure that the
|
|
* correct key ends up getting bound (the last key in the pattern)
|
|
*
|
|
* @param {string|Array} keys
|
|
* @param {Function} callback
|
|
* @param {string=} action - 'keypress', 'keydown', or 'keyup'
|
|
* @returns void
|
|
*/
|
|
bind: function(keys, callback, action) {
|
|
keys = keys instanceof Array ? keys : [keys];
|
|
_bindMultiple(keys, callback, action);
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* unbinds an event to mousetrap
|
|
*
|
|
* the unbinding sets the callback function of the specified key combo
|
|
* to an empty function and deletes the corresponding key in the
|
|
* _directMap dict.
|
|
*
|
|
* TODO: actually remove this from the _callbacks dictionary instead
|
|
* of binding an empty function
|
|
*
|
|
* the keycombo+action has to be exactly the same as
|
|
* it was defined in the bind method
|
|
*
|
|
* @param {string|Array} keys
|
|
* @param {string} action
|
|
* @returns void
|
|
*/
|
|
unbind: function(keys, action) {
|
|
return Mousetrap.bind(keys, function() {}, action);
|
|
},
|
|
|
|
/**
|
|
* triggers an event that has already been bound
|
|
*
|
|
* @param {string} keys
|
|
* @param {string=} action
|
|
* @returns void
|
|
*/
|
|
trigger: function(keys, action) {
|
|
if (_directMap[keys + ':' + action]) {
|
|
_directMap[keys + ':' + action]({}, keys);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* resets the library back to its initial state. this is useful
|
|
* if you want to clear out the current keyboard shortcuts and bind
|
|
* new ones - for example if you switch to another page
|
|
*
|
|
* @returns void
|
|
*/
|
|
reset: function() {
|
|
_callbacks = {};
|
|
_directMap = {};
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* should we stop this event before firing off callbacks
|
|
*
|
|
* @param {Event} e
|
|
* @param {Element} element
|
|
* @return {boolean}
|
|
*/
|
|
stopCallback: function(e, element) {
|
|
|
|
// if the element has the class "mousetrap" then no need to stop
|
|
if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
|
|
return false;
|
|
}
|
|
|
|
// stop for input, select, and textarea
|
|
return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
|
|
},
|
|
|
|
/**
|
|
* exposes _handleKey publicly so it can be overwritten by extensions
|
|
*/
|
|
handleKey: _handleKey
|
|
};
|
|
|
|
// expose mousetrap to the global object
|
|
window.Mousetrap = Mousetrap;
|
|
|
|
// expose mousetrap as an AMD module
|
|
if (typeof define === 'function' && define.amd) {
|
|
define('Mousetrap',Mousetrap);
|
|
}
|
|
}) (window, document);
|
|
|
|
define('core/navigation',[
|
|
"jQuery"
|
|
], function($) {
|
|
var goNext = function() {
|
|
var url = $("link[rel='next']").attr("href");
|
|
if (url) location.href = url;
|
|
};
|
|
var goPrev = function() {
|
|
var url = $("link[rel='prev']").attr("href");
|
|
if (url) location.href = url;
|
|
};
|
|
|
|
return {
|
|
goNext: goNext,
|
|
goPrev: goPrev
|
|
};
|
|
});
|
|
define('utils/platform',[], function() {
|
|
return {
|
|
isMobile: /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
|
|
};
|
|
});
|
|
define('core/sidebar',[
|
|
"lodash",
|
|
"utils/storage",
|
|
"utils/platform",
|
|
"core/state"
|
|
], function(_, storage, platform, state) {
|
|
var $summary = state.$book.find(".book-summary");
|
|
|
|
// Toggle sidebar with or withour animation
|
|
var toggleSidebar = function(_state, animation) {
|
|
if (state != null && isOpen() == _state) return;
|
|
if (animation == null) animation = true;
|
|
|
|
state.$book.toggleClass("without-animation", !animation);
|
|
state.$book.toggleClass("with-summary", _state);
|
|
|
|
storage.set("sidebar", isOpen());
|
|
};
|
|
|
|
// Return true if sidebar is open
|
|
var isOpen = function() {
|
|
return state.$book.hasClass("with-summary");
|
|
};
|
|
|
|
// Prepare sidebar: state and toggle button
|
|
var init = function() {
|
|
// Toggle summary
|
|
state.$book.find(".book-header .toggle-summary").click(function(e) {
|
|
e.preventDefault();
|
|
toggleSidebar();
|
|
});
|
|
|
|
// Init last state if not mobile
|
|
if (!platform.isMobile) {
|
|
toggleSidebar(storage.get("sidebar", true), false);
|
|
}
|
|
};
|
|
|
|
// Filter summary with a list of path
|
|
var filterSummary = function(paths) {
|
|
$summary.find("li").each(function() {
|
|
var path = $(this).data("path");
|
|
var st = paths == null || _.contains(paths, path);
|
|
|
|
$(this).toggle(st);
|
|
if (st) $(this).parents("li").show();
|
|
});
|
|
};
|
|
|
|
return {
|
|
$el: $summary,
|
|
init: init,
|
|
toggle: toggleSidebar,
|
|
filter: filterSummary
|
|
}
|
|
});
|
|
/**
|
|
* lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.2
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
* MIT Licensed
|
|
* @license
|
|
*/
|
|
|
|
(function(){
|
|
|
|
/**
|
|
* Convenience function for instantiating a new lunr index and configuring it
|
|
* with the default pipeline functions and the passed config function.
|
|
*
|
|
* When using this convenience function a new index will be created with the
|
|
* following functions already in the pipeline:
|
|
*
|
|
* lunr.StopWordFilter - filters out any stop words before they enter the
|
|
* index
|
|
*
|
|
* lunr.stemmer - stems the tokens before entering the index.
|
|
*
|
|
* Example:
|
|
*
|
|
* var idx = lunr(function () {
|
|
* this.field('title', 10)
|
|
* this.field('tags', 100)
|
|
* this.field('body')
|
|
*
|
|
* this.ref('cid')
|
|
*
|
|
* this.pipeline.add(function () {
|
|
* // some custom pipeline function
|
|
* })
|
|
*
|
|
* })
|
|
*
|
|
* @param {Function} config A function that will be called with the new instance
|
|
* of the lunr.Index as both its context and first parameter. It can be used to
|
|
* customize the instance of new lunr.Index.
|
|
* @namespace
|
|
* @module
|
|
* @returns {lunr.Index}
|
|
*
|
|
*/
|
|
var lunr = function (config) {
|
|
var idx = new lunr.Index
|
|
|
|
idx.pipeline.add(
|
|
lunr.trimmer,
|
|
lunr.stopWordFilter,
|
|
lunr.stemmer
|
|
)
|
|
|
|
if (config) config.call(idx, idx)
|
|
|
|
return idx
|
|
}
|
|
|
|
lunr.version = "0.5.2"
|
|
/*!
|
|
* lunr.utils
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* A namespace containing utils for the rest of the lunr library
|
|
*/
|
|
lunr.utils = {}
|
|
|
|
/**
|
|
* Print a warning message to the console.
|
|
*
|
|
* @param {String} message The message to be printed.
|
|
* @memberOf Utils
|
|
*/
|
|
lunr.utils.warn = (function (global) {
|
|
return function (message) {
|
|
if (global.console && console.warn) {
|
|
console.warn(message)
|
|
}
|
|
}
|
|
})(this)
|
|
|
|
/*!
|
|
* lunr.EventEmitter
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.EventEmitter is an event emitter for lunr. It manages adding and removing event handlers and triggering events and their handlers.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.EventEmitter = function () {
|
|
this.events = {}
|
|
}
|
|
|
|
/**
|
|
* Binds a handler function to a specific event(s).
|
|
*
|
|
* Can bind a single function to many different events in one call.
|
|
*
|
|
* @param {String} [eventName] The name(s) of events to bind this function to.
|
|
* @param {Function} handler The function to call when an event is fired.
|
|
* @memberOf EventEmitter
|
|
*/
|
|
lunr.EventEmitter.prototype.addListener = function () {
|
|
var args = Array.prototype.slice.call(arguments),
|
|
fn = args.pop(),
|
|
names = args
|
|
|
|
if (typeof fn !== "function") throw new TypeError ("last argument must be a function")
|
|
|
|
names.forEach(function (name) {
|
|
if (!this.hasHandler(name)) this.events[name] = []
|
|
this.events[name].push(fn)
|
|
}, this)
|
|
}
|
|
|
|
/**
|
|
* Removes a handler function from a specific event.
|
|
*
|
|
* @param {String} eventName The name of the event to remove this function from.
|
|
* @param {Function} handler The function to remove from an event.
|
|
* @memberOf EventEmitter
|
|
*/
|
|
lunr.EventEmitter.prototype.removeListener = function (name, fn) {
|
|
if (!this.hasHandler(name)) return
|
|
|
|
var fnIndex = this.events[name].indexOf(fn)
|
|
this.events[name].splice(fnIndex, 1)
|
|
|
|
if (!this.events[name].length) delete this.events[name]
|
|
}
|
|
|
|
/**
|
|
* Calls all functions bound to the given event.
|
|
*
|
|
* Additional data can be passed to the event handler as arguments to `emit`
|
|
* after the event name.
|
|
*
|
|
* @param {String} eventName The name of the event to emit.
|
|
* @memberOf EventEmitter
|
|
*/
|
|
lunr.EventEmitter.prototype.emit = function (name) {
|
|
if (!this.hasHandler(name)) return
|
|
|
|
var args = Array.prototype.slice.call(arguments, 1)
|
|
|
|
this.events[name].forEach(function (fn) {
|
|
fn.apply(undefined, args)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Checks whether a handler has ever been stored against an event.
|
|
*
|
|
* @param {String} eventName The name of the event to check.
|
|
* @private
|
|
* @memberOf EventEmitter
|
|
*/
|
|
lunr.EventEmitter.prototype.hasHandler = function (name) {
|
|
return name in this.events
|
|
}
|
|
|
|
/*!
|
|
* lunr.tokenizer
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* A function for splitting a string into tokens ready to be inserted into
|
|
* the search index.
|
|
*
|
|
* @module
|
|
* @param {String} obj The string to convert into tokens
|
|
* @returns {Array}
|
|
*/
|
|
lunr.tokenizer = function (obj) {
|
|
if (!arguments.length || obj == null || obj == undefined) return []
|
|
if (Array.isArray(obj)) return obj.map(function (t) { return t.toLowerCase() })
|
|
|
|
var str = obj.toString().replace(/^\s+/, '')
|
|
|
|
for (var i = str.length - 1; i >= 0; i--) {
|
|
if (/\S/.test(str.charAt(i))) {
|
|
str = str.substring(0, i + 1)
|
|
break
|
|
}
|
|
}
|
|
|
|
return str
|
|
.split(/\s+/)
|
|
.map(function (token) {
|
|
return token.toLowerCase()
|
|
})
|
|
}
|
|
/*!
|
|
* lunr.Pipeline
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.Pipelines maintain an ordered list of functions to be applied to all
|
|
* tokens in documents entering the search index and queries being ran against
|
|
* the index.
|
|
*
|
|
* An instance of lunr.Index created with the lunr shortcut will contain a
|
|
* pipeline with a stop word filter and an English language stemmer. Extra
|
|
* functions can be added before or after either of these functions or these
|
|
* default functions can be removed.
|
|
*
|
|
* When run the pipeline will call each function in turn, passing a token, the
|
|
* index of that token in the original list of all tokens and finally a list of
|
|
* all the original tokens.
|
|
*
|
|
* The output of functions in the pipeline will be passed to the next function
|
|
* in the pipeline. To exclude a token from entering the index the function
|
|
* should return undefined, the rest of the pipeline will not be called with
|
|
* this token.
|
|
*
|
|
* For serialisation of pipelines to work, all functions used in an instance of
|
|
* a pipeline should be registered with lunr.Pipeline. Registered functions can
|
|
* then be loaded. If trying to load a serialised pipeline that uses functions
|
|
* that are not registered an error will be thrown.
|
|
*
|
|
* If not planning on serialising the pipeline then registering pipeline functions
|
|
* is not necessary.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.Pipeline = function () {
|
|
this._stack = []
|
|
}
|
|
|
|
lunr.Pipeline.registeredFunctions = {}
|
|
|
|
/**
|
|
* Register a function with the pipeline.
|
|
*
|
|
* Functions that are used in the pipeline should be registered if the pipeline
|
|
* needs to be serialised, or a serialised pipeline needs to be loaded.
|
|
*
|
|
* Registering a function does not add it to a pipeline, functions must still be
|
|
* added to instances of the pipeline for them to be used when running a pipeline.
|
|
*
|
|
* @param {Function} fn The function to check for.
|
|
* @param {String} label The label to register this function with
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.registerFunction = function (fn, label) {
|
|
if (label in this.registeredFunctions) {
|
|
lunr.utils.warn('Overwriting existing registered function: ' + label)
|
|
}
|
|
|
|
fn.label = label
|
|
lunr.Pipeline.registeredFunctions[fn.label] = fn
|
|
}
|
|
|
|
/**
|
|
* Warns if the function is not registered as a Pipeline function.
|
|
*
|
|
* @param {Function} fn The function to check for.
|
|
* @private
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {
|
|
var isRegistered = fn.label && (fn.label in this.registeredFunctions)
|
|
|
|
if (!isRegistered) {
|
|
lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\n', fn)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads a previously serialised pipeline.
|
|
*
|
|
* All functions to be loaded must already be registered with lunr.Pipeline.
|
|
* If any function from the serialised data has not been registered then an
|
|
* error will be thrown.
|
|
*
|
|
* @param {Object} serialised The serialised pipeline to load.
|
|
* @returns {lunr.Pipeline}
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.load = function (serialised) {
|
|
var pipeline = new lunr.Pipeline
|
|
|
|
serialised.forEach(function (fnName) {
|
|
var fn = lunr.Pipeline.registeredFunctions[fnName]
|
|
|
|
if (fn) {
|
|
pipeline.add(fn)
|
|
} else {
|
|
throw new Error ('Cannot load un-registered function: ' + fnName)
|
|
}
|
|
})
|
|
|
|
return pipeline
|
|
}
|
|
|
|
/**
|
|
* Adds new functions to the end of the pipeline.
|
|
*
|
|
* Logs a warning if the function has not been registered.
|
|
*
|
|
* @param {Function} functions Any number of functions to add to the pipeline.
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.add = function () {
|
|
var fns = Array.prototype.slice.call(arguments)
|
|
|
|
fns.forEach(function (fn) {
|
|
lunr.Pipeline.warnIfFunctionNotRegistered(fn)
|
|
this._stack.push(fn)
|
|
}, this)
|
|
}
|
|
|
|
/**
|
|
* Adds a single function after a function that already exists in the
|
|
* pipeline.
|
|
*
|
|
* Logs a warning if the function has not been registered.
|
|
*
|
|
* @param {Function} existingFn A function that already exists in the pipeline.
|
|
* @param {Function} newFn The new function to add to the pipeline.
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.after = function (existingFn, newFn) {
|
|
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
|
|
|
|
var pos = this._stack.indexOf(existingFn) + 1
|
|
this._stack.splice(pos, 0, newFn)
|
|
}
|
|
|
|
/**
|
|
* Adds a single function before a function that already exists in the
|
|
* pipeline.
|
|
*
|
|
* Logs a warning if the function has not been registered.
|
|
*
|
|
* @param {Function} existingFn A function that already exists in the pipeline.
|
|
* @param {Function} newFn The new function to add to the pipeline.
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.before = function (existingFn, newFn) {
|
|
lunr.Pipeline.warnIfFunctionNotRegistered(newFn)
|
|
|
|
var pos = this._stack.indexOf(existingFn)
|
|
this._stack.splice(pos, 0, newFn)
|
|
}
|
|
|
|
/**
|
|
* Removes a function from the pipeline.
|
|
*
|
|
* @param {Function} fn The function to remove from the pipeline.
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.remove = function (fn) {
|
|
var pos = this._stack.indexOf(fn)
|
|
this._stack.splice(pos, 1)
|
|
}
|
|
|
|
/**
|
|
* Runs the current list of functions that make up the pipeline against the
|
|
* passed tokens.
|
|
*
|
|
* @param {Array} tokens The tokens to run through the pipeline.
|
|
* @returns {Array}
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.run = function (tokens) {
|
|
var out = [],
|
|
tokenLength = tokens.length,
|
|
stackLength = this._stack.length
|
|
|
|
for (var i = 0; i < tokenLength; i++) {
|
|
var token = tokens[i]
|
|
|
|
for (var j = 0; j < stackLength; j++) {
|
|
token = this._stack[j](token, i, tokens)
|
|
if (token === void 0) break
|
|
};
|
|
|
|
if (token !== void 0) out.push(token)
|
|
};
|
|
|
|
return out
|
|
}
|
|
|
|
/**
|
|
* Resets the pipeline by removing any existing processors.
|
|
*
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.reset = function () {
|
|
this._stack = []
|
|
}
|
|
|
|
/**
|
|
* Returns a representation of the pipeline ready for serialisation.
|
|
*
|
|
* Logs a warning if the function has not been registered.
|
|
*
|
|
* @returns {Array}
|
|
* @memberOf Pipeline
|
|
*/
|
|
lunr.Pipeline.prototype.toJSON = function () {
|
|
return this._stack.map(function (fn) {
|
|
lunr.Pipeline.warnIfFunctionNotRegistered(fn)
|
|
|
|
return fn.label
|
|
})
|
|
}
|
|
/*!
|
|
* lunr.Vector
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.Vectors implement vector related operations for
|
|
* a series of elements.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.Vector = function () {
|
|
this._magnitude = null
|
|
this.list = undefined
|
|
this.length = 0
|
|
}
|
|
|
|
/**
|
|
* lunr.Vector.Node is a simple struct for each node
|
|
* in a lunr.Vector.
|
|
*
|
|
* @private
|
|
* @param {Number} The index of the node in the vector.
|
|
* @param {Object} The data at this node in the vector.
|
|
* @param {lunr.Vector.Node} The node directly after this node in the vector.
|
|
* @constructor
|
|
* @memberOf Vector
|
|
*/
|
|
lunr.Vector.Node = function (idx, val, next) {
|
|
this.idx = idx
|
|
this.val = val
|
|
this.next = next
|
|
}
|
|
|
|
/**
|
|
* Inserts a new value at a position in a vector.
|
|
*
|
|
* @param {Number} The index at which to insert a value.
|
|
* @param {Object} The object to insert in the vector.
|
|
* @memberOf Vector.
|
|
*/
|
|
lunr.Vector.prototype.insert = function (idx, val) {
|
|
var list = this.list
|
|
|
|
if (!list) {
|
|
this.list = new lunr.Vector.Node (idx, val, list)
|
|
return this.length++
|
|
}
|
|
|
|
var prev = list,
|
|
next = list.next
|
|
|
|
while (next != undefined) {
|
|
if (idx < next.idx) {
|
|
prev.next = new lunr.Vector.Node (idx, val, next)
|
|
return this.length++
|
|
}
|
|
|
|
prev = next, next = next.next
|
|
}
|
|
|
|
prev.next = new lunr.Vector.Node (idx, val, next)
|
|
return this.length++
|
|
}
|
|
|
|
/**
|
|
* Calculates the magnitude of this vector.
|
|
*
|
|
* @returns {Number}
|
|
* @memberOf Vector
|
|
*/
|
|
lunr.Vector.prototype.magnitude = function () {
|
|
if (this._magniture) return this._magnitude
|
|
var node = this.list,
|
|
sumOfSquares = 0,
|
|
val
|
|
|
|
while (node) {
|
|
val = node.val
|
|
sumOfSquares += val * val
|
|
node = node.next
|
|
}
|
|
|
|
return this._magnitude = Math.sqrt(sumOfSquares)
|
|
}
|
|
|
|
/**
|
|
* Calculates the dot product of this vector and another vector.
|
|
*
|
|
* @param {lunr.Vector} otherVector The vector to compute the dot product with.
|
|
* @returns {Number}
|
|
* @memberOf Vector
|
|
*/
|
|
lunr.Vector.prototype.dot = function (otherVector) {
|
|
var node = this.list,
|
|
otherNode = otherVector.list,
|
|
dotProduct = 0
|
|
|
|
while (node && otherNode) {
|
|
if (node.idx < otherNode.idx) {
|
|
node = node.next
|
|
} else if (node.idx > otherNode.idx) {
|
|
otherNode = otherNode.next
|
|
} else {
|
|
dotProduct += node.val * otherNode.val
|
|
node = node.next
|
|
otherNode = otherNode.next
|
|
}
|
|
}
|
|
|
|
return dotProduct
|
|
}
|
|
|
|
/**
|
|
* Calculates the cosine similarity between this vector and another
|
|
* vector.
|
|
*
|
|
* @param {lunr.Vector} otherVector The other vector to calculate the
|
|
* similarity with.
|
|
* @returns {Number}
|
|
* @memberOf Vector
|
|
*/
|
|
lunr.Vector.prototype.similarity = function (otherVector) {
|
|
return this.dot(otherVector) / (this.magnitude() * otherVector.magnitude())
|
|
}
|
|
/*!
|
|
* lunr.SortedSet
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.SortedSets are used to maintain an array of uniq values in a sorted
|
|
* order.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.SortedSet = function () {
|
|
this.length = 0
|
|
this.elements = []
|
|
}
|
|
|
|
/**
|
|
* Loads a previously serialised sorted set.
|
|
*
|
|
* @param {Array} serialisedData The serialised set to load.
|
|
* @returns {lunr.SortedSet}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.load = function (serialisedData) {
|
|
var set = new this
|
|
|
|
set.elements = serialisedData
|
|
set.length = serialisedData.length
|
|
|
|
return set
|
|
}
|
|
|
|
/**
|
|
* Inserts new items into the set in the correct position to maintain the
|
|
* order.
|
|
*
|
|
* @param {Object} The objects to add to this set.
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.add = function () {
|
|
Array.prototype.slice.call(arguments).forEach(function (element) {
|
|
if (~this.indexOf(element)) return
|
|
this.elements.splice(this.locationFor(element), 0, element)
|
|
}, this)
|
|
|
|
this.length = this.elements.length
|
|
}
|
|
|
|
/**
|
|
* Converts this sorted set into an array.
|
|
*
|
|
* @returns {Array}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.toArray = function () {
|
|
return this.elements.slice()
|
|
}
|
|
|
|
/**
|
|
* Creates a new array with the results of calling a provided function on every
|
|
* element in this sorted set.
|
|
*
|
|
* Delegates to Array.prototype.map and has the same signature.
|
|
*
|
|
* @param {Function} fn The function that is called on each element of the
|
|
* set.
|
|
* @param {Object} ctx An optional object that can be used as the context
|
|
* for the function fn.
|
|
* @returns {Array}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.map = function (fn, ctx) {
|
|
return this.elements.map(fn, ctx)
|
|
}
|
|
|
|
/**
|
|
* Executes a provided function once per sorted set element.
|
|
*
|
|
* Delegates to Array.prototype.forEach and has the same signature.
|
|
*
|
|
* @param {Function} fn The function that is called on each element of the
|
|
* set.
|
|
* @param {Object} ctx An optional object that can be used as the context
|
|
* @memberOf SortedSet
|
|
* for the function fn.
|
|
*/
|
|
lunr.SortedSet.prototype.forEach = function (fn, ctx) {
|
|
return this.elements.forEach(fn, ctx)
|
|
}
|
|
|
|
/**
|
|
* Returns the index at which a given element can be found in the
|
|
* sorted set, or -1 if it is not present.
|
|
*
|
|
* @param {Object} elem The object to locate in the sorted set.
|
|
* @param {Number} start An optional index at which to start searching from
|
|
* within the set.
|
|
* @param {Number} end An optional index at which to stop search from within
|
|
* the set.
|
|
* @returns {Number}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.indexOf = function (elem, start, end) {
|
|
var start = start || 0,
|
|
end = end || this.elements.length,
|
|
sectionLength = end - start,
|
|
pivot = start + Math.floor(sectionLength / 2),
|
|
pivotElem = this.elements[pivot]
|
|
|
|
if (sectionLength <= 1) {
|
|
if (pivotElem === elem) {
|
|
return pivot
|
|
} else {
|
|
return -1
|
|
}
|
|
}
|
|
|
|
if (pivotElem < elem) return this.indexOf(elem, pivot, end)
|
|
if (pivotElem > elem) return this.indexOf(elem, start, pivot)
|
|
if (pivotElem === elem) return pivot
|
|
}
|
|
|
|
/**
|
|
* Returns the position within the sorted set that an element should be
|
|
* inserted at to maintain the current order of the set.
|
|
*
|
|
* This function assumes that the element to search for does not already exist
|
|
* in the sorted set.
|
|
*
|
|
* @param {Object} elem The elem to find the position for in the set
|
|
* @param {Number} start An optional index at which to start searching from
|
|
* within the set.
|
|
* @param {Number} end An optional index at which to stop search from within
|
|
* the set.
|
|
* @returns {Number}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.locationFor = function (elem, start, end) {
|
|
var start = start || 0,
|
|
end = end || this.elements.length,
|
|
sectionLength = end - start,
|
|
pivot = start + Math.floor(sectionLength / 2),
|
|
pivotElem = this.elements[pivot]
|
|
|
|
if (sectionLength <= 1) {
|
|
if (pivotElem > elem) return pivot
|
|
if (pivotElem < elem) return pivot + 1
|
|
}
|
|
|
|
if (pivotElem < elem) return this.locationFor(elem, pivot, end)
|
|
if (pivotElem > elem) return this.locationFor(elem, start, pivot)
|
|
}
|
|
|
|
/**
|
|
* Creates a new lunr.SortedSet that contains the elements in the intersection
|
|
* of this set and the passed set.
|
|
*
|
|
* @param {lunr.SortedSet} otherSet The set to intersect with this set.
|
|
* @returns {lunr.SortedSet}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.intersect = function (otherSet) {
|
|
var intersectSet = new lunr.SortedSet,
|
|
i = 0, j = 0,
|
|
a_len = this.length, b_len = otherSet.length,
|
|
a = this.elements, b = otherSet.elements
|
|
|
|
while (true) {
|
|
if (i > a_len - 1 || j > b_len - 1) break
|
|
|
|
if (a[i] === b[j]) {
|
|
intersectSet.add(a[i])
|
|
i++, j++
|
|
continue
|
|
}
|
|
|
|
if (a[i] < b[j]) {
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if (a[i] > b[j]) {
|
|
j++
|
|
continue
|
|
}
|
|
};
|
|
|
|
return intersectSet
|
|
}
|
|
|
|
/**
|
|
* Makes a copy of this set
|
|
*
|
|
* @returns {lunr.SortedSet}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.clone = function () {
|
|
var clone = new lunr.SortedSet
|
|
|
|
clone.elements = this.toArray()
|
|
clone.length = clone.elements.length
|
|
|
|
return clone
|
|
}
|
|
|
|
/**
|
|
* Creates a new lunr.SortedSet that contains the elements in the union
|
|
* of this set and the passed set.
|
|
*
|
|
* @param {lunr.SortedSet} otherSet The set to union with this set.
|
|
* @returns {lunr.SortedSet}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.union = function (otherSet) {
|
|
var longSet, shortSet, unionSet
|
|
|
|
if (this.length >= otherSet.length) {
|
|
longSet = this, shortSet = otherSet
|
|
} else {
|
|
longSet = otherSet, shortSet = this
|
|
}
|
|
|
|
unionSet = longSet.clone()
|
|
|
|
unionSet.add.apply(unionSet, shortSet.toArray())
|
|
|
|
return unionSet
|
|
}
|
|
|
|
/**
|
|
* Returns a representation of the sorted set ready for serialisation.
|
|
*
|
|
* @returns {Array}
|
|
* @memberOf SortedSet
|
|
*/
|
|
lunr.SortedSet.prototype.toJSON = function () {
|
|
return this.toArray()
|
|
}
|
|
/*!
|
|
* lunr.Index
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.Index is object that manages a search index. It contains the indexes
|
|
* and stores all the tokens and document lookups. It also provides the main
|
|
* user facing API for the library.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.Index = function () {
|
|
this._fields = []
|
|
this._ref = 'id'
|
|
this.pipeline = new lunr.Pipeline
|
|
this.documentStore = new lunr.Store
|
|
this.tokenStore = new lunr.TokenStore
|
|
this.corpusTokens = new lunr.SortedSet
|
|
this.eventEmitter = new lunr.EventEmitter
|
|
|
|
this._idfCache = {}
|
|
|
|
this.on('add', 'remove', 'update', (function () {
|
|
this._idfCache = {}
|
|
}).bind(this))
|
|
}
|
|
|
|
/**
|
|
* Bind a handler to events being emitted by the index.
|
|
*
|
|
* The handler can be bound to many events at the same time.
|
|
*
|
|
* @param {String} [eventName] The name(s) of events to bind the function to.
|
|
* @param {Function} handler The serialised set to load.
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.on = function () {
|
|
var args = Array.prototype.slice.call(arguments)
|
|
return this.eventEmitter.addListener.apply(this.eventEmitter, args)
|
|
}
|
|
|
|
/**
|
|
* Removes a handler from an event being emitted by the index.
|
|
*
|
|
* @param {String} eventName The name of events to remove the function from.
|
|
* @param {Function} handler The serialised set to load.
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.off = function (name, fn) {
|
|
return this.eventEmitter.removeListener(name, fn)
|
|
}
|
|
|
|
/**
|
|
* Loads a previously serialised index.
|
|
*
|
|
* Issues a warning if the index being imported was serialised
|
|
* by a different version of lunr.
|
|
*
|
|
* @param {Object} serialisedData The serialised set to load.
|
|
* @returns {lunr.Index}
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.load = function (serialisedData) {
|
|
if (serialisedData.version !== lunr.version) {
|
|
lunr.utils.warn('version mismatch: current ' + lunr.version + ' importing ' + serialisedData.version)
|
|
}
|
|
|
|
var idx = new this
|
|
|
|
idx._fields = serialisedData.fields
|
|
idx._ref = serialisedData.ref
|
|
|
|
idx.documentStore = lunr.Store.load(serialisedData.documentStore)
|
|
idx.tokenStore = lunr.TokenStore.load(serialisedData.tokenStore)
|
|
idx.corpusTokens = lunr.SortedSet.load(serialisedData.corpusTokens)
|
|
idx.pipeline = lunr.Pipeline.load(serialisedData.pipeline)
|
|
|
|
return idx
|
|
}
|
|
|
|
/**
|
|
* Adds a field to the list of fields that will be searchable within documents
|
|
* in the index.
|
|
*
|
|
* An optional boost param can be passed to affect how much tokens in this field
|
|
* rank in search results, by default the boost value is 1.
|
|
*
|
|
* Fields should be added before any documents are added to the index, fields
|
|
* that are added after documents are added to the index will only apply to new
|
|
* documents added to the index.
|
|
*
|
|
* @param {String} fieldName The name of the field within the document that
|
|
* should be indexed
|
|
* @param {Number} boost An optional boost that can be applied to terms in this
|
|
* field.
|
|
* @returns {lunr.Index}
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.field = function (fieldName, opts) {
|
|
var opts = opts || {},
|
|
field = { name: fieldName, boost: opts.boost || 1 }
|
|
|
|
this._fields.push(field)
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Sets the property used to uniquely identify documents added to the index,
|
|
* by default this property is 'id'.
|
|
*
|
|
* This should only be changed before adding documents to the index, changing
|
|
* the ref property without resetting the index can lead to unexpected results.
|
|
*
|
|
* @param {String} refName The property to use to uniquely identify the
|
|
* documents in the index.
|
|
* @param {Boolean} emitEvent Whether to emit add events, defaults to true
|
|
* @returns {lunr.Index}
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.ref = function (refName) {
|
|
this._ref = refName
|
|
return this
|
|
}
|
|
|
|
/**
|
|
* Add a document to the index.
|
|
*
|
|
* This is the way new documents enter the index, this function will run the
|
|
* fields from the document through the index's pipeline and then add it to
|
|
* the index, it will then show up in search results.
|
|
*
|
|
* An 'add' event is emitted with the document that has been added and the index
|
|
* the document has been added to. This event can be silenced by passing false
|
|
* as the second argument to add.
|
|
*
|
|
* @param {Object} doc The document to add to the index.
|
|
* @param {Boolean} emitEvent Whether or not to emit events, default true.
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.add = function (doc, emitEvent) {
|
|
var docTokens = {},
|
|
allDocumentTokens = new lunr.SortedSet,
|
|
docRef = doc[this._ref],
|
|
emitEvent = emitEvent === undefined ? true : emitEvent
|
|
|
|
this._fields.forEach(function (field) {
|
|
var fieldTokens = this.pipeline.run(lunr.tokenizer(doc[field.name]))
|
|
|
|
docTokens[field.name] = fieldTokens
|
|
lunr.SortedSet.prototype.add.apply(allDocumentTokens, fieldTokens)
|
|
}, this)
|
|
|
|
this.documentStore.set(docRef, allDocumentTokens)
|
|
lunr.SortedSet.prototype.add.apply(this.corpusTokens, allDocumentTokens.toArray())
|
|
|
|
for (var i = 0; i < allDocumentTokens.length; i++) {
|
|
var token = allDocumentTokens.elements[i]
|
|
var tf = this._fields.reduce(function (memo, field) {
|
|
var fieldLength = docTokens[field.name].length
|
|
|
|
if (!fieldLength) return memo
|
|
|
|
var tokenCount = docTokens[field.name].filter(function (t) { return t === token }).length
|
|
|
|
return memo + (tokenCount / fieldLength * field.boost)
|
|
}, 0)
|
|
|
|
this.tokenStore.add(token, { ref: docRef, tf: tf })
|
|
};
|
|
|
|
if (emitEvent) this.eventEmitter.emit('add', doc, this)
|
|
}
|
|
|
|
/**
|
|
* Removes a document from the index.
|
|
*
|
|
* To make sure documents no longer show up in search results they can be
|
|
* removed from the index using this method.
|
|
*
|
|
* The document passed only needs to have the same ref property value as the
|
|
* document that was added to the index, they could be completely different
|
|
* objects.
|
|
*
|
|
* A 'remove' event is emitted with the document that has been removed and the index
|
|
* the document has been removed from. This event can be silenced by passing false
|
|
* as the second argument to remove.
|
|
*
|
|
* @param {Object} doc The document to remove from the index.
|
|
* @param {Boolean} emitEvent Whether to emit remove events, defaults to true
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.remove = function (doc, emitEvent) {
|
|
var docRef = doc[this._ref],
|
|
emitEvent = emitEvent === undefined ? true : emitEvent
|
|
|
|
if (!this.documentStore.has(docRef)) return
|
|
|
|
var docTokens = this.documentStore.get(docRef)
|
|
|
|
this.documentStore.remove(docRef)
|
|
|
|
docTokens.forEach(function (token) {
|
|
this.tokenStore.remove(token, docRef)
|
|
}, this)
|
|
|
|
if (emitEvent) this.eventEmitter.emit('remove', doc, this)
|
|
}
|
|
|
|
/**
|
|
* Updates a document in the index.
|
|
*
|
|
* When a document contained within the index gets updated, fields changed,
|
|
* added or removed, to make sure it correctly matched against search queries,
|
|
* it should be updated in the index.
|
|
*
|
|
* This method is just a wrapper around `remove` and `add`
|
|
*
|
|
* An 'update' event is emitted with the document that has been updated and the index.
|
|
* This event can be silenced by passing false as the second argument to update. Only
|
|
* an update event will be fired, the 'add' and 'remove' events of the underlying calls
|
|
* are silenced.
|
|
*
|
|
* @param {Object} doc The document to update in the index.
|
|
* @param {Boolean} emitEvent Whether to emit update events, defaults to true
|
|
* @see Index.prototype.remove
|
|
* @see Index.prototype.add
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.update = function (doc, emitEvent) {
|
|
var emitEvent = emitEvent === undefined ? true : emitEvent
|
|
|
|
this.remove(doc, false)
|
|
this.add(doc, false)
|
|
|
|
if (emitEvent) this.eventEmitter.emit('update', doc, this)
|
|
}
|
|
|
|
/**
|
|
* Calculates the inverse document frequency for a token within the index.
|
|
*
|
|
* @param {String} token The token to calculate the idf of.
|
|
* @see Index.prototype.idf
|
|
* @private
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.idf = function (term) {
|
|
var cacheKey = "@" + term
|
|
if (Object.prototype.hasOwnProperty.call(this._idfCache, cacheKey)) return this._idfCache[cacheKey]
|
|
|
|
var documentFrequency = this.tokenStore.count(term),
|
|
idf = 1
|
|
|
|
if (documentFrequency > 0) {
|
|
idf = 1 + Math.log(this.tokenStore.length / documentFrequency)
|
|
}
|
|
|
|
return this._idfCache[cacheKey] = idf
|
|
}
|
|
|
|
/**
|
|
* Searches the index using the passed query.
|
|
*
|
|
* Queries should be a string, multiple words are allowed and will lead to an
|
|
* AND based query, e.g. `idx.search('foo bar')` will run a search for
|
|
* documents containing both 'foo' and 'bar'.
|
|
*
|
|
* All query tokens are passed through the same pipeline that document tokens
|
|
* are passed through, so any language processing involved will be run on every
|
|
* query term.
|
|
*
|
|
* Each query term is expanded, so that the term 'he' might be expanded to
|
|
* 'hello' and 'help' if those terms were already included in the index.
|
|
*
|
|
* Matching documents are returned as an array of objects, each object contains
|
|
* the matching document ref, as set for this index, and the similarity score
|
|
* for this document against the query.
|
|
*
|
|
* @param {String} query The query to search the index with.
|
|
* @returns {Object}
|
|
* @see Index.prototype.idf
|
|
* @see Index.prototype.documentVector
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.search = function (query) {
|
|
var queryTokens = this.pipeline.run(lunr.tokenizer(query)),
|
|
queryVector = new lunr.Vector,
|
|
documentSets = [],
|
|
fieldBoosts = this._fields.reduce(function (memo, f) { return memo + f.boost }, 0)
|
|
|
|
var hasSomeToken = queryTokens.some(function (token) {
|
|
return this.tokenStore.has(token)
|
|
}, this)
|
|
|
|
if (!hasSomeToken) return []
|
|
|
|
queryTokens
|
|
.forEach(function (token, i, tokens) {
|
|
var tf = 1 / tokens.length * this._fields.length * fieldBoosts,
|
|
self = this
|
|
|
|
var set = this.tokenStore.expand(token).reduce(function (memo, key) {
|
|
var pos = self.corpusTokens.indexOf(key),
|
|
idf = self.idf(key),
|
|
similarityBoost = 1,
|
|
set = new lunr.SortedSet
|
|
|
|
// if the expanded key is not an exact match to the token then
|
|
// penalise the score for this key by how different the key is
|
|
// to the token.
|
|
if (key !== token) {
|
|
var diff = Math.max(3, key.length - token.length)
|
|
similarityBoost = 1 / Math.log(diff)
|
|
}
|
|
|
|
// calculate the query tf-idf score for this token
|
|
// applying an similarityBoost to ensure exact matches
|
|
// these rank higher than expanded terms
|
|
if (pos > -1) queryVector.insert(pos, tf * idf * similarityBoost)
|
|
|
|
// add all the documents that have this key into a set
|
|
Object.keys(self.tokenStore.get(key)).forEach(function (ref) { set.add(ref) })
|
|
|
|
return memo.union(set)
|
|
}, new lunr.SortedSet)
|
|
|
|
documentSets.push(set)
|
|
}, this)
|
|
|
|
var documentSet = documentSets.reduce(function (memo, set) {
|
|
return memo.intersect(set)
|
|
})
|
|
|
|
return documentSet
|
|
.map(function (ref) {
|
|
return { ref: ref, score: queryVector.similarity(this.documentVector(ref)) }
|
|
}, this)
|
|
.sort(function (a, b) {
|
|
return b.score - a.score
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Generates a vector containing all the tokens in the document matching the
|
|
* passed documentRef.
|
|
*
|
|
* The vector contains the tf-idf score for each token contained in the
|
|
* document with the passed documentRef. The vector will contain an element
|
|
* for every token in the indexes corpus, if the document does not contain that
|
|
* token the element will be 0.
|
|
*
|
|
* @param {Object} documentRef The ref to find the document with.
|
|
* @returns {lunr.Vector}
|
|
* @private
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.documentVector = function (documentRef) {
|
|
var documentTokens = this.documentStore.get(documentRef),
|
|
documentTokensLength = documentTokens.length,
|
|
documentVector = new lunr.Vector
|
|
|
|
for (var i = 0; i < documentTokensLength; i++) {
|
|
var token = documentTokens.elements[i],
|
|
tf = this.tokenStore.get(token)[documentRef].tf,
|
|
idf = this.idf(token)
|
|
|
|
documentVector.insert(this.corpusTokens.indexOf(token), tf * idf)
|
|
};
|
|
|
|
return documentVector
|
|
}
|
|
|
|
/**
|
|
* Returns a representation of the index ready for serialisation.
|
|
*
|
|
* @returns {Object}
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.toJSON = function () {
|
|
return {
|
|
version: lunr.version,
|
|
fields: this._fields,
|
|
ref: this._ref,
|
|
documentStore: this.documentStore.toJSON(),
|
|
tokenStore: this.tokenStore.toJSON(),
|
|
corpusTokens: this.corpusTokens.toJSON(),
|
|
pipeline: this.pipeline.toJSON()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies a plugin to the current index.
|
|
*
|
|
* A plugin is a function that is called with the index as its context.
|
|
* Plugins can be used to customise or extend the behaviour the index
|
|
* in some way. A plugin is just a function, that encapsulated the custom
|
|
* behaviour that should be applied to the index.
|
|
*
|
|
* The plugin function will be called with the index as its argument, additional
|
|
* arguments can also be passed when calling use. The function will be called
|
|
* with the index as its context.
|
|
*
|
|
* Example:
|
|
*
|
|
* var myPlugin = function (idx, arg1, arg2) {
|
|
* // `this` is the index to be extended
|
|
* // apply any extensions etc here.
|
|
* }
|
|
*
|
|
* var idx = lunr(function () {
|
|
* this.use(myPlugin, 'arg1', 'arg2')
|
|
* })
|
|
*
|
|
* @param {Function} plugin The plugin to apply.
|
|
* @memberOf Index
|
|
*/
|
|
lunr.Index.prototype.use = function (plugin) {
|
|
var args = Array.prototype.slice.call(arguments, 1)
|
|
args.unshift(this)
|
|
plugin.apply(this, args)
|
|
}
|
|
/*!
|
|
* lunr.Store
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.Store is a simple key-value store used for storing sets of tokens for
|
|
* documents stored in index.
|
|
*
|
|
* @constructor
|
|
* @module
|
|
*/
|
|
lunr.Store = function () {
|
|
this.store = {}
|
|
this.length = 0
|
|
}
|
|
|
|
/**
|
|
* Loads a previously serialised store
|
|
*
|
|
* @param {Object} serialisedData The serialised store to load.
|
|
* @returns {lunr.Store}
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.load = function (serialisedData) {
|
|
var store = new this
|
|
|
|
store.length = serialisedData.length
|
|
store.store = Object.keys(serialisedData.store).reduce(function (memo, key) {
|
|
memo[key] = lunr.SortedSet.load(serialisedData.store[key])
|
|
return memo
|
|
}, {})
|
|
|
|
return store
|
|
}
|
|
|
|
/**
|
|
* Stores the given tokens in the store against the given id.
|
|
*
|
|
* @param {Object} id The key used to store the tokens against.
|
|
* @param {Object} tokens The tokens to store against the key.
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.prototype.set = function (id, tokens) {
|
|
this.store[id] = tokens
|
|
this.length = Object.keys(this.store).length
|
|
}
|
|
|
|
/**
|
|
* Retrieves the tokens from the store for a given key.
|
|
*
|
|
* @param {Object} id The key to lookup and retrieve from the store.
|
|
* @returns {Object}
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.prototype.get = function (id) {
|
|
return this.store[id]
|
|
}
|
|
|
|
/**
|
|
* Checks whether the store contains a key.
|
|
*
|
|
* @param {Object} id The id to look up in the store.
|
|
* @returns {Boolean}
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.prototype.has = function (id) {
|
|
return id in this.store
|
|
}
|
|
|
|
/**
|
|
* Removes the value for a key in the store.
|
|
*
|
|
* @param {Object} id The id to remove from the store.
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.prototype.remove = function (id) {
|
|
if (!this.has(id)) return
|
|
|
|
delete this.store[id]
|
|
this.length--
|
|
}
|
|
|
|
/**
|
|
* Returns a representation of the store ready for serialisation.
|
|
*
|
|
* @returns {Object}
|
|
* @memberOf Store
|
|
*/
|
|
lunr.Store.prototype.toJSON = function () {
|
|
return {
|
|
store: this.store,
|
|
length: this.length
|
|
}
|
|
}
|
|
|
|
/*!
|
|
* lunr.stemmer
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
|
|
*/
|
|
|
|
/**
|
|
* lunr.stemmer is an english language stemmer, this is a JavaScript
|
|
* implementation of the PorterStemmer taken from http://tartaurs.org/~martin
|
|
*
|
|
* @module
|
|
* @param {String} str The string to stem
|
|
* @returns {String}
|
|
* @see lunr.Pipeline
|
|
*/
|
|
lunr.stemmer = (function(){
|
|
var step2list = {
|
|
"ational" : "ate",
|
|
"tional" : "tion",
|
|
"enci" : "ence",
|
|
"anci" : "ance",
|
|
"izer" : "ize",
|
|
"bli" : "ble",
|
|
"alli" : "al",
|
|
"entli" : "ent",
|
|
"eli" : "e",
|
|
"ousli" : "ous",
|
|
"ization" : "ize",
|
|
"ation" : "ate",
|
|
"ator" : "ate",
|
|
"alism" : "al",
|
|
"iveness" : "ive",
|
|
"fulness" : "ful",
|
|
"ousness" : "ous",
|
|
"aliti" : "al",
|
|
"iviti" : "ive",
|
|
"biliti" : "ble",
|
|
"logi" : "log"
|
|
},
|
|
|
|
step3list = {
|
|
"icate" : "ic",
|
|
"ative" : "",
|
|
"alize" : "al",
|
|
"iciti" : "ic",
|
|
"ical" : "ic",
|
|
"ful" : "",
|
|
"ness" : ""
|
|
},
|
|
|
|
c = "[^aeiou]", // consonant
|
|
v = "[aeiouy]", // vowel
|
|
C = c + "[^aeiouy]*", // consonant sequence
|
|
V = v + "[aeiou]*", // vowel sequence
|
|
|
|
mgr0 = "^(" + C + ")?" + V + C, // [C]VC... is m>0
|
|
meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$", // [C]VC[V] is m=1
|
|
mgr1 = "^(" + C + ")?" + V + C + V + C, // [C]VCVC... is m>1
|
|
s_v = "^(" + C + ")?" + v; // vowel in stem
|
|
|
|
return function (w) {
|
|
var stem,
|
|
suffix,
|
|
firstch,
|
|
re,
|
|
re2,
|
|
re3,
|
|
re4;
|
|
|
|
if (w.length < 3) { return w; }
|
|
|
|
firstch = w.substr(0,1);
|
|
if (firstch == "y") {
|
|
w = firstch.toUpperCase() + w.substr(1);
|
|
}
|
|
|
|
// Step 1a
|
|
re = /^(.+?)(ss|i)es$/;
|
|
re2 = /^(.+?)([^s])s$/;
|
|
|
|
if (re.test(w)) { w = w.replace(re,"$1$2"); }
|
|
else if (re2.test(w)) { w = w.replace(re2,"$1$2"); }
|
|
|
|
// Step 1b
|
|
re = /^(.+?)eed$/;
|
|
re2 = /^(.+?)(ed|ing)$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
re = new RegExp(mgr0);
|
|
if (re.test(fp[1])) {
|
|
re = /.$/;
|
|
w = w.replace(re,"");
|
|
}
|
|
} else if (re2.test(w)) {
|
|
var fp = re2.exec(w);
|
|
stem = fp[1];
|
|
re2 = new RegExp(s_v);
|
|
if (re2.test(stem)) {
|
|
w = stem;
|
|
re2 = /(at|bl|iz)$/;
|
|
re3 = new RegExp("([^aeiouylsz])\\1$");
|
|
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
if (re2.test(w)) { w = w + "e"; }
|
|
else if (re3.test(w)) { re = /.$/; w = w.replace(re,""); }
|
|
else if (re4.test(w)) { w = w + "e"; }
|
|
}
|
|
}
|
|
|
|
// Step 1c
|
|
re = /^(.+?)y$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
stem = fp[1];
|
|
re = new RegExp(s_v);
|
|
if (re.test(stem)) { w = stem + "i"; }
|
|
}
|
|
|
|
// Step 2
|
|
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
stem = fp[1];
|
|
suffix = fp[2];
|
|
re = new RegExp(mgr0);
|
|
if (re.test(stem)) {
|
|
w = stem + step2list[suffix];
|
|
}
|
|
}
|
|
|
|
// Step 3
|
|
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
stem = fp[1];
|
|
suffix = fp[2];
|
|
re = new RegExp(mgr0);
|
|
if (re.test(stem)) {
|
|
w = stem + step3list[suffix];
|
|
}
|
|
}
|
|
|
|
// Step 4
|
|
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
|
re2 = /^(.+?)(s|t)(ion)$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
stem = fp[1];
|
|
re = new RegExp(mgr1);
|
|
if (re.test(stem)) {
|
|
w = stem;
|
|
}
|
|
} else if (re2.test(w)) {
|
|
var fp = re2.exec(w);
|
|
stem = fp[1] + fp[2];
|
|
re2 = new RegExp(mgr1);
|
|
if (re2.test(stem)) {
|
|
w = stem;
|
|
}
|
|
}
|
|
|
|
// Step 5
|
|
re = /^(.+?)e$/;
|
|
if (re.test(w)) {
|
|
var fp = re.exec(w);
|
|
stem = fp[1];
|
|
re = new RegExp(mgr1);
|
|
re2 = new RegExp(meq1);
|
|
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
|
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
|
|
w = stem;
|
|
}
|
|
}
|
|
|
|
re = /ll$/;
|
|
re2 = new RegExp(mgr1);
|
|
if (re.test(w) && re2.test(w)) {
|
|
re = /.$/;
|
|
w = w.replace(re,"");
|
|
}
|
|
|
|
// and turn initial Y back to y
|
|
|
|
if (firstch == "y") {
|
|
w = firstch.toLowerCase() + w.substr(1);
|
|
}
|
|
|
|
return w;
|
|
}
|
|
})();
|
|
|
|
lunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')
|
|
/*!
|
|
* lunr.stopWordFilter
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.stopWordFilter is an English language stop word list filter, any words
|
|
* contained in the list will not be passed through the filter.
|
|
*
|
|
* This is intended to be used in the Pipeline. If the token does not pass the
|
|
* filter then undefined will be returned.
|
|
*
|
|
* @module
|
|
* @param {String} token The token to pass through the filter
|
|
* @returns {String}
|
|
* @see lunr.Pipeline
|
|
*/
|
|
lunr.stopWordFilter = function (token) {
|
|
if (lunr.stopWordFilter.stopWords.indexOf(token) === -1) return token
|
|
}
|
|
|
|
lunr.stopWordFilter.stopWords = new lunr.SortedSet
|
|
lunr.stopWordFilter.stopWords.length = 119
|
|
lunr.stopWordFilter.stopWords.elements = [
|
|
"",
|
|
"a",
|
|
"able",
|
|
"about",
|
|
"across",
|
|
"after",
|
|
"all",
|
|
"almost",
|
|
"also",
|
|
"am",
|
|
"among",
|
|
"an",
|
|
"and",
|
|
"any",
|
|
"are",
|
|
"as",
|
|
"at",
|
|
"be",
|
|
"because",
|
|
"been",
|
|
"but",
|
|
"by",
|
|
"can",
|
|
"cannot",
|
|
"could",
|
|
"dear",
|
|
"did",
|
|
"do",
|
|
"does",
|
|
"either",
|
|
"else",
|
|
"ever",
|
|
"every",
|
|
"for",
|
|
"from",
|
|
"get",
|
|
"got",
|
|
"had",
|
|
"has",
|
|
"have",
|
|
"he",
|
|
"her",
|
|
"hers",
|
|
"him",
|
|
"his",
|
|
"how",
|
|
"however",
|
|
"i",
|
|
"if",
|
|
"in",
|
|
"into",
|
|
"is",
|
|
"it",
|
|
"its",
|
|
"just",
|
|
"least",
|
|
"let",
|
|
"like",
|
|
"likely",
|
|
"may",
|
|
"me",
|
|
"might",
|
|
"most",
|
|
"must",
|
|
"my",
|
|
"neither",
|
|
"no",
|
|
"nor",
|
|
"not",
|
|
"of",
|
|
"off",
|
|
"often",
|
|
"on",
|
|
"only",
|
|
"or",
|
|
"other",
|
|
"our",
|
|
"own",
|
|
"rather",
|
|
"said",
|
|
"say",
|
|
"says",
|
|
"she",
|
|
"should",
|
|
"since",
|
|
"so",
|
|
"some",
|
|
"than",
|
|
"that",
|
|
"the",
|
|
"their",
|
|
"them",
|
|
"then",
|
|
"there",
|
|
"these",
|
|
"they",
|
|
"this",
|
|
"tis",
|
|
"to",
|
|
"too",
|
|
"twas",
|
|
"us",
|
|
"wants",
|
|
"was",
|
|
"we",
|
|
"were",
|
|
"what",
|
|
"when",
|
|
"where",
|
|
"which",
|
|
"while",
|
|
"who",
|
|
"whom",
|
|
"why",
|
|
"will",
|
|
"with",
|
|
"would",
|
|
"yet",
|
|
"you",
|
|
"your"
|
|
]
|
|
|
|
lunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')
|
|
/*!
|
|
* lunr.trimmer
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
*/
|
|
|
|
/**
|
|
* lunr.trimmer is a pipeline function for trimming non word
|
|
* characters from the begining and end of tokens before they
|
|
* enter the index.
|
|
*
|
|
* This implementation may not work correctly for non latin
|
|
* characters and should either be removed or adapted for use
|
|
* with languages with non-latin characters.
|
|
*
|
|
* @module
|
|
* @param {String} token The token to pass through the filter
|
|
* @returns {String}
|
|
* @see lunr.Pipeline
|
|
*/
|
|
lunr.trimmer = function (token) {
|
|
return token
|
|
.replace(/^\W+/, '')
|
|
.replace(/\W+$/, '')
|
|
}
|
|
|
|
lunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')
|
|
/*!
|
|
* lunr.stemmer
|
|
* Copyright (C) 2014 Oliver Nightingale
|
|
* Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt
|
|
*/
|
|
|
|
/**
|
|
* lunr.TokenStore is used for efficient storing and lookup of the reverse
|
|
* index of token to document ref.
|
|
*
|
|
* @constructor
|
|
*/
|
|
lunr.TokenStore = function () {
|
|
this.root = { docs: {} }
|
|
this.length = 0
|
|
}
|
|
|
|
/**
|
|
* Loads a previously serialised token store
|
|
*
|
|
* @param {Object} serialisedData The serialised token store to load.
|
|
* @returns {lunr.TokenStore}
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.load = function (serialisedData) {
|
|
var store = new this
|
|
|
|
store.root = serialisedData.root
|
|
store.length = serialisedData.length
|
|
|
|
return store
|
|
}
|
|
|
|
/**
|
|
* Adds a new token doc pair to the store.
|
|
*
|
|
* By default this function starts at the root of the current store, however
|
|
* it can start at any node of any token store if required.
|
|
*
|
|
* @param {String} token The token to store the doc under
|
|
* @param {Object} doc The doc to store against the token
|
|
* @param {Object} root An optional node at which to start looking for the
|
|
* correct place to enter the doc, by default the root of this lunr.TokenStore
|
|
* is used.
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.add = function (token, doc, root) {
|
|
var root = root || this.root,
|
|
key = token[0],
|
|
rest = token.slice(1)
|
|
|
|
if (!(key in root)) root[key] = {docs: {}}
|
|
|
|
if (rest.length === 0) {
|
|
root[key].docs[doc.ref] = doc
|
|
this.length += 1
|
|
return
|
|
} else {
|
|
return this.add(rest, doc, root[key])
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether this key is contained within this lunr.TokenStore.
|
|
*
|
|
* By default this function starts at the root of the current store, however
|
|
* it can start at any node of any token store if required.
|
|
*
|
|
* @param {String} token The token to check for
|
|
* @param {Object} root An optional node at which to start
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.has = function (token) {
|
|
if (!token) return false
|
|
|
|
var node = this.root
|
|
|
|
for (var i = 0; i < token.length; i++) {
|
|
if (!node[token[i]]) return false
|
|
|
|
node = node[token[i]]
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
/**
|
|
* Retrieve a node from the token store for a given token.
|
|
*
|
|
* By default this function starts at the root of the current store, however
|
|
* it can start at any node of any token store if required.
|
|
*
|
|
* @param {String} token The token to get the node for.
|
|
* @param {Object} root An optional node at which to start.
|
|
* @returns {Object}
|
|
* @see TokenStore.prototype.get
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.getNode = function (token) {
|
|
if (!token) return {}
|
|
|
|
var node = this.root
|
|
|
|
for (var i = 0; i < token.length; i++) {
|
|
if (!node[token[i]]) return {}
|
|
|
|
node = node[token[i]]
|
|
}
|
|
|
|
return node
|
|
}
|
|
|
|
/**
|
|
* Retrieve the documents for a node for the given token.
|
|
*
|
|
* By default this function starts at the root of the current store, however
|
|
* it can start at any node of any token store if required.
|
|
*
|
|
* @param {String} token The token to get the documents for.
|
|
* @param {Object} root An optional node at which to start.
|
|
* @returns {Object}
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.get = function (token, root) {
|
|
return this.getNode(token, root).docs || {}
|
|
}
|
|
|
|
lunr.TokenStore.prototype.count = function (token, root) {
|
|
return Object.keys(this.get(token, root)).length
|
|
}
|
|
|
|
/**
|
|
* Remove the document identified by ref from the token in the store.
|
|
*
|
|
* By default this function starts at the root of the current store, however
|
|
* it can start at any node of any token store if required.
|
|
*
|
|
* @param {String} token The token to get the documents for.
|
|
* @param {String} ref The ref of the document to remove from this token.
|
|
* @param {Object} root An optional node at which to start.
|
|
* @returns {Object}
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.remove = function (token, ref) {
|
|
if (!token) return
|
|
var node = this.root
|
|
|
|
for (var i = 0; i < token.length; i++) {
|
|
if (!(token[i] in node)) return
|
|
node = node[token[i]]
|
|
}
|
|
|
|
delete node.docs[ref]
|
|
}
|
|
|
|
/**
|
|
* Find all the possible suffixes of the passed token using tokens
|
|
* currently in the store.
|
|
*
|
|
* @param {String} token The token to expand.
|
|
* @returns {Array}
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.expand = function (token, memo) {
|
|
var root = this.getNode(token),
|
|
docs = root.docs || {},
|
|
memo = memo || []
|
|
|
|
if (Object.keys(docs).length) memo.push(token)
|
|
|
|
Object.keys(root)
|
|
.forEach(function (key) {
|
|
if (key === 'docs') return
|
|
|
|
memo.concat(this.expand(token + key, memo))
|
|
}, this)
|
|
|
|
return memo
|
|
}
|
|
|
|
/**
|
|
* Returns a representation of the token store ready for serialisation.
|
|
*
|
|
* @returns {Object}
|
|
* @memberOf TokenStore
|
|
*/
|
|
lunr.TokenStore.prototype.toJSON = function () {
|
|
return {
|
|
root: this.root,
|
|
length: this.length
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* export the module via AMD, CommonnJS or as a browser global
|
|
* Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
|
|
*/
|
|
;(function (root, factory) {
|
|
if (typeof define === 'function' && define.amd) {
|
|
// AMD. Register as an anonymous module.
|
|
define('lunr',factory)
|
|
} else if (typeof exports === 'object') {
|
|
/**
|
|
* Node. Does not work with strict CommonJS, but
|
|
* only CommonJS-like enviroments that support module.exports,
|
|
* like Node.
|
|
*/
|
|
module.exports = factory()
|
|
} else {
|
|
// Browser globals (root is window)
|
|
root.lunr = factory()
|
|
}
|
|
}(this, function () {
|
|
/**
|
|
* Just return a value to define the module export.
|
|
* This example returns an object, but the module
|
|
* can return a function as the exported value.
|
|
*/
|
|
return lunr
|
|
}))
|
|
})()
|
|
;
|
|
define('core/search',[
|
|
"jQuery",
|
|
"lodash",
|
|
"lunr",
|
|
"utils/storage",
|
|
"core/state",
|
|
"core/sidebar"
|
|
], function($, _, lunr, storage, state, sidebar) {
|
|
var index = null;
|
|
var $searchBar = state.$book.find(".book-search");
|
|
var $searchInput = $searchBar.find("input");
|
|
|
|
// Use a specific idnex
|
|
var useIndex = function(data) {
|
|
index = lunr.Index.load(data);
|
|
};
|
|
|
|
// Load complete index
|
|
var loadIndex = function() {
|
|
$.getJSON(state.basePath+"/search_index.json")
|
|
.then(useIndex);
|
|
};
|
|
|
|
// Search for a term
|
|
var search = function(q) {
|
|
if (!index) return;
|
|
var results = _.chain(index.search(q))
|
|
.map(function(result) {
|
|
var parts = result.ref.split("#")
|
|
return {
|
|
path: parts[0],
|
|
hash: parts[1]
|
|
}
|
|
})
|
|
.value();
|
|
|
|
return results;
|
|
};
|
|
|
|
// Toggle search bar
|
|
var toggleSearch = function(_state) {
|
|
if (state != null && isSearchOpen() == _state) return;
|
|
|
|
state.$book.toggleClass("with-search", _state);
|
|
|
|
// If search bar is open: focus input
|
|
if (isSearchOpen()) {
|
|
sidebar.toggle(true);
|
|
$searchInput.focus();
|
|
} else {
|
|
$searchInput.blur();
|
|
$searchInput.val("");
|
|
sidebar.filter(null);
|
|
}
|
|
};
|
|
|
|
// Return true if search bar is open
|
|
var isSearchOpen = function() {
|
|
return state.$book.hasClass("with-search");
|
|
};
|
|
|
|
|
|
var init = function() {
|
|
loadIndex();
|
|
|
|
// Toggle search
|
|
state.$book.find(".book-header .toggle-search").click(function(e) {
|
|
e.preventDefault();
|
|
toggleSearch();
|
|
});
|
|
|
|
$searchInput.keyup(function(e) {
|
|
var key = (e.keyCode ? e.keyCode : e.which);
|
|
var q = $(this).val();
|
|
|
|
if (key == 27) {
|
|
e.preventDefault();
|
|
toggleSearch(false);
|
|
return;
|
|
}
|
|
if (q.length == 0) {
|
|
sidebar.filter(null);
|
|
} else {
|
|
var results = search(q);
|
|
sidebar.filter(
|
|
_.pluck(results, "path")
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
return {
|
|
init: init,
|
|
search: search,
|
|
toggle: toggleSearch
|
|
};
|
|
});
|
|
define('core/keyboard',[
|
|
"jQuery",
|
|
"Mousetrap",
|
|
"core/navigation",
|
|
"core/sidebar",
|
|
"core/search"
|
|
], function($, Mousetrap, navigation, sidebar, search){
|
|
// Bind keyboard shortcuts
|
|
var init = function() {
|
|
// Next
|
|
Mousetrap.bind(['right'], function(e) {
|
|
navigation.goNext();
|
|
return false;
|
|
});
|
|
|
|
// Prev
|
|
Mousetrap.bind(['left'], function(e) {
|
|
navigation.goPrev();
|
|
return false;
|
|
});
|
|
|
|
// Toggle Summary
|
|
Mousetrap.bind(['s'], function(e) {
|
|
sidebar.toggle();
|
|
return false;
|
|
});
|
|
|
|
// Toggle Search
|
|
Mousetrap.bind(['f'], function(e) {
|
|
search.toggle();
|
|
return false;
|
|
});
|
|
};
|
|
|
|
return {
|
|
init: init,
|
|
search: search
|
|
};
|
|
});
|
|
define('execute/javascript',[],function() {
|
|
return {
|
|
id: "javascript",
|
|
assertCode: "function assert(condition, message) { \nif (!condition) { \n throw message || \"Assertion failed\"; \n } \n }\n",
|
|
REPL: JSREPL
|
|
};
|
|
});
|
|
define('utils/execute',[
|
|
"execute/javascript"
|
|
], function(javascript) {
|
|
var LANGUAGES = {
|
|
"javascript": javascript
|
|
};
|
|
|
|
|
|
var evalJS = function(lang, code, callback) {
|
|
var ready = false;
|
|
var finished = false;
|
|
|
|
var finish = function() {
|
|
if(finished) {
|
|
return console.error('Already finished');
|
|
}
|
|
finished = true;
|
|
return callback.apply(null, arguments);
|
|
};
|
|
|
|
var repl;
|
|
|
|
// Handles all our events
|
|
var eventHandler = function(data, eventType) {
|
|
console.log([eventType, data]);
|
|
switch(eventType) {
|
|
case 'progress':
|
|
// Update UI loading bar
|
|
break;
|
|
case 'timeout':
|
|
finish(new Error(data));
|
|
break;
|
|
case 'result':
|
|
finish(null, {
|
|
value: data,
|
|
type: 'result'
|
|
});
|
|
break;
|
|
case 'error':
|
|
if(ready) {
|
|
return finish(null, {
|
|
value: data,
|
|
type: 'error'
|
|
});
|
|
}
|
|
return finish(new Error(data));
|
|
break
|
|
case 'ready':
|
|
// We're good to get results and stuff back now
|
|
ready = true;
|
|
// Eval our code now that the runtime is ready
|
|
repl.eval(code);
|
|
break;
|
|
default:
|
|
console.log('Unhandled event =', eventType, 'data =', data);
|
|
}
|
|
};
|
|
|
|
repl = new lang.REPL({
|
|
input: eventHandler,
|
|
output: eventHandler,
|
|
result: eventHandler,
|
|
error: eventHandler,
|
|
progress: eventHandler,
|
|
timeout: {
|
|
time: 30000,
|
|
callback: eventHandler
|
|
}
|
|
});
|
|
|
|
repl.loadLanguage(lang.id, eventHandler);
|
|
};
|
|
|
|
var execute = function(lang, solution, validation, callback) {
|
|
// Check language is supported
|
|
if (!LANGUAGES[lang]) return callback(new Error("Language '"+lang+"' not available for execution"));
|
|
|
|
// Validate with validation code
|
|
var code = [solution, LANGUAGES[lang].assertCode, validation].join(";\n");
|
|
evalJS(LANGUAGES[lang], code, function(err, res) {
|
|
if(err) return callback(err);
|
|
|
|
if (res.type == "error") callback(new Error(res.value));
|
|
else callback(null, res.value);
|
|
});
|
|
};
|
|
|
|
return execute;
|
|
});
|
|
define('core/exercise',[
|
|
"jQuery",
|
|
"utils/execute",
|
|
"utils/analytic",
|
|
"core/state"
|
|
], function($, execute, analytic, state){
|
|
// Bind an exercise
|
|
var prepareExercise = function($exercise) {
|
|
var codeSolution = $exercise.find(".code-solution").text();
|
|
var codeValidation = $exercise.find(".code-validation").text();
|
|
|
|
var editor = ace.edit($exercise.find(".editor").get(0));
|
|
editor.setTheme("ace/theme/tomorrow");
|
|
editor.getSession().setUseWorker(false);
|
|
editor.getSession().setMode("ace/mode/javascript");
|
|
|
|
// Submit: test code
|
|
$exercise.find(".action-submit").click(function(e) {
|
|
e.preventDefault();
|
|
|
|
analytic.track("exercise.submit");
|
|
|
|
execute("javascript", editor.getValue(), codeValidation, function(err, result) {
|
|
$exercise.toggleClass("return-error", err != null);
|
|
$exercise.toggleClass("return-success", err == null);
|
|
if (err) $exercise.find(".alert-danger").text(err.message || err);
|
|
});
|
|
});
|
|
|
|
// Set solution
|
|
$exercise.find(".action-solution").click(function(e) {
|
|
e.preventDefault();
|
|
|
|
editor.setValue(codeSolution);
|
|
editor.gotoLine(0);
|
|
});
|
|
};
|
|
|
|
// Prepare all exercise
|
|
var init = function() {
|
|
state.$book.find("section.exercise").each(function() {
|
|
prepareExercise($(this));
|
|
});
|
|
};
|
|
|
|
return {
|
|
init: init,
|
|
prepare: prepareExercise
|
|
};
|
|
});
|
|
define('core/progress',[
|
|
"lodash",
|
|
"jQuery",
|
|
"utils/storage",
|
|
"core/state"
|
|
], function(_, $, storage, state) {
|
|
// Get current level
|
|
var getCurrentLevel = function() {
|
|
return state.level;
|
|
};
|
|
|
|
// Return all levels
|
|
var getLevels = function() {
|
|
var levels = $(".book-summary li[data-level]");
|
|
return _.map(levels, function(level) {
|
|
return $(level).data("level").toString();
|
|
});
|
|
};
|
|
|
|
// Return a map chapter -> number (timestamp)
|
|
var getProgress = function() {
|
|
// Current level
|
|
var progress = storage.get("progress", {});
|
|
|
|
// Levels
|
|
var levels = getLevels();
|
|
|
|
_.each(levels, function(level) {
|
|
progress[level] = progress[level] || 0;
|
|
});
|
|
|
|
return progress;
|
|
};
|
|
|
|
// Change value of progress for a level
|
|
var markProgress = function(level, state) {
|
|
if (state == null) state = true;
|
|
|
|
var progress = getProgress();
|
|
progress[level] = state? Date.now() : 0;
|
|
|
|
storage.set("progress", progress);
|
|
};
|
|
|
|
// Show progress
|
|
var showProgress = function() {
|
|
// Update progress
|
|
var progress = getProgress();
|
|
var $summary = $(".book-summary");
|
|
|
|
_.each(progress, function(value, level) {
|
|
$summary.find("li[data-level='"+level+"']").toggleClass("done", value > 0);
|
|
});
|
|
|
|
// Mark current progress
|
|
markProgress(getCurrentLevel(), true);
|
|
};
|
|
|
|
return {
|
|
'current': getCurrentLevel,
|
|
'levels': getLevels,
|
|
'get': getProgress,
|
|
'mark': markProgress,
|
|
'show': showProgress
|
|
};
|
|
});
|
|
require([
|
|
"jQuery",
|
|
"utils/storage",
|
|
"utils/analytic",
|
|
"utils/sharing",
|
|
|
|
"core/state",
|
|
"core/keyboard",
|
|
"core/exercise",
|
|
"core/progress",
|
|
"core/sidebar",
|
|
"core/search"
|
|
], function($, storage, analytic, sharing, state, keyboard, exercise, progress, sidebar, search){
|
|
$(document).ready(function() {
|
|
var $book = state.$book;
|
|
|
|
// Initialize storage
|
|
storage.setBaseKey(state.githubId);
|
|
|
|
// Init sidebar
|
|
sidebar.init();
|
|
|
|
// Load search
|
|
search.init();
|
|
|
|
// Init keyboard
|
|
keyboard.init();
|
|
|
|
// Star and watch count
|
|
$.getJSON("https://api.github.com/repos/"+state.githubId)
|
|
.done(function(repo) {
|
|
$book.find(".count-star span").text(repo.stargazers_count);
|
|
$book.find(".count-watch span").text(repo.subscribers_count);
|
|
});
|
|
|
|
// Bind exercise
|
|
exercise.init();
|
|
|
|
// Bind sharing button
|
|
sharing.init();
|
|
|
|
// Show progress
|
|
progress.show();
|
|
|
|
// Focus on content
|
|
$(".book-body").focus();
|
|
});
|
|
});
|
|
define("app", function(){});
|
|
|