| Current Path : /home/lejardintz/www/media/foundry/3.1/scripts/ |
| Current File : /home/lejardintz/www/media/foundry/3.1/scripts/foundry.js |
/*!
* jQuery JavaScript Library v1.11.1pre
* 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-02-26T03:20Z
*/
(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 deletedIds = [];
var slice = deletedIds.slice;
var concat = deletedIds.concat;
var push = deletedIds.push;
var indexOf = deletedIds.indexOf;
var class2type = {};
var toString = class2type.toString;
var hasOwn = class2type.hasOwnProperty;
var trim = "".trim;
var support = {};
var
version = "1.11.1pre",
// 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 );
},
// Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
// 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: deletedIds.sort,
splice: deletedIds.splice
};
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, 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 || function( obj ) {
return jQuery.type(obj) === "array";
},
isWindow: function( obj ) {
/* jshint eqeqeq: false */
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;
},
isEmptyObject: function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
isPlainObject: function( obj ) {
var key;
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
return false;
}
try {
// Not own constructor property must be Object
if ( obj.constructor &&
!hasOwn.call(obj, "constructor") &&
!hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
} catch ( e ) {
// IE8,9 Will throw exceptions on certain host objects #9897
return false;
}
// Support: IE<9
// Handle iteration over inherited properties before own properties.
if ( support.ownLast ) {
for ( key in obj ) {
return hasOwn.call( obj, key );
}
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
for ( key in obj ) {}
return key === undefined || hasOwn.call( obj, key );
},
type: function( obj ) {
if ( obj == null ) {
return obj + "";
}
return typeof obj === "object" || typeof obj === "function" ?
class2type[ toString.call(obj) ] || "object" :
typeof obj;
},
// Evaluates a script in a global context
// Workarounds based on findings by Jim Driscoll
// http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
globalEval: function( data ) {
if ( data && jQuery.trim( data ) ) {
// We use execScript on Internet Explorer
// We use an anonymous function so that context is window
// rather than jQuery in Firefox
( window.execScript || function( data ) {
window[ "eval" ].call( window, data );
} )( data );
}
},
// 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;
},
// Use native String.trim function wherever possible
trim: trim && !trim.call("\uFEFF\xA0") ?
function( text ) {
return text == null ?
"" :
trim.call( text );
} :
// Otherwise use our own trimming functionality
function( text ) {
return text == null ?
"" :
( text + "" ).replace( rtrim, "" );
},
// 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 ) {
var len;
if ( arr ) {
if ( indexOf ) {
return indexOf.call( arr, elem, i );
}
len = arr.length;
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
for ( ; i < len; i++ ) {
// Skip accessing in sparse arrays
if ( i in arr && arr[ i ] === elem ) {
return i;
}
}
}
return -1;
},
merge: function( first, second ) {
var len = +second.length,
j = 0,
i = first.length;
while ( j < len ) {
first[ i++ ] = second[ j++ ];
}
// Support: IE<9
// Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
if ( len !== len ) {
while ( second[j] !== undefined ) {
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 args, proxy, tmp;
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: function() {
return +( new Date() );
},
// 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.18
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-02-05
*/
(function( window ) {
var i,
support,
Expr,
getText,
isXML,
compile,
select,
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<24
// 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 multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results );
}
return results;
}
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, match /* 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 ( !match ) {
match = tokenize( selector );
}
i = match.length;
while ( i-- ) {
cached = matcherFromTokens( match[i] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
}
// Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
// Save selector and tokenization
cached.selector = selector;
}
return cached;
};
/**
* A low-level selection function that works with Sizzle's compiled
* selector functions
* @param {String|Function} selector A selector or a pre-compiled
* selector function built with Sizzle.compile
* @param {Element} context
* @param {Array} [results]
* @param {Array} [seed] A set of elements to match against
*/
select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find,
compiled = typeof selector === "function" && selector,
match = !seed && tokenize( (selector = compiled.selector || selector) );
results = results || [];
// Try to minimize operations if there is no seed and 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;
// Precompiled matchers will still verify ancestry, so step up a level
} else if ( compiled ) {
context = context.parentNode;
}
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 if one is not provided
// Provide `match` to avoid retokenization if we modified the selector above
( compiled || 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 ( jQuery.inArray( elem, qualifier ) >= 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,
ret = [],
self = this,
len = self.length;
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,
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
// 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.charAt(0) === "<" && selector.charAt( 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 ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}
// Otherwise, we 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 = [],
cur = elem[ dir ];
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
cur = cur[dir];
}
return matched;
},
sibling: function( n, elem ) {
var r = [];
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
return r;
}
});
jQuery.fn.extend({
has: function( target ) {
var i,
targets = jQuery( target, this ),
len = targets.length;
return this.filter(function() {
for ( i = 0; i < len; 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 jQuery.inArray( this[0], jQuery( elem ) );
}
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[0] : elem, this );
},
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 ) {
do {
cur = cur[ dir ];
} while ( cur && 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 jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
jQuery.merge( [], elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );
if ( name.slice( -5 ) !== "Until" ) {
selector = until;
}
if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}
if ( this.length > 1 ) {
// Remove duplicates
if ( !guaranteedUnique[ name ] ) {
ret = jQuery.unique( ret );
}
// Reverse order for parents* and prev-derivatives
if ( rparentsprev.test( name ) ) {
ret = ret.reverse();
}
}
return this.pushStack( ret );
};
});
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 // Flag to know if list is currently firing
firing,
// Last fire value (for non-forgettable lists)
memory,
// Flag to know if list was already fired
fired,
// End of the loop when firing
firingLength,
// Index of currently firing callback (modified by remove if needed)
firingIndex,
// First callback to fire (used internally by add and fireWith)
firingStart,
// 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;
}
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready );
}
// 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");
}
}
});
/**
* Clean-up method for dom ready events
*/
function detach() {
if ( document.addEventListener ) {
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
} else {
document.detachEvent( "onreadystatechange", completed );
window.detachEvent( "onload", completed );
}
}
/**
* The ready event handler and self cleanup method
*/
function completed() {
// readyState === "complete" is good enough for us to call the dom ready in oldIE
if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
detach();
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 );
// Standards-based browsers support DOMContentLoaded
} else if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false );
// If IE event model is used
} else {
// Ensure firing before onload, maybe late but safe also for iframes
document.attachEvent( "onreadystatechange", completed );
// A fallback to window.onload, that will always work
window.attachEvent( "onload", completed );
// If IE and not a frame
// continually check to see if the document is ready
var top = false;
try {
top = window.frameElement == null && document.documentElement;
} catch(e) {}
if ( top && top.doScroll ) {
(function doScrollCheck() {
if ( !jQuery.isReady ) {
try {
// Use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
top.doScroll("left");
} catch(e) {
return setTimeout( doScrollCheck, 50 );
}
// detach all dom ready events
detach();
// and execute any waiting functions
jQuery.ready();
}
})();
}
}
}
return readyList.promise( obj );
};
var strundefined = typeof undefined;
// Support: IE<9
// Iteration over object's inherited properties before its own
var i;
for ( i in jQuery( support ) ) {
break;
}
support.ownLast = i !== "0";
// Note: most support tests are defined in their respective modules.
// false until the test is run
support.inlineBlockNeedsLayout = false;
jQuery(function() {
// We need to execute this one support test ASAP because we need to know
// if body.style.zoom needs to be set.
var container, div,
body = document.getElementsByTagName("body")[0];
if ( !body ) {
// Return for frameset docs that don't have a body
return;
}
// Setup
container = document.createElement( "div" );
container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
div = document.createElement( "div" );
body.appendChild( container ).appendChild( div );
if ( typeof div.style.zoom !== strundefined ) {
// Support: IE<8
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
div.style.cssText = "border:0;margin:0;width:1px;padding:1px;display:inline;zoom:1";
if ( (support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 )) ) {
// Prevent IE 6 from affecting layout for positioned elements #11048
// Prevent IE from shrinking the body in IE 7 mode #12869
// Support: IE<8
body.style.zoom = 1;
}
}
body.removeChild( container );
// Null elements to avoid leaks in IE
container = div = null;
});
(function() {
var div = document.createElement( "div" );
// Execute the test only if not already executed in another module.
if (support.deleteExpando == null) {
// Support: IE<9
support.deleteExpando = true;
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
}
// Null elements to avoid leaks in IE.
div = null;
})();
/**
* Determines whether an object can have data
*/
jQuery.acceptData = function( elem ) {
var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
nodeType = +elem.nodeType || 1;
// Do not set data on non-element DOM nodes because it will not be cleared (#8335).
return nodeType !== 1 && nodeType !== 9 ?
false :
// Nodes accept data unless otherwise specified; rejection can be conditional
!noData || noData !== true && elem.getAttribute("classid") === noData;
};
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
rmultiDash = /([A-Z])/g;
function dataAttr( elem, key, data ) {
// If nothing was found internally, try to fetch any
// data from the HTML5 data-* attribute
if ( data === undefined && elem.nodeType === 1 ) {
var 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
jQuery.data( elem, key, data );
} else {
data = undefined;
}
}
return data;
}
// checks a cache object for emptiness
function isEmptyDataObject( obj ) {
var name;
for ( name in obj ) {
// if the public data object is empty, the private is still empty
if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
continue;
}
if ( name !== "toJSON" ) {
return false;
}
}
return true;
}
function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var ret, thisCache,
internalKey = jQuery.expando,
// We have to handle DOM nodes and JS objects differently because IE6-7
// can't GC object references properly across the DOM-JS boundary
isNode = elem.nodeType,
// Only DOM nodes need the global jQuery cache; JS object data is
// attached directly to the object so GC can occur automatically
cache = isNode ? jQuery.cache : elem,
// Only defining an ID for JS objects if its cache already exists allows
// the code to shortcut on the same path as a DOM node with no cache
id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
// Avoid doing any more work than we need to when trying to get data on an
// object that has no data at all
if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
return;
}
if ( !id ) {
// Only DOM nodes need a new unique ID for each element since their data
// ends up in the global cache
if ( isNode ) {
id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
} else {
id = internalKey;
}
}
if ( !cache[ id ] ) {
// Avoid exposing jQuery metadata on plain JS objects when the object
// is serialized using JSON.stringify
cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
}
// An object can be passed to jQuery.data instead of a key/value pair; this gets
// shallow copied over onto the existing cache
if ( typeof name === "object" || typeof name === "function" ) {
if ( pvt ) {
cache[ id ] = jQuery.extend( cache[ id ], name );
} else {
cache[ id ].data = jQuery.extend( cache[ id ].data, name );
}
}
thisCache = cache[ id ];
// jQuery data() is stored in a separate object inside the object's internal data
// cache in order to avoid key collisions between internal data and user-defined
// data.
if ( !pvt ) {
if ( !thisCache.data ) {
thisCache.data = {};
}
thisCache = thisCache.data;
}
if ( data !== undefined ) {
thisCache[ jQuery.camelCase( name ) ] = data;
}
// Check for both converted-to-camel and non-converted data property names
// If a data property was specified
if ( typeof name === "string" ) {
// First Try to find as-is property data
ret = thisCache[ name ];
// Test for null|undefined property data
if ( ret == null ) {
// Try to find the camelCased property
ret = thisCache[ jQuery.camelCase( name ) ];
}
} else {
ret = thisCache;
}
return ret;
}
function internalRemoveData( elem, name, pvt ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
var thisCache, i,
isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
if ( name ) {
thisCache = pvt ? cache[ id ] : cache[ id ].data;
if ( thisCache ) {
// Support array or space separated string names for data keys
if ( !jQuery.isArray( name ) ) {
// try the string as a key before any manipulation
if ( name in thisCache ) {
name = [ name ];
} else {
// split the camel cased version by spaces unless a key with the spaces exists
name = jQuery.camelCase( name );
if ( name in thisCache ) {
name = [ name ];
} else {
name = name.split(" ");
}
}
} else {
// 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 = name.concat( jQuery.map( name, jQuery.camelCase ) );
}
i = name.length;
while ( i-- ) {
delete thisCache[ name[i] ];
}
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
return;
}
}
}
// See jQuery.data for more information
if ( !pvt ) {
delete cache[ id ].data;
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !isEmptyDataObject( cache[ id ] ) ) {
return;
}
}
// Destroy the cache
if ( isNode ) {
jQuery.cleanData( [ elem ], true );
// Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
/* jshint eqeqeq: false */
} else if ( support.deleteExpando || cache != cache.window ) {
/* jshint eqeqeq: true */
delete cache[ id ];
// When all else fails, null
} else {
cache[ id ] = null;
}
}
jQuery.extend({
cache: {},
// The following elements (space-suffixed to avoid Object.prototype collisions)
// throw uncatchable exceptions if you attempt to set expando properties
noData: {
"applet ": true,
"embed ": true,
// ...but Flash objects (which have this classid) *can* handle expandos
"object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
},
hasData: function( elem ) {
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !isEmptyDataObject( elem );
},
data: function( elem, name, data ) {
return internalData( elem, name, data );
},
removeData: function( elem, name ) {
return internalRemoveData( elem, name );
},
// For internal use only.
_data: function( elem, name, data ) {
return internalData( elem, name, data, true );
},
_removeData: function( elem, name ) {
return internalRemoveData( elem, name, true );
}
});
jQuery.fn.extend({
data: function( key, value ) {
var i, name, data,
elem = this[0],
attrs = elem && elem.attributes;
// Special expections of .data basically thwart jQuery.access,
// so implement the relevant behavior ourselves
// Gets all values
if ( key === undefined ) {
if ( this.length ) {
data = jQuery.data( elem );
if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
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 ] );
}
}
jQuery._data( elem, "parsedAttrs", true );
}
}
return data;
}
// Sets multiple values
if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
return arguments.length > 1 ?
// Sets one value
this.each(function() {
jQuery.data( this, key, value );
}) :
// Gets one value
// Try to fetch any internally stored data first
elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
},
removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
jQuery.extend({
queue: function( elem, type, data ) {
var queue;
if ( elem ) {
type = ( type || "fx" ) + "queue";
queue = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( data ) {
if ( !queue || jQuery.isArray(data) ) {
queue = jQuery._data( 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 jQuery._data( elem, key ) || jQuery._data( elem, key, {
empty: jQuery.Callbacks("once memory").add(function() {
jQuery._removeData( elem, type + "queue" );
jQuery._removeData( elem, 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 = jQuery._data( 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 );
};
// 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,
length = 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 < length; i++ ) {
fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
}
}
}
return chainable ?
elems :
// Gets
bulk ?
fn.call( elems ) :
length ? fn( elems[0], key ) : emptyGet;
};
var rcheckableType = (/^(?:checkbox|radio)$/i);
(function() {
var fragment = document.createDocumentFragment(),
div = document.createElement("div"),
input = document.createElement("input");
// Setup
div.setAttribute( "className", "t" );
div.innerHTML = " <link/><table></table><a href='/a'>a</a>";
// IE strips leading whitespace when .innerHTML is used
support.leadingWhitespace = div.firstChild.nodeType === 3;
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
support.tbody = !div.getElementsByTagName( "tbody" ).length;
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
// Makes sure cloning an html5 element does not cause problems
// Where outerHTML is undefined, this still works
support.html5Clone =
document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav></:nav>";
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
input.type = "checkbox";
input.checked = true;
fragment.appendChild( input );
support.appendChecked = input.checked;
// Make sure textarea (and checkbox) defaultValue is properly cloned
// Support: IE6-IE11+
div.innerHTML = "<textarea>x</textarea>";
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
// #11217 - WebKit loses check when the name is after the checked attribute
fragment.appendChild( div );
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;
// Support: IE<9
// Opera does not clone events (and typeof div.attachEvent === undefined).
// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
support.noCloneEvent = true;
if ( div.attachEvent ) {
div.attachEvent( "onclick", function() {
support.noCloneEvent = false;
});
div.cloneNode( true ).click();
}
// Execute the test only if not already executed in another module.
if (support.deleteExpando == null) {
// Support: IE<9
support.deleteExpando = true;
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}
}
// Null elements to avoid leaks in IE.
fragment = div = input = null;
})();
(function() {
var i, eventName,
div = document.createElement( "div" );
// Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
for ( i in { submit: true, change: true, focusin: true }) {
eventName = "on" + i;
if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
div.setAttribute( eventName, "t" );
support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
}
}
// Null elements to avoid leaks in IE.
div = null;
})();
var rformElems = /^(?:input|select|textarea)$/i,
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 tmp, events, t, handleObjIn,
special, eventHandle, handleObj,
handlers, type, namespaces, origType,
elemData = jQuery._data( 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 && (!e || jQuery.event.triggered !== e.type) ?
jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
undefined;
};
// Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
eventHandle.elem = elem;
}
// 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/attachEvent if the special events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
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;
}
// Nullify elem to prevent memory leaks in IE
elem = null;
},
// Detach an event or set of events from an element
remove: function( elem, types, handler, selector, mappedTypes ) {
var j, handleObj, tmp,
origCount, t, events,
special, handlers, type,
namespaces, origType,
elemData = jQuery.hasData( elem ) && jQuery._data( 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;
// removeData also checks for emptiness and clears the expando if empty
// so use it instead of delete
jQuery._removeData( elem, "events" );
}
},
trigger: function( event, data, elem, onlyHandlers ) {
var handle, ontype, cur,
bubbleType, special, tmp, i,
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 = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( 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.
// Can't use an .isFunction() check here because IE6/7 fails that test.
// Don't do default actions on window, that's where global variables be (#6170)
if ( ontype && 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;
try {
elem[ type ]();
} catch ( e ) {
// IE<9 dies on focus/blur to hidden element (#1486,#12518)
// only reproducible on winXP IE8 native, not IE9 in IE8 mode
}
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, ret, handleObj, matched, j,
handlerQueue = [],
args = slice.call( arguments ),
handlers = ( jQuery._data( 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 sel, handleObj, matches, i,
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") ) {
/* jshint eqeqeq: false */
for ( ; cur != this; cur = cur.parentNode || this ) {
/* jshint eqeqeq: true */
// Don't check non-elements (#13208)
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
if ( cur.nodeType === 1 && (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;
},
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: IE<9
// Fix target property (#1925)
if ( !event.target ) {
event.target = originalEvent.srcElement || document;
}
// Support: Chrome 23+, Safari?
// Target should not be a text node (#504, #13143)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// Support: IE<9
// For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
event.metaKey = !!event.metaKey;
return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
},
// 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 fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
filter: function( event, original ) {
var body, eventDoc, doc,
button = original.button,
fromElement = original.fromElement;
// 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 relatedTarget, if necessary
if ( !event.relatedTarget && fromElement ) {
event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
}
// 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;
}
},
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 ) {
try {
this.focus();
return false;
} catch ( e ) {
// Support: IE<9
// If we error on focus to hidden element (#1486, #12518),
// let .trigger() run the handlers
}
}
},
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 ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
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 ) {
// Even when returnValue equals to undefined Firefox will still show alert
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 = document.removeEventListener ?
function( elem, type, handle ) {
if ( elem.removeEventListener ) {
elem.removeEventListener( type, handle, false );
}
} :
function( elem, type, handle ) {
var name = "on" + type;
if ( elem.detachEvent ) {
// #8545, #7054, preventing memory leaks for custom events in IE6-8
// detachEvent needed property on element, by name of that event, to properly expose it to GC
if ( typeof elem[ name ] === strundefined ) {
elem[ name ] = null;
}
elem.detachEvent( name, handle );
}
};
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 ||
src.defaultPrevented === undefined && (
// Support: IE < 9
src.returnValue === false ||
// Support: Android < 4.0
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 ) {
return;
}
// If preventDefault exists, run it on the original event
if ( e.preventDefault ) {
e.preventDefault();
// Support: IE
// Otherwise set the returnValue property of the original event to false
} else {
e.returnValue = false;
}
},
stopPropagation: function() {
var e = this.originalEvent;
this.isPropagationStopped = returnTrue;
if ( !e ) {
return;
}
// If stopPropagation exists, run it on the original event
if ( e.stopPropagation ) {
e.stopPropagation();
}
// Support: IE
// Set the cancelBubble property of the original event to true
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
}
};
// Create mouseenter/leave events using mouseover/out and event-time checks
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;
}
};
});
// IE submit delegation
if ( !support.submitBubbles ) {
jQuery.event.special.submit = {
setup: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Lazy-add a submit handler when a descendant form may potentially be submitted
jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
// Node name check avoids a VML-related crash in IE (#9807)
var elem = e.target,
form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
if ( form && !jQuery._data( form, "submitBubbles" ) ) {
jQuery.event.add( form, "submit._submit", function( event ) {
event._submit_bubble = true;
});
jQuery._data( form, "submitBubbles", true );
}
});
// return undefined since we don't need an event listener
},
postDispatch: function( event ) {
// If form was submitted by the user, bubble the event up the tree
if ( event._submit_bubble ) {
delete event._submit_bubble;
if ( this.parentNode && !event.isTrigger ) {
jQuery.event.simulate( "submit", this.parentNode, event, true );
}
}
},
teardown: function() {
// Only need this for delegated form submit events
if ( jQuery.nodeName( this, "form" ) ) {
return false;
}
// Remove delegated handlers; cleanData eventually reaps submit handlers attached above
jQuery.event.remove( this, "._submit" );
}
};
}
// IE change delegation and checkbox/radio fix
if ( !support.changeBubbles ) {
jQuery.event.special.change = {
setup: function() {
if ( rformElems.test( this.nodeName ) ) {
// IE doesn't fire change on a check/radio until blur; trigger it on click
// after a propertychange. Eat the blur-change in special.change.handle.
// This still fires onchange a second time for check/radio after blur.
if ( this.type === "checkbox" || this.type === "radio" ) {
jQuery.event.add( this, "propertychange._change", function( event ) {
if ( event.originalEvent.propertyName === "checked" ) {
this._just_changed = true;
}
});
jQuery.event.add( this, "click._change", function( event ) {
if ( this._just_changed && !event.isTrigger ) {
this._just_changed = false;
}
// Allow triggered, simulated change events (#11500)
jQuery.event.simulate( "change", this, event, true );
});
}
return false;
}
// Delegated event; lazy-add a change handler on descendant inputs
jQuery.event.add( this, "beforeactivate._change", function( e ) {
var elem = e.target;
if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
jQuery.event.add( elem, "change._change", function( event ) {
if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
jQuery.event.simulate( "change", this.parentNode, event, true );
}
});
jQuery._data( elem, "changeBubbles", true );
}
});
},
handle: function( event ) {
var elem = event.target;
// Swallow native change events from checkbox/radio, we already triggered them above
if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
return event.handleObj.handler.apply( this, arguments );
}
},
teardown: function() {
jQuery.event.remove( this, "._change" );
return !rformElems.test( this.nodeName );
}
};
}
// Create "bubbling" focus and blur events
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 = jQuery._data( doc, fix );
if ( !attaches ) {
doc.addEventListener( orig, handler, true );
}
jQuery._data( doc, fix, ( attaches || 0 ) + 1 );
},
teardown: function() {
var doc = this.ownerDocument || this,
attaches = jQuery._data( doc, fix ) - 1;
if ( !attaches ) {
doc.removeEventListener( orig, handler, true );
jQuery._removeData( doc, fix );
} else {
jQuery._data( doc, fix, attaches );
}
}
};
});
}
jQuery.fn.extend({
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var type, origFn;
// 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 );
}
}
});
function createSafeFragment( document ) {
var list = nodeNames.split( "|" ),
safeFrag = document.createDocumentFragment();
if ( safeFrag.createElement ) {
while ( list.length ) {
safeFrag.createElement(
list.pop()
);
}
}
return safeFrag;
}
var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
"header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
rleadingWhitespace = /^\s+/,
rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
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 = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
area: [ 1, "<map>", "</map>" ],
param: [ 1, "<object>", "</object>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
// IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
// unless wrapped in a div with non-breaking characters in front of it.
_default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
},
safeFragment = createSafeFragment( document ),
fragmentDiv = safeFragment.appendChild( document.createElement("div") );
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
function getAll( context, tag ) {
var elems, elem,
i = 0,
found = typeof context.getElementsByTagName !== strundefined ? context.getElementsByTagName( tag || "*" ) :
typeof context.querySelectorAll !== strundefined ? context.querySelectorAll( tag || "*" ) :
undefined;
if ( !found ) {
for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
if ( !tag || jQuery.nodeName( elem, tag ) ) {
found.push( elem );
} else {
jQuery.merge( found, getAll( elem, tag ) );
}
}
}
return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
jQuery.merge( [ context ], found ) :
found;
}
// Used in buildFragment, fixes the defaultChecked property
function fixDefaultChecked( elem ) {
if ( rcheckableType.test( elem.type ) ) {
elem.defaultChecked = elem.checked;
}
}
// Support: IE<8
// 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 = (jQuery.find.attr( elem, "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 elem,
i = 0;
for ( ; (elem = elems[i]) != null; i++ ) {
jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
}
}
function cloneCopyEvent( src, dest ) {
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
return;
}
var type, i, l,
oldData = jQuery._data( src ),
curData = jQuery._data( dest, oldData ),
events = oldData.events;
if ( events ) {
delete curData.handle;
curData.events = {};
for ( type in events ) {
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ] );
}
}
}
// make the cloned public data object a copy from the original
if ( curData.data ) {
curData.data = jQuery.extend( {}, curData.data );
}
}
function fixCloneNodeIssues( src, dest ) {
var nodeName, e, data;
// We do not need to do anything for non-Elements
if ( dest.nodeType !== 1 ) {
return;
}
nodeName = dest.nodeName.toLowerCase();
// IE6-8 copies events bound via attachEvent when using cloneNode.
if ( !support.noCloneEvent && dest[ jQuery.expando ] ) {
data = jQuery._data( dest );
for ( e in data.events ) {
jQuery.removeEvent( dest, e, data.handle );
}
// Event data gets referenced instead of copied if the expando gets copied too
dest.removeAttribute( jQuery.expando );
}
// IE blanks contents when cloning scripts, and tries to evaluate newly-set text
if ( nodeName === "script" && dest.text !== src.text ) {
disableScript( dest ).text = src.text;
restoreScript( dest );
// IE6-10 improperly clones children of object elements using classid.
// IE10 throws NoModificationAllowedError if parent is null, #12132.
} else if ( nodeName === "object" ) {
if ( dest.parentNode ) {
dest.outerHTML = src.outerHTML;
}
// This path appears unavoidable for IE9. When cloning an object
// element in IE9, the outerHTML strategy above is not sufficient.
// If the src has innerHTML and the destination does not,
// copy the src.innerHTML into the dest.innerHTML. #10324
if ( support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
dest.innerHTML = src.innerHTML;
}
} else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
// IE6-8 fails to persist the checked state of a cloned checkbox
// or radio button. Worse, IE6-7 fail to give the cloned element
// a checked appearance if the defaultChecked value isn't also set
dest.defaultChecked = dest.checked = src.checked;
// IE6-7 get confused and end up setting the value of a cloned
// checkbox/radio button to an empty string instead of "on"
if ( dest.value !== src.value ) {
dest.value = src.value;
}
// IE6-8 fails to return the selected option to the default selected
// state when cloning options
} else if ( nodeName === "option" ) {
dest.defaultSelected = dest.selected = src.defaultSelected;
// IE6-8 fails to set the defaultValue to the correct value when
// cloning other types of input fields
} else if ( nodeName === "input" || nodeName === "textarea" ) {
dest.defaultValue = src.defaultValue;
}
}
jQuery.extend({
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
var destElements, node, clone, i, srcElements,
inPage = jQuery.contains( elem.ownerDocument, elem );
if ( support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
clone = elem.cloneNode( true );
// IE<=8 does not properly clone detached, unknown element nodes
} else {
fragmentDiv.innerHTML = elem.outerHTML;
fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
}
if ( (!support.noCloneEvent || !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 );
// Fix all IE cloning issues
for ( i = 0; (node = srcElements[i]) != null; ++i ) {
// Ensure that the destination node is not null; Fixes #9587
if ( destElements[i] ) {
fixCloneNodeIssues( node, 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; (node = srcElements[i]) != null; i++ ) {
cloneCopyEvent( node, destElements[i] );
}
} else {
cloneCopyEvent( elem, clone );
}
}
// Preserve script evaluation history
destElements = getAll( clone, "script" );
if ( destElements.length > 0 ) {
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
}
destElements = srcElements = node = null;
// Return the cloned set
return clone;
},
buildFragment: function( elems, context, scripts, selection ) {
var j, elem, contains,
tmp, tag, tbody, wrap,
l = elems.length,
// Ensure a safe fragment
safe = createSafeFragment( context ),
nodes = [],
i = 0;
for ( ; i < l; i++ ) {
elem = elems[ i ];
if ( elem || elem === 0 ) {
// Add nodes directly
if ( jQuery.type( elem ) === "object" ) {
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 || safe.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;
}
// Manually add leading whitespace removed by IE
if ( !support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
}
// Remove IE's autoinserted <tbody> from table fragments
if ( !support.tbody ) {
// String was a <table>, *may* have spurious <tbody>
elem = tag === "table" && !rtbody.test( elem ) ?
tmp.firstChild :
// String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !rtbody.test( elem ) ?
tmp :
0;
j = elem && elem.childNodes.length;
while ( j-- ) {
if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
elem.removeChild( tbody );
}
}
}
jQuery.merge( nodes, tmp.childNodes );
// Fix #12392 for WebKit and IE > 9
tmp.textContent = "";
// Fix #12392 for oldIE
while ( tmp.firstChild ) {
tmp.removeChild( tmp.firstChild );
}
// Remember the top-level container for proper cleanup
tmp = safe.lastChild;
}
}
}
// Fix #11356: Clear elements from fragment
if ( tmp ) {
safe.removeChild( tmp );
}
// Reset defaultChecked for any radios and checkboxes
// about to be appended to the DOM in IE 6/7 (#8060)
if ( !support.appendChecked ) {
jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
}
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( safe.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 );
}
}
}
}
tmp = null;
return safe;
},
cleanData: function( elems, /* internal */ acceptData ) {
var elem, type, id, data,
i = 0,
internalKey = jQuery.expando,
cache = jQuery.cache,
deleteExpando = support.deleteExpando,
special = jQuery.event.special;
for ( ; (elem = elems[i]) != null; i++ ) {
if ( acceptData || jQuery.acceptData( elem ) ) {
id = elem[ internalKey ];
data = id && cache[ id ];
if ( data ) {
if ( data.events ) {
for ( type in data.events ) {
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 );
}
}
}
// Remove cache only if it was not already removed by jQuery.event.remove
if ( cache[ id ] ) {
delete cache[ id ];
// IE does not allow us to delete expando properties from nodes,
// nor does it have a removeAttribute function on Document nodes;
// we must handle all of these cases
if ( deleteExpando ) {
delete elem[ internalKey ];
} else if ( typeof elem.removeAttribute !== strundefined ) {
elem.removeAttribute( internalKey );
} else {
elem[ internalKey ] = null;
}
deletedIds.push( id );
}
}
}
}
}
});
jQuery.fn.extend({
text: function( value ) {
return access( this, function( value ) {
return value === undefined ?
jQuery.text( this ) :
this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( 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++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( getAll( elem, false ) );
}
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
// If this is a select, ensure that it displays empty (#12336)
// Support: IE<9
if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
elem.options.length = 0;
}
}
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 ) {
return elem.nodeType === 1 ?
elem.innerHTML.replace( rinlinejQuery, "" ) :
undefined;
}
// See if we can take a shortcut and just use innerHTML
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
( support.htmlSerialize || !rnoshimcache.test( value ) ) &&
( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
value = value.replace( rxhtmlTag, "<$1></$2>" );
try {
for (; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
elem = this[i] || {};
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 first, node, hasScripts,
scripts, doc, fragment,
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 ) {
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 || "" ) &&
!jQuery._data( 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.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
}
}
}
}
// Fix #11809: Avoid leaking memory
fragment = first = null;
}
}
return this;
}
});
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
var elems,
i = 0,
ret = [],
insert = jQuery( selector ),
last = insert.length - 1;
for ( ; i <= last; i++ ) {
elems = i === last ? this : this.clone(true);
jQuery( insert[i] )[ original ]( elems );
// Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
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 ].contentWindow || iframe[ 0 ].contentDocument ).document;
// Support: IE
doc.write();
doc.close();
display = actualDisplay( nodeName, doc );
iframe.detach();
}
// Store the correct default display
elemdisplay[ nodeName ] = display;
}
return display;
}
(function() {
var a, shrinkWrapBlocksVal,
div = document.createElement( "div" ),
// Support: Firefox<29, Android 2.3 (Prefixed box-sizing versions).
divReset =
"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;" +
"display:block;padding:0;margin:0;border:0";
// Setup
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
a = div.getElementsByTagName( "a" )[ 0 ];
a.style.cssText = "float:left;opacity:.5";
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
support.opacity = /^0.5/.test( a.style.opacity );
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
support.cssFloat = !!a.style.cssFloat;
div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box";
// Null elements to avoid leaks in IE.
a = div = null;
support.shrinkWrapBlocks = function() {
var body, container, div, containerStyles;
if ( shrinkWrapBlocksVal == null ) {
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body ) {
// Test fired too early or in an unsupported environment, exit.
return;
}
containerStyles = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px";
container = document.createElement( "div" );
div = document.createElement( "div" );
body.appendChild( container ).appendChild( div );
// Will be changed later if needed.
shrinkWrapBlocksVal = false;
if ( typeof div.style.zoom !== strundefined ) {
// Support: IE6
// Check if elements with layout shrink-wrap their children
div.style.cssText = divReset + ";width:1px;padding:1px;zoom:1";
div.innerHTML = "<div></div>";
div.firstChild.style.width = "5px";
shrinkWrapBlocksVal = div.offsetWidth !== 3;
}
body.removeChild( container );
// Null elements to avoid leaks in IE.
body = container = div = null;
}
return shrinkWrapBlocksVal;
};
})();
var rmargin = (/^margin/);
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
var getStyles, curCSS,
rposition = /^(top|right|bottom|left)$/;
if ( window.getComputedStyle ) {
getStyles = function( elem ) {
return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
};
curCSS = function( elem, name, computed ) {
var width, minWidth, maxWidth, ret,
style = elem.style;
computed = computed || getStyles( elem );
// getPropertyValue is only needed for .css('filter') in IE9, see #12537
ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined;
if ( computed ) {
if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
ret = jQuery.style( elem, name );
}
// A tribute to the "awesome hack by Dean Edwards"
// Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
// Safari 5.1.7 (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;
}
}
// Support: IE
// IE returns zIndex value as an integer.
return ret === undefined ?
ret :
ret + "";
};
} else if ( document.documentElement.currentStyle ) {
getStyles = function( elem ) {
return elem.currentStyle;
};
curCSS = function( elem, name, computed ) {
var left, rs, rsLeft, ret,
style = elem.style;
computed = computed || getStyles( elem );
ret = computed ? computed[ name ] : undefined;
// Avoid setting ret to empty string here
// so we don't default to auto
if ( ret == null && style && style[ name ] ) {
ret = style[ name ];
}
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
// but not position css attributes, as those are proportional to the parent element instead
// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
// Remember the original values
left = style.left;
rs = elem.runtimeStyle;
rsLeft = rs && rs.left;
// Put in the new values to get a computed value out
if ( rsLeft ) {
rs.left = elem.currentStyle.left;
}
style.left = name === "fontSize" ? "1em" : ret;
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
if ( rsLeft ) {
rs.left = rsLeft;
}
}
// Support: IE
// IE returns zIndex value as an integer.
return ret === undefined ?
ret :
ret + "" || "auto";
};
}
function addGetHookIf( conditionFn, hookFn ) {
// Define the hook, we'll check on the first run if it's really needed.
return {
get: function() {
var condition = conditionFn();
if ( condition == null ) {
// The test was not ready at this point; screw the hook this time
// but check again when needed next time.
return;
}
if ( condition ) {
// 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 a, reliableHiddenOffsetsVal, boxSizingVal, boxSizingReliableVal,
pixelPositionVal,
div = document.createElement( "div" ),
containerStyles = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px",
// Support: Firefox<29, Android 2.3 (Prefixed box-sizing versions).
divReset =
"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;" +
"display:block;padding:0;margin:0;border:0";
// Setup
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
a = div.getElementsByTagName( "a" )[ 0 ];
a.style.cssText = "float:left;opacity:.5";
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
support.opacity = /^0.5/.test( a.style.opacity );
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
support.cssFloat = !!a.style.cssFloat;
div.style.backgroundClip = "content-box";
div.cloneNode( true ).style.backgroundClip = "";
support.clearCloneStyle = div.style.backgroundClip === "content-box";
// Null elements to avoid leaks in IE.
a = div = null;
jQuery.extend(support, {
reliableHiddenOffsets: function() {
if ( reliableHiddenOffsetsVal != null ) {
return reliableHiddenOffsetsVal;
}
var container, tds, isSupported,
div = document.createElement( "div" ),
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body ) {
// Return for frameset docs that don't have a body
return;
}
// Setup
div.setAttribute( "className", "t" );
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
container = document.createElement( "div" );
container.style.cssText = containerStyles;
body.appendChild( container ).appendChild( div );
// Support: IE8
// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
tds = div.getElementsByTagName( "td" );
tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
isSupported = ( tds[ 0 ].offsetHeight === 0 );
tds[ 0 ].style.display = "";
tds[ 1 ].style.display = "none";
// Support: IE8
// Check if empty table cells still have offsetWidth/Height
reliableHiddenOffsetsVal = isSupported && ( tds[ 0 ].offsetHeight === 0 );
body.removeChild( container );
// Null elements to avoid leaks in IE.
div = body = null;
return reliableHiddenOffsetsVal;
},
boxSizing: function() {
if ( boxSizingVal == null ) {
computeStyleTests();
}
return boxSizingVal;
},
boxSizingReliable: function() {
if ( boxSizingReliableVal == null ) {
computeStyleTests();
}
return boxSizingReliableVal;
},
pixelPosition: function() {
if ( pixelPositionVal == null ) {
computeStyleTests();
}
return pixelPositionVal;
},
// Support: Android 2.3
reliableMarginRight: function() {
var body, container, div, marginDiv,
// Support: IE<9.
// IE should pass the test but we're using getComputedStyle
// to compute it so just return true if the method is not present.
reliableMarginRightVal = true;
// Use window.getComputedStyle because jsdom on node.js will break without it.
if ( window.getComputedStyle ) {
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body ) {
// Test fired too early or in an unsupported environment, exit.
return;
}
container = document.createElement( "div" );
div = document.createElement( "div" );
container.style.cssText = containerStyles;
body.appendChild( container ).appendChild( div );
// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. (#3333)
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
marginDiv = div.appendChild( document.createElement( "div" ) );
marginDiv.style.cssText = div.style.cssText = divReset;
marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px";
reliableMarginRightVal =
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
body.removeChild( container );
}
return reliableMarginRightVal;
}
});
function computeStyleTests() {
var container, div,
body = document.getElementsByTagName( "body" )[ 0 ];
if ( !body ) {
// Test fired too early or in an unsupported environment, exit.
return;
}
container = document.createElement( "div" );
div = document.createElement( "div" );
container.style.cssText = containerStyles;
body.appendChild( container ).appendChild( div );
// Support: Firefox<29, Android 2.3 (Prefixed box-sizing versions).
div.style.cssText =
"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;" +
"position:absolute;display:block;padding:1px;border:1px;width:4px;" +
"margin-top:1%;top:1%";
// Workaround failing boxSizing test due to offsetWidth returning wrong value
// with some non-1 values of body zoom, ticket #13543
jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() {
boxSizingVal = div.offsetWidth === 4;
});
// Will be changed later if needed.
boxSizingReliableVal = true;
pixelPositionVal = false;
// Use window.getComputedStyle because jsdom on node.js will break without it.
if ( window.getComputedStyle ) {
pixelPositionVal = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
boxSizingReliableVal =
( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
}
body.removeChild( container );
// Null elements to avoid leaks in IE.
div = body = null;
}
})();
// 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
ralpha = /alpha\([^)]*\)/i,
ropacity = /opacity\s*=\s*([^)]*)/,
// 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.charAt(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 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 ] = jQuery._data( 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 ] = jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
}
} else {
if ( !values[ index ] ) {
hidden = isHidden( elem );
if ( display && display !== "none" || !hidden ) {
jQuery._data( 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;
}
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 = support.boxSizing() && 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";
}
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": support.cssFloat ? "cssFloat" : "styleFloat"
},
// 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 specifing 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: IE
// Swallow errors from 'invalid' CSS values (#5509)
try {
// Support: Chrome, Safari
// Setting style to blank string required to delete "style: x !important;"
style[ name ] = "";
style[ name ] = value;
} catch(e) {}
}
} 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 num, val, 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,
support.boxSizing() && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
styles
) : 0
);
}
};
});
if ( !support.opacity ) {
jQuery.cssHooks.opacity = {
get: function( elem, computed ) {
// IE uses filters for opacity
return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
computed ? "1" : "";
},
set: function( elem, value ) {
var style = elem.style,
currentStyle = elem.currentStyle,
opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
filter = currentStyle && currentStyle.filter || style.filter || "";
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
style.zoom = 1;
// if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
// if value === "", then remove inline opacity #12685
if ( ( value >= 1 || value === "" ) &&
jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
style.removeAttribute ) {
// Setting style.filter to null, "" & " " still leave "filter:" in the cssText
// if "filter:" is present at all, clearType is disabled, we want to avoid this
// style.removeAttribute is IE Only, but so apparently is this code path...
style.removeAttribute( "filter" );
// if there is no filter style applied in a css rule or unset inline opacity, we are done
if ( value === "" || currentStyle && !currentStyle.filter ) {
return;
}
}
// otherwise, set new filter values
style.filter = ralpha.test( filter ) ?
filter.replace( ralpha, opacity ) :
filter + " " + opacity;
}
};
}
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: IE <=9
// 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,
attrs = { height: type },
i = 0;
// 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, dDisplay,
anim = this,
orig = {},
style = elem.style,
hidden = elem.nodeType && isHidden( elem ),
dataShow = jQuery._data( 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 IE does 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" );
dDisplay = defaultDisplay( elem.nodeName );
if ( display === "none" ) {
display = dDisplay;
}
if ( display === "inline" &&
jQuery.css( elem, "float" ) === "none" ) {
// inline-level elements accept inline-block;
// block-level elements need to be inline with layout
if ( !support.inlineBlockNeedsLayout || dDisplay === "inline" ) {
style.display = "inline-block";
} else {
style.zoom = 1;
}
}
}
if ( opts.overflow ) {
style.overflow = "hidden";
if ( !support.shrinkWrapBlocks() ) {
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 = jQuery._data( 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;
jQuery._removeData( 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 || jQuery._data( 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 = jQuery._data( 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 = jQuery._data( 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,
timers = jQuery.timers,
i = 0;
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 a, input, select, opt,
div = document.createElement("div" );
// Setup
div.setAttribute( "className", "t" );
div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
a = div.getElementsByTagName("a")[ 0 ];
// First batch of tests.
select = document.createElement("select");
opt = select.appendChild( document.createElement("option") );
input = div.getElementsByTagName("input")[ 0 ];
a.style.cssText = "top:1px";
// Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
support.getSetAttribute = div.className !== "t";
// Get the style information from getAttribute
// (IE uses .cssText instead)
support.style = /top/.test( a.getAttribute("style") );
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
support.hrefNormalized = a.getAttribute("href") === "/a";
// Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
support.checkOn = !!input.value;
// Make sure that a selected-by-default option has a working selected property.
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
support.optSelected = opt.selected;
// Tests for enctype support on a form (#6743)
support.enctype = !!document.createElement("form").enctype;
// 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;
// Support: IE8 only
// Check if we can trust getAttribute("value")
input = document.createElement( "input" );
input.setAttribute( "value", "" );
support.input = input.getAttribute( "value" ) === "";
// Check if an input maintains its value after becoming a radio
input.value = "t";
input.setAttribute( "type", "radio" );
support.radioValue = input.value === "t";
// Null elements to avoid leaks in IE.
a = input = select = opt = div = null;
})();
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: {
option: {
get: function( elem ) {
var val = jQuery.find.attr( elem, "value" );
return val != null ?
val :
jQuery.text( elem );
}
},
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 ];
// oldIE 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 ( jQuery.inArray( jQuery.valHooks.option.get( option ), values ) >= 0 ) {
// Support: IE6
// When new option element is added to select box we need to
// force reflow of newly added node in order to workaround delay
// of initialization properties
try {
option.selected = optionSet = true;
} catch ( _ ) {
// Will be executed only in IE6
option.scrollHeight;
}
} else {
option.selected = false;
}
}
// Force browsers to behave consistently when non-matching value is set
if ( !optionSet ) {
elem.selectedIndex = -1;
}
return options;
}
}
}
});
// 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;
};
}
});
var nodeHook, boolHook,
attrHandle = jQuery.expr.attrHandle,
ruseDefault = /^(?:checked|selected)$/i,
getSetAttribute = support.getSetAttribute,
getSetInput = support.input;
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
if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
elem[ propName ] = false;
// Support: IE<9
// Also clear defaultChecked/defaultSelected (if appropriate)
} else {
elem[ jQuery.camelCase( "default-" + name ) ] =
elem[ propName ] = false;
}
// See #9699 for explanation of this approach (setting first, then removal)
} else {
jQuery.attr( elem, name, "" );
}
elem.removeAttribute( getSetAttribute ? name : propName );
}
}
},
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;
}
}
}
}
});
// Hook for boolean attributes
boolHook = {
set: function( elem, value, name ) {
if ( value === false ) {
// Remove boolean attributes when set to false
jQuery.removeAttr( elem, name );
} else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
// IE<8 needs the *property* name
elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
// Use defaultChecked and defaultSelected for oldIE
} else {
elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
}
return name;
}
};
// Retrieve booleans specially
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
var getter = attrHandle[ name ] || jQuery.find.attr;
attrHandle[ name ] = getSetInput && getSetAttribute || !ruseDefault.test( 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;
} :
function( elem, name, isXML ) {
if ( !isXML ) {
return elem[ jQuery.camelCase( "default-" + name ) ] ?
name.toLowerCase() :
null;
}
};
});
// fix oldIE attroperties
if ( !getSetInput || !getSetAttribute ) {
jQuery.attrHooks.value = {
set: function( elem, value, name ) {
if ( jQuery.nodeName( elem, "input" ) ) {
// Does not return so that setAttribute is also used
elem.defaultValue = value;
} else {
// Use nodeHook if defined (#1954); otherwise setAttribute is fine
return nodeHook && nodeHook.set( elem, value, name );
}
}
};
}
// IE6/7 do not support getting/setting some attributes with get/setAttribute
if ( !getSetAttribute ) {
// Use this for any attribute in IE6/7
// This fixes almost every IE6/7 issue
nodeHook = {
set: function( elem, value, name ) {
// Set the existing or create a new attribute node
var ret = elem.getAttributeNode( name );
if ( !ret ) {
elem.setAttributeNode(
(ret = elem.ownerDocument.createAttribute( name ))
);
}
ret.value = value += "";
// Break association with cloned elements by also using setAttribute (#9646)
if ( name === "value" || value === elem.getAttribute( name ) ) {
return value;
}
}
};
// Some attributes are constructed with empty-string values when not defined
attrHandle.id = attrHandle.name = attrHandle.coords =
function( elem, name, isXML ) {
var ret;
if ( !isXML ) {
return (ret = elem.getAttributeNode( name )) && ret.value !== "" ?
ret.value :
null;
}
};
// Fixing value retrieval on a button requires this module
jQuery.valHooks.button = {
get: function( elem, name ) {
var ret = elem.getAttributeNode( name );
if ( ret && ret.specified ) {
return ret.value;
}
},
set: nodeHook.set
};
// Set contenteditable to false on removals(#10429)
// Setting to empty string throws an error as an invalid value
jQuery.attrHooks.contenteditable = {
set: function( elem, value, name ) {
nodeHook.set( elem, value === "" ? false : value, name );
}
};
// Set width and height to auto instead of 0 on empty string( Bug #8150 )
// This is for removals
jQuery.each([ "width", "height" ], function( i, name ) {
jQuery.attrHooks[ name ] = {
set: function( elem, value ) {
if ( value === "" ) {
elem.setAttribute( name, "auto" );
return value;
}
}
};
});
}
if ( !support.style ) {
jQuery.attrHooks.style = {
get: function( elem ) {
// Return undefined in the case of empty string
// Note: IE uppercases css property names, but if we were to .toLowerCase()
// .cssText, that would destroy case senstitivity in URL's, like in "background"
return elem.style.cssText || undefined;
},
set: function( elem, value ) {
return ( elem.style.cssText = value + "" );
}
};
}
var rfocusable = /^(?:input|select|textarea|button|object)$/i,
rclickable = /^(?:a|area)$/i;
jQuery.fn.extend({
prop: function( name, value ) {
return access( this, jQuery.prop, name, value, arguments.length > 1 );
},
removeProp: function( name ) {
name = jQuery.propFix[ name ] || name;
return this.each(function() {
// try/catch handles cases where IE balks (such as removing a property on window)
try {
this[ name ] = undefined;
delete this[ name ];
} catch( e ) {}
});
}
});
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 ) {
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
// Use proper attribute retrieval(#12072)
var tabindex = jQuery.find.attr( elem, "tabindex" );
return tabindex ?
parseInt( tabindex, 10 ) :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
-1;
}
}
}
});
// Some attributes require a special call on IE
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !support.hrefNormalized ) {
// href/src property should get the full normalized URL (#10299/#12915)
jQuery.each([ "href", "src" ], function( i, name ) {
jQuery.propHooks[ name ] = {
get: function( elem ) {
return elem.getAttribute( name, 4 );
}
};
});
}
// Support: Safari, IE9+
// mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( !support.optSelected ) {
jQuery.propHooks.selected = {
get: function( elem ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( 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;
});
// IE6/7 call enctype encoding
if ( !support.enctype ) {
jQuery.propFix.enctype = "encoding";
}
var rclass = /[\t\r\n\f]/g;
jQuery.fn.extend({
addClass: function( value ) {
var classes, elem, cur, clazz, j, finalValue,
i = 0,
len = this.length,
proceed = typeof value === "string" && value;
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,
i = 0,
len = this.length,
proceed = arguments.length === 0 || typeof value === "string" && value;
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
jQuery._data( 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 ? "" : jQuery._data( 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;
}
});
// 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 = (/\?/);
var rvalidtokens = /(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;
jQuery.parseJSON = function( data ) {
// Attempt to parse using the native JSON parser first
if ( window.JSON && window.JSON.parse ) {
// Support: Android 2.3
// Workaround failure to string-cast null input
return window.JSON.parse( data + "" );
}
var requireNonComma,
depth = null,
str = jQuery.trim( data + "" );
// Guard against invalid (and possibly dangerous) input by ensuring that nothing remains
// after removing valid tokens
return str && !jQuery.trim( str.replace( rvalidtokens, function( token, comma, open, close ) {
// Force termination if we see a misplaced comma
if ( requireNonComma && comma ) {
depth = 0;
}
// Perform no more replacements after returning to outermost depth
if ( depth === 0 ) {
return token;
}
// Commas must not follow "[", "{", or ","
requireNonComma = open || comma;
// Determine new depth
// array/object open ("[" or "{"): depth += true - false (increment)
// array/object close ("]" or "}"): depth += false - true (decrement)
// other cases ("," or primitive): depth += true - true (numeric cast)
depth += !close - !open;
// Remove this token
return "";
}) ) ?
( Function( "return " + str ) )() :
jQuery.error( "Invalid JSON: " + data );
};
// Cross-browser xml parsing
jQuery.parseXML = function( data ) {
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
}
try {
if ( window.DOMParser ) { // Standard
tmp = new DOMParser();
xml = tmp.parseFromString( data, "text/xml" );
} else { // IE
xml = new ActiveXObject( "Microsoft.XMLDOM" );
xml.async = "false";
xml.loadXML( data );
}
} catch( e ) {
xml = undefined;
}
if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
};
var
// Document location
ajaxLocParts,
ajaxLocation,
rhash = /#.*$/,
rts = /([?&])_=[^&]*/,
rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
// #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.charAt( 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 deep, key,
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 firstDataType, ct, finalDataType, type,
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 // Cross-domain detection vars
parts,
// Loop variable
i,
// URL without anti-cache param
cacheURL,
// Response headers as string
responseHeadersString,
// timeout handle
timeoutTimer,
// To know if global events are to be dispatched
fireGlobals,
transport,
// Response headers
responseHeaders,
// 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 (#5866: IE7 issue with protocol-less urls)
// 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 ) {
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
var 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.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}
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 ||
(!support.reliableHiddenOffsets() &&
((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
};
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();
}
});
// Create the request object
// (This is still attached to ajaxSettings for backward compatibility)
jQuery.ajaxSettings.xhr = window.ActiveXObject !== undefined ?
// Support: IE6+
function() {
// XHR cannot access local files, always use ActiveX for that case
return !this.isLocal &&
// Support: IE7-8
// oldIE XHR does not support non-RFC2616 methods (#13240)
// See http://msdn.microsoft.com/en-us/library/ie/ms536648(v=vs.85).aspx
// and http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9
// Although this check for six methods instead of eight
// since IE also does not support "trace" and "connect"
/^(get|post|head|put|delete|options)$/i.test( this.type ) &&
createStandardXHR() || createActiveXHR();
} :
// For all other browsers, use the standard XMLHttpRequest object
createStandardXHR;
var xhrId = 0,
xhrCallbacks = {},
xhrSupported = jQuery.ajaxSettings.xhr();
// Support: IE<10
// Open requests must be manually aborted on unload (#5280)
if ( window.ActiveXObject ) {
jQuery( window ).on( "unload", function() {
for ( var key in xhrCallbacks ) {
xhrCallbacks[ key ]( undefined, true );
}
});
}
// Determine support properties
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
xhrSupported = support.ajax = !!xhrSupported;
// Create transport if the browser can provide an xhr
if ( xhrSupported ) {
jQuery.ajaxTransport(function( options ) {
// Cross domain only allowed if supported through XMLHttpRequest
if ( !options.crossDomain || support.cors ) {
var callback;
return {
send: function( headers, complete ) {
var i,
xhr = options.xhr(),
id = ++xhrId;
// Open the socket
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 ) {
// Support: IE<9
// IE's ActiveXObject throws a 'Type Mismatch' exception when setting
// request header to a null-value.
//
// To keep consistent with other XHR implementations, cast the value
// to string and ignore `undefined`.
if ( headers[ i ] !== undefined ) {
xhr.setRequestHeader( i, headers[ i ] + "" );
}
}
// 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 );
// Listener
callback = function( _, isAbort ) {
var status, statusText, responses;
// Was never called and is aborted or complete
if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
// Clean up
delete xhrCallbacks[ id ];
callback = undefined;
xhr.onreadystatechange = jQuery.noop;
// Abort manually if needed
if ( isAbort ) {
if ( xhr.readyState !== 4 ) {
xhr.abort();
}
} else {
responses = {};
status = xhr.status;
// Support: IE<10
// Accessing binary-data responseText throws an exception
// (#11426)
if ( typeof xhr.responseText === "string" ) {
responses.text = xhr.responseText;
}
// Firefox throws an exception when accessing
// statusText for faulty cross-domain requests
try {
statusText = xhr.statusText;
} catch( e ) {
// We normalize with Webkit giving an empty statusText
statusText = "";
}
// Filter status for non standard behaviors
// If the request is local and we have data: assume a success
// (success with no data won't get notified, that's the best we
// can do given current implementations)
if ( !status && options.isLocal && !options.crossDomain ) {
status = responses.text ? 200 : 404;
// IE - #1450: sometimes returns 1223 when it should be 204
} else if ( status === 1223 ) {
status = 204;
}
}
}
// Call complete if needed
if ( responses ) {
complete( status, statusText, responses, xhr.getAllResponseHeaders() );
}
};
if ( !options.async ) {
// if we're in sync mode we fire the callback
callback();
} else if ( xhr.readyState === 4 ) {
// (IE6 & IE7) if it's in cache and has been
// retrieved directly we need to fire the callback
setTimeout( callback );
} else {
// Add to the list of active xhr callbacks
xhr.onreadystatechange = xhrCallbacks[ id ] = callback;
}
},
abort: function() {
if ( callback ) {
callback( undefined, true );
}
}
};
}
});
}
// Functions to create xhrs
function createStandardXHR() {
try {
return new window.XMLHttpRequest();
} catch( e ) {}
}
function createActiveXHR() {
try {
return new window.ActiveXObject( "Microsoft.XMLHTTP" );
} catch( e ) {}
}
// 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 global
jQuery.ajaxPrefilter( "script", function( s ) {
if ( s.cache === undefined ) {
s.cache = false;
}
if ( s.crossDomain ) {
s.type = "GET";
s.global = false;
}
});
// Bind script tag hack transport
jQuery.ajaxTransport( "script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.head || jQuery("head")[0] || document.documentElement;
return {
send: function( _, callback ) {
script = document.createElement("script");
script.async = true;
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
script.src = s.url;
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function( _, isAbort ) {
if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
// Remove the script
if ( script.parentNode ) {
script.parentNode.removeChild( script );
}
// Dereference the script
script = null;
// Callback if not abort
if ( !isAbort ) {
callback( 200, "success" );
}
}
};
// Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
// Use native DOM manipulation to avoid our domManip AJAX trickery
head.insertBefore( script, head.firstChild );
},
abort: function() {
if ( script ) {
script.onload( undefined, true );
}
}
};
}
});
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, response, type,
self = this,
off = url.indexOf(" ");
if ( off >= 0 ) {
selector = url.slice( off, url.length );
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 || elem.parentWindow :
false;
}
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" ) &&
jQuery.inArray("auto", [ curCSSTop, curCSSLeft ] ) > -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,
box = { top: 0, left: 0 },
elem = this[ 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.scrollTop ) - ( docElem.clientTop || 0 ),
left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
};
},
position: function() {
if ( !this[ 0 ] ) {
return;
}
var offsetParent, offset,
parentOffset = { top: 0, left: 0 },
elem = this[ 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
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
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 = /Y/.test( prop );
jQuery.fn[ method ] = function( val ) {
return access( this, function( elem, method, val ) {
var win = getWindow( elem );
if ( val === undefined ) {
return win ? (prop in win) ? win[ prop ] :
win.document.documentElement[ method ] :
elem[ method ];
}
if ( win ) {
win.scrollTo(
!top ? val : jQuery( win ).scrollLeft(),
top ? val : jQuery( win ).scrollTop()
);
} 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
// unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
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;
// Limit scope pollution from any deprecated API
(function() {
var matched, browser;
// Use of jQuery.browser is frowned upon.
// More details: http://api.jquery.com/jQuery.browser
// jQuery.uaMatch maintained for back-compat
jQuery.uaMatch = function( ua ) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
matched = jQuery.uaMatch( navigator.userAgent );
browser = {};
if ( matched.browser ) {
browser[ matched.browser ] = true;
browser.version = matched.version;
}
// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
browser.webkit = true;
} else if ( browser.webkit ) {
browser.safari = true;
}
jQuery.browser = browser;
})();
jQuery.version = "3.1";
jQuery.long_version = "3.1.17";
jQuery.uid = function(p,s) {
return ((p) ? p : "") + Math.random().toString().replace(".","") + ((s) ? s : "");
};
jQuery.globalNamespace = jQuery.uid("Foundry");
window[jQuery.globalNamespace] = jQuery;
jQuery.run = function(command) {
return (jQuery.isFunction(command)) ? command(jQuery) : null;
};
jQuery.initialize = function(options) {
if (jQuery.initialized) return;
jQuery.extend(jQuery, options);
if (jQuery.environment=="development") {
window.F = jQuery;
}
jQuery.initialized = true;
// Execute any pending modules
FD31.module.execute();
}
// Register jquery into bootloader
FD31.jquery(jQuery);
// 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;
});
}
return jQuery;
}));
FD31.plugin("lodash", function($) {
/**
* @license
* Lo-Dash 1.3.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.4.4 <http://underscorejs.org/>
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
* Available under MIT license <http://lodash.com/license>
*/
/** 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 internally to indicate various things */
var indicatorObject = {};
/** 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 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 HTML entities */
var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g;
/**
* Used to match ES6 template delimiters
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-7.8.6
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
/** Used to match regexp flags from their coerced string values */
var reFlags = /\w*$/;
/** Used to match "interpolate" template delimiters */
var reInterpolate = /<%=([\s\S]+?)%>/g;
/** Used to detect functions containing a `this` reference */
var reThis = (reThis = /\bthis\b/) && reThis.test(runInContext) && reThis;
/** 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 leading whitespace and zeros to be removed */
var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
/** Used to match HTML characters */
var reUnescapedHtml = /[&<>"']/g;
/** 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', 'setImmediate', '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 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'
};
/** Detect free variable `exports` */
var freeExports = objectTypes[typeof exports] && exports;
/** Detect free variable `module` */
var freeModule = objectTypes[typeof module] && module && module.exports == freeExports && module;
/** Detect free variable `global`, from Node.js or Browserified code, and use it as `window` */
var freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
window = freeGlobal;
}
/*--------------------------------------------------------------------------*/
/**
* A basic implementation of `_.indexOf` without support for binary searches
* or `fromIndex` constraints.
*
* @private
* @param {Array} array The array to search.
* @param {Mixed} 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 basicIndexOf(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array.length;
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 {Mixed} 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];
}
if (type != 'number' && type != 'string') {
type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = cache[type] || (cache[type] = {});
return type == 'object'
? (cache[key] && basicIndexOf(cache[key], value) > -1 ? 0 : -1)
: (cache[key] ? 0 : -1);
}
/**
* Adds a given `value` to the corresponding cache object.
*
* @private
* @param {Mixed} 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') {
if ((typeCache[key] || (typeCache[key] = [])).push(value) == this.array.length) {
cache[type] = false;
}
} 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` values, 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 ai = a.index,
bi = b.index;
a = a.criteria;
b = b.criteria;
// ensure a stable sort in V8 and other engines
// http://code.google.com/p/v8/issues/detail?id=90
if (a !== b) {
if (a > b || typeof a == 'undefined') {
return 1;
}
if (a < b || typeof b == 'undefined') {
return -1;
}
}
return ai < bi ? -1 : 1;
}
/**
* 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;
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 cache.object === false
? (releaseObject(result), null)
: 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,
'leading': false,
'maxWait': 0,
'null': false,
'number': null,
'object': null,
'push': null,
'string': null,
'trailing': false,
'true': false,
'undefined': false,
'value': null
};
}
/**
* A no-operation function.
*
* @private
*/
function noop() {
// no operation performed
}
/**
* 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=window] 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.com/#x11.1.5.
context = context ? _.defaults(window.Object(), context, _.pick(window, contextProps)) : window;
/** 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 detect if a method is native */
var reNative = RegExp('^' +
String(objectProto.valueOf)
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
.replace(/valueOf|for [^\]]+/g, '.+?') + '$'
);
/** Native method shortcuts */
var ceil = Math.ceil,
clearTimeout = context.clearTimeout,
concat = arrayRef.concat,
floor = Math.floor,
fnToString = Function.prototype.toString,
getPrototypeOf = reNative.test(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
hasOwnProperty = objectProto.hasOwnProperty,
push = arrayRef.push,
setImmediate = context.setImmediate,
setTimeout = context.setTimeout,
toString = objectProto.toString;
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeBind = reNative.test(nativeBind = toString.bind) && nativeBind,
nativeCreate = reNative.test(nativeCreate = Object.create) && nativeCreate,
nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = context.isFinite,
nativeIsNaN = context.isNaN,
nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys,
nativeMax = Math.max,
nativeMin = Math.min,
nativeParseInt = context.parseInt,
nativeRandom = Math.random,
nativeSlice = arrayRef.slice;
/** Detect various environments */
var isIeOpera = reNative.test(context.attachEvent),
isV8 = nativeBind && !/\n|true/.test(nativeBind + isIeOpera);
/** 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 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`, `createCallback`, `debounce`, `defaults`,
* `defer`, `delay`, `difference`, `filter`, `flatten`, `forEach`, `forIn`,
* `forOwn`, `functions`, `groupBy`, `initial`, `intersection`, `invert`,
* `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
* `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `push`, `range`,
* `reject`, `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`, `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
* passed, otherwise they return unwrapped values.
*
* @name _
* @constructor
* @alias chain
* @category Chaining
* @param {Mixed} 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 {Mixed} value The value to wrap in a `lodash` instance.
* @returns {Object} Returns a `lodash` instance.
*/
function lodashWrapper(value) {
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 `Function#bind` exists and is inferred to be fast (all but V8).
*
* @memberOf _.support
* @type Boolean
*/
support.fastBind = nativeBind && !isV8;
/**
* 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
}
};
/*--------------------------------------------------------------------------*/
/**
* Creates a function that, when called, invokes `func` with the `this` binding
* of `thisArg` and prepends any `partialArgs` to the arguments passed to the
* bound function.
*
* @private
* @param {Function|String} func The function to bind or the method name.
* @param {Mixed} [thisArg] The `this` binding of `func`.
* @param {Array} partialArgs An array of arguments to be partially applied.
* @param {Object} [idicator] Used to indicate binding by key or partially
* applying arguments from the right.
* @returns {Function} Returns the new bound function.
*/
function createBound(func, thisArg, partialArgs, indicator) {
var isFunc = isFunction(func),
isPartial = !partialArgs,
key = thisArg;
// juggle arguments
if (isPartial) {
var rightIndicator = indicator;
partialArgs = thisArg;
}
else if (!isFunc) {
if (!indicator) {
throw new TypeError;
}
thisArg = func;
}
function bound() {
// `Function#bind` spec
// http://es5.github.com/#x15.3.4.5
var args = arguments,
thisBinding = isPartial ? this : thisArg;
if (!isFunc) {
func = thisArg[key];
}
if (partialArgs.length) {
args = args.length
? (args = nativeSlice.call(args), rightIndicator ? args.concat(partialArgs) : partialArgs.concat(args))
: partialArgs;
}
if (this instanceof bound) {
// ensure `new bound` is an instance of `func`
thisBinding = createObject(func.prototype);
// mimic the constructor's `return` behavior
// http://es5.github.com/#x13.2.2
var result = func.apply(thisBinding, args);
return isObject(result) ? result : thisBinding;
}
return func.apply(thisBinding, args);
}
return bound;
}
/**
* Creates a new object with the specified `prototype`.
*
* @private
* @param {Object} prototype The prototype object.
* @returns {Object} Returns the new object.
*/
function createObject(prototype) {
return isObject(prototype) ? nativeCreate(prototype) : {};
}
/**
* 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 `basicIndexOf` function.
*
* @private
* @returns {Function} Returns the "indexOf" function.
*/
function getIndexOf(array, value, fromIndex) {
var result = (result = lodash.indexOf) === indexOf ? basicIndexOf : result;
return result;
}
/**
* Creates a function that juggles arguments, allowing argument overloading
* for `_.flatten` and `_.uniq`, before passing them to the given `func`.
*
* @private
* @param {Function} func The function to wrap.
* @returns {Function} Returns the new function.
*/
function overloadWrapper(func) {
return function(array, flag, callback, thisArg) {
// juggle arguments
if (typeof flag != 'boolean' && flag != null) {
thisArg = callback;
callback = !(thisArg && thisArg[flag] === array) ? flag : undefined;
flag = false;
}
if (callback != null) {
callback = lodash.createCallback(callback, thisArg);
}
return func(array, flag, callback, thisArg);
};
}
/**
* 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 {Mixed} 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 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 {Mixed} 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 toString.call(value) == argsClass;
}
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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;
/**
* 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 a new 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 `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
* @example
*
* _.keys({ 'one': 1, 'two': 2, 'three': 3 });
* // => ['one', 'two', 'three'] (order is not guaranteed)
*/
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);
/*--------------------------------------------------------------------------*/
/**
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources will overwrite property assignments of previous
* sources. If a `callback` function is passed, 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} [source1, source2, ...] The source objects.
* @param {Function} [callback] The function to customize assigning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
* @example
*
* _.assign({ 'name': 'moe' }, { 'age': 40 });
* // => { 'name': 'moe', 'age': 40 }
*
* var defaults = _.partialRight(_.assign, function(a, b) {
* return typeof a == 'undefined' ? b : a;
* });
*
* var food = { 'name': 'apple' };
* defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' }
*/
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 = lodash.createCallback(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 `deep` is `true`, nested objects will also
* be cloned, otherwise they will be assigned by reference. If a `callback`
* function is passed, it will be executed to produce the cloned values. If
* `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 {Mixed} value The value to clone.
* @param {Boolean} [deep=false] A flag to indicate a deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @param- {Array} [stackA=[]] Tracks traversed source objects.
* @param- {Array} [stackB=[]] Associates clones with source counterparts.
* @returns {Mixed} Returns the cloned `value`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* var shallow = _.clone(stooges);
* shallow[0] === stooges[0];
* // => true
*
* var deep = _.clone(stooges, true);
* deep[0] === stooges[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, deep, callback, thisArg, stackA, stackB) {
var result = value;
// allows working with "Collections" methods without using their `callback`
// argument, `index|key`, for this method's `callback`
if (typeof deep != 'boolean' && deep != null) {
thisArg = callback;
callback = deep;
deep = false;
}
if (typeof callback == 'function') {
callback = (typeof thisArg == 'undefined')
? callback
: lodash.createCallback(callback, thisArg, 1);
result = callback(result);
if (typeof result != 'undefined') {
return result;
}
result = value;
}
// inspect [[Class]]
var isObj = isObject(result);
if (isObj) {
var className = toString.call(result);
if (!cloneableClasses[className]) {
return result;
}
var isArr = isArray(result);
}
// shallow clone
if (!isObj || !deep) {
return isObj
? (isArr ? slice(result) : assign({}, result))
: result;
}
var ctor = ctorByClass[className];
switch (className) {
case boolClass:
case dateClass:
return new ctor(+result);
case numberClass:
case stringClass:
return new ctor(result);
case regexpClass:
return ctor(result.source, reFlags.exec(result));
}
// 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];
}
}
// init cloned object
result = isArr ? ctor(result.length) : {};
// 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;
}
}
// 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] = clone(objValue, deep, callback, undefined, stackA, stackB);
});
if (initedStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result;
}
/**
* Creates a deep clone of `value`. If a `callback` function is passed,
* it will be executed to produce the cloned values. If `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 {Mixed} value The value to deep clone.
* @param {Function} [callback] The function to customize cloning values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the deep cloned `value`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* var deep = _.cloneDeep(stooges);
* deep[0] === stooges[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 clone(value, true, callback, thisArg);
}
/**
* 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} [source1, source2, ...] The source objects.
* @param- {Object} [guard] Allows working with `_.reduce` without using its
* callback's `key` and `object` arguments as sources.
* @returns {Object} Returns the destination object.
* @example
*
* var food = { 'name': 'apple' };
* _.defaults(food, { 'name': 'banana', 'type': 'fruit' });
* // => { 'name': 'apple', 'type': 'fruit' }
*/
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 similar to `_.find`, except that it returns the key of the
* element that passes the callback check, instead of the element itself.
*
* @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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the key of the found element, else `undefined`.
* @example
*
* _.findKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) {
* return num % 2 == 0;
* });
* // => 'b'
*/
function findKey(object, callback, thisArg) {
var result;
callback = lodash.createCallback(callback, thisArg);
forOwn(object, function(value, key, object) {
if (callback(value, key, object)) {
result = key;
return false;
}
});
return result;
}
/**
* Iterates over `object`'s own and inherited enumerable properties, 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 {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* function Dog(name) {
* this.name = name;
* }
*
* Dog.prototype.bark = function() {
* alert('Woof, woof!');
* };
*
* _.forIn(new Dog('Dagny'), function(value, key) {
* alert(key);
* });
* // => alerts 'name' and 'bark' (order is not guaranteed)
*/
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 : lodash.createCallback(callback, thisArg);
for (index in iterable) {
if (callback(iterable[index], index, collection) === false) return result;
}
return result
};
/**
* Iterates over an object's own enumerable properties, 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 {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns `object`.
* @example
*
* _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
* alert(key);
* });
* // => alerts '0', '1', and 'length' (order is not guaranteed)
*/
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 : lodash.createCallback(callback, thisArg);
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
};
/**
* Creates a sorted array 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 a new 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 object `property` exists and is a direct property,
* instead of an inherited property.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to check.
* @param {String} property The property to check for.
* @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, property) {
return object ? hasOwnProperty.call(object, property) : 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': 'moe', 'second': 'larry' });
* // => { 'moe': 'first', 'larry': '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 {Mixed} 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 || toString.call(value) == boolClass;
}
/**
* Checks if `value` is a date.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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 {Mixed} 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 `callback` is passed, it will be executed to
* compare values. If `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 {Mixed} a The value to compare.
* @param {Mixed} b The other value to compare.
* @param {Function} [callback] The function to customize comparing values.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @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`.
* @example
*
* var moe = { 'name': 'moe', 'age': 40 };
* var copy = { 'name': 'moe', 'age': 40 };
*
* moe == copy;
* // => false
*
* _.isEqual(moe, 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, stackA, stackB) {
// used to indicate that when comparing objects, `a` has at least the properties of `b`
var whereIndicator = callback === indicatorObject;
if (typeof callback == 'function' && !whereIndicator) {
callback = lodash.createCallback(callback, thisArg, 2);
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 || (type != 'function' && type != 'object')) &&
(!b || (otherType != 'function' && otherType != 'object'))) {
return false;
}
// exit early for `null` and `undefined`, avoiding ES3's Function#call behavior
// http://es5.github.com/#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.com/#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
if (hasOwnProperty.call(a, '__wrapped__ ') || hasOwnProperty.call(b, '__wrapped__')) {
return isEqual(a.__wrapped__ || a, b.__wrapped__ || b, callback, thisArg, 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
)) {
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.com/#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) {
length = a.length;
size = b.length;
// compare lengths to determine if a deep comparison is necessary
result = size == a.length;
if (!result && !whereIndicator) {
return result;
}
// deep compare the contents, ignoring non-numeric properties
while (size--) {
var index = length,
value = b[size];
if (whereIndicator) {
while (index--) {
if ((result = isEqual(a[index], value, callback, thisArg, stackA, stackB))) {
break;
}
}
} else if (!(result = isEqual(a[size], value, callback, thisArg, stackA, stackB))) {
break;
}
}
return result;
}
// 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) && isEqual(a[key], value, callback, thisArg, stackA, stackB));
}
});
if (result && !whereIndicator) {
// 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);
}
});
}
if (initedStack) {
releaseArray(stackA);
releaseArray(stackB);
}
return result;
}
/**
* 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.com/#x15.1.2.5.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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 {Mixed} 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 {Mixed} 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.com/#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 values. See http://es5.github.com/#x15.1.2.4.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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 {Mixed} 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.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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' || toString.call(value) == numberClass;
}
/**
* Checks if a given `value` is an object created by the `Object` constructor.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if `value` is a plain object, else `false`.
* @example
*
* function Stooge(name, age) {
* this.name = name;
* this.age = age;
* }
*
* _.isPlainObject(new Stooge('moe', 40));
* // => false
*
* _.isPlainObject([1, 2, 3]);
* // => false
*
* _.isPlainObject({ 'name': 'moe', 'age': 40 });
* // => true
*/
var isPlainObject = function(value) {
if (!(value && toString.call(value) == objectClass)) {
return false;
}
var valueOf = value.valueOf,
objProto = typeof valueOf == 'function' && (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 {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if the `value` is a regular expression, else `false`.
* @example
*
* _.isRegExp(/moe/);
* // => 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 {Mixed} value The value to check.
* @returns {Boolean} Returns `true`, if the `value` is a string, else `false`.
* @example
*
* _.isString('moe');
* // => true
*/
function isString(value) {
return typeof value == 'string' || toString.call(value) == stringClass;
}
/**
* Checks if `value` is `undefined`.
*
* @static
* @memberOf _
* @category Objects
* @param {Mixed} 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';
}
/**
* 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` function
* is passed, it will be executed to produce the merged values of the destination
* and source properties. If `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} [source1, source2, ...] The source objects.
* @param {Function} [callback] The function to customize merging properties.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @param- {Object} [deepIndicator] Indicates that `stackA` and `stackB` are
* arrays of traversed objects, instead of source objects.
* @param- {Array} [stackA=[]] Tracks traversed source objects.
* @param- {Array} [stackB=[]] Associates values with source counterparts.
* @returns {Object} Returns the destination object.
* @example
*
* var names = {
* 'stooges': [
* { 'name': 'moe' },
* { 'name': 'larry' }
* ]
* };
*
* var ages = {
* 'stooges': [
* { 'age': 40 },
* { 'age': 50 }
* ]
* };
*
* _.merge(names, ages);
* // => { 'stooges': [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] }
*
* 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, source, deepIndicator) {
var args = arguments,
index = 0,
length = 2;
if (!isObject(object)) {
return object;
}
if (deepIndicator === indicatorObject) {
var callback = args[3],
stackA = args[4],
stackB = args[5];
} else {
var initedStack = true;
stackA = getArray();
stackB = getArray();
// allows working with `_.reduce` and `_.reduceRight` without
// using their `callback` arguments, `index|key` and `collection`
if (typeof deepIndicator != 'number') {
length = args.length;
}
if (length > 3 && typeof args[length - 2] == 'function') {
callback = lodash.createCallback(args[--length - 1], args[length--], 2);
} else if (length > 2 && typeof args[length - 1] == 'function') {
callback = args[--length];
}
}
while (++index < length) {
(isArray(args[index]) ? forEach : forOwn)(args[index], 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) {
value = merge(value, source, indicatorObject, 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;
});
}
if (initedStack) {
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` function is passed, it will be executed
* for each property in the `object`, omitting the properties `callback`
* returns truthy 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} callback|[prop1, prop2, ...] The properties to omit
* or the function called per iteration.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns an object without the omitted properties.
* @example
*
* _.omit({ 'name': 'moe', 'age': 40 }, 'age');
* // => { 'name': 'moe' }
*
* _.omit({ 'name': 'moe', 'age': 40 }, function(value) {
* return typeof value == 'number';
* });
* // => { 'name': 'moe' }
*/
function omit(object, callback, thisArg) {
var indexOf = getIndexOf(),
isFunc = typeof callback == 'function',
result = {};
if (isFunc) {
callback = lodash.createCallback(callback, thisArg);
} else {
var props = concat.apply(arrayRef, nativeSlice.call(arguments, 1));
}
forIn(object, function(value, key, object) {
if (isFunc
? !callback(value, key, object)
: indexOf(props, key) < 0
) {
result[key] = value;
}
});
return result;
}
/**
* Creates a two dimensional array of the given 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({ 'moe': 30, 'larry': 40 });
* // => [['moe', 30], ['larry', 40]] (order is not guaranteed)
*/
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 `callback` is passed, it will be executed for each property in the
* `object`, picking the properties `callback` returns truthy 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 {Array|Function|String} callback|[prop1, prop2, ...] The function called
* per iteration or properties to pick, either as individual arguments or arrays.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns an object composed of the picked properties.
* @example
*
* _.pick({ 'name': 'moe', '_userid': 'moe1' }, 'name');
* // => { 'name': 'moe' }
*
* _.pick({ 'name': 'moe', '_userid': 'moe1' }, function(value, key) {
* return key.charAt(0) != '_';
* });
* // => { 'name': 'moe' }
*/
function pick(object, callback, thisArg) {
var result = {};
if (typeof callback != 'function') {
var index = -1,
props = concat.apply(arrayRef, nativeSlice.call(arguments, 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);
forIn(object, function(value, key, object) {
if (callback(value, key, object)) {
result[key] = value;
}
});
}
return result;
}
/**
* An alternative to `_.reduce`, this method transforms an `object` to a new
* `accumulator` object which is the result of running each of its elements
* through the `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} collection The collection to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [accumulator] The custom accumulator value.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} 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);
callback = lodash.createCallback(callback, thisArg, 4);
if (accumulator == null) {
if (isArr) {
accumulator = [];
} else {
var ctor = object && object.constructor,
proto = ctor && ctor.prototype;
accumulator = createObject(proto);
}
}
(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 a new array of property values.
* @example
*
* _.values({ 'one': 1, 'two': 2, 'three': 3 });
* // => [1, 2, 3] (order is not guaranteed)
*/
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 {Array|Number|String} [index1, index2, ...] The indexes of
* `collection` to retrieve, either as individual arguments or arrays.
* @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(['moe', 'larry', 'curly'], 0, 2);
* // => ['moe', 'curly']
*/
function at(collection) {
var index = -1,
props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
length = props.length,
result = Array(length);
while(++index < length) {
result[index] = collection[props[index]];
}
return result;
}
/**
* Checks if a given `target` element 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 {Mixed} 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': 'moe', 'age': 40 }, 'moe');
* // => true
*
* _.contains('curly', 'ur');
* // => 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 (length && 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 returned from running each element of the
* `collection` through the given `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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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 }
*/
function countBy(collection, callback, thisArg) {
var result = {};
callback = lodash.createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
key = String(callback(value, key, collection));
(hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
});
return result;
}
/**
* Checks if the `callback` returns a truthy 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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Boolean} Returns `true` if all elements pass the callback check,
* else `false`.
* @example
*
* _.every([true, 1, null, 'yes'], Boolean);
* // => false
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* // using "_.pluck" callback shorthand
* _.every(stooges, 'age');
* // => true
*
* // using "_.where" callback shorthand
* _.every(stooges, { 'age': 50 });
* // => false
*/
function every(collection, callback, thisArg) {
var result = true;
callback = lodash.createCallback(callback, thisArg);
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;
}
/**
* Examines each element in a `collection`, returning an array of all elements
* the `callback` returns truthy for. The `callback` is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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 food = [
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
* { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
* ];
*
* // using "_.pluck" callback shorthand
* _.filter(food, 'organic');
* // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
*
* // using "_.where" callback shorthand
* _.filter(food, { 'type': 'fruit' });
* // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
*/
function filter(collection, callback, thisArg) {
var result = [];
callback = lodash.createCallback(callback, thisArg);
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;
}
/**
* Examines each element in a `collection`, returning the first that the `callback`
* returns truthy for. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index|key, collection).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the found element, else `undefined`.
* @example
*
* _.find([1, 2, 3, 4], function(num) {
* return num % 2 == 0;
* });
* // => 2
*
* var food = [
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
* { 'name': 'banana', 'organic': true, 'type': 'fruit' },
* { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
* ];
*
* // using "_.where" callback shorthand
* _.find(food, { 'type': 'vegetable' });
* // => { 'name': 'beet', 'organic': false, 'type': 'vegetable' }
*
* // using "_.pluck" callback shorthand
* _.find(food, 'organic');
* // => { 'name': 'banana', 'organic': true, 'type': 'fruit' }
*/
function find(collection, callback, thisArg) {
callback = lodash.createCallback(callback, thisArg);
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;
}
}
/**
* Iterates over a `collection`, executing the `callback` for each element in
* the `collection`. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index|key, collection). Callbacks may exit iteration early
* by explicitly returning `false`.
*
* @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 {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array|Object|String} Returns `collection`.
* @example
*
* _([1, 2, 3]).forEach(alert).join(',');
* // => alerts each number and returns '1,2,3'
*
* _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert);
* // => alerts each number value (order is not guaranteed)
*/
function forEach(collection, callback, thisArg) {
var index = -1,
length = collection ? collection.length : 0;
callback = callback && typeof thisArg == 'undefined' ? callback : lodash.createCallback(callback, thisArg);
if (typeof length == 'number') {
while (++index < length) {
if (callback(collection[index], index, collection) === false) {
break;
}
}
} else {
forOwn(collection, callback);
}
return collection;
}
/**
* Creates an object composed of keys returned from running each element of the
* `collection` through the `callback`. The corresponding value of each key is
* an array of elements passed to `callback` that returned the key. The `callback`
* is bound to `thisArg` and invoked with three arguments; (value, index|key, collection).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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'] }
*/
function groupBy(collection, callback, thisArg) {
var result = {};
callback = lodash.createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
key = String(callback(value, key, collection));
(hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
});
return result;
}
/**
* 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 passed 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 {Mixed} [arg1, arg2, ...] 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 = nativeSlice.call(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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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] (order is not guaranteed)
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* // using "_.pluck" callback shorthand
* _.map(stooges, 'name');
* // => ['moe', 'larry']
*/
function map(collection, callback, thisArg) {
var index = -1,
length = collection ? collection.length : 0;
callback = lodash.createCallback(callback, thisArg);
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 an `array`. If `callback` is passed,
* it will be executed for each value in the `array` 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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the maximum value.
* @example
*
* _.max([4, 2, 8, 6]);
* // => 8
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* _.max(stooges, function(stooge) { return stooge.age; });
* // => { 'name': 'larry', 'age': 50 };
*
* // using "_.pluck" callback shorthand
* _.max(stooges, 'age');
* // => { 'name': 'larry', 'age': 50 };
*/
function max(collection, callback, thisArg) {
var computed = -Infinity,
result = computed;
if (!callback && isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
if (value > result) {
result = value;
}
}
} else {
callback = (!callback && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg);
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 an `array`. If `callback` is passed,
* it will be executed for each value in the `array` 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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the minimum value.
* @example
*
* _.min([4, 2, 8, 6]);
* // => 2
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* _.min(stooges, function(stooge) { return stooge.age; });
* // => { 'name': 'moe', 'age': 40 };
*
* // using "_.pluck" callback shorthand
* _.min(stooges, 'age');
* // => { 'name': 'moe', 'age': 40 };
*/
function min(collection, callback, thisArg) {
var computed = Infinity,
result = computed;
if (!callback && isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
if (value < result) {
result = value;
}
}
} else {
callback = (!callback && isString(collection))
? charAtCallback
: lodash.createCallback(callback, thisArg);
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 property to pluck.
* @returns {Array} Returns a new array of property values.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* _.pluck(stooges, 'name');
* // => ['moe', 'larry']
*/
function pluck(collection, property) {
var index = -1,
length = collection ? collection.length : 0;
if (typeof length == 'number') {
var result = Array(length);
while (++index < length) {
result[index] = collection[index][property];
}
}
return result || map(collection, property);
}
/**
* 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 passed, 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 {Mixed} [accumulator] Initial value of the accumulator.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} 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 similar to `_.reduce`, except that it iterates over 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 {Mixed} [accumulator] Initial value of the accumulator.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} 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 iterable = collection,
length = collection ? collection.length : 0,
noaccum = arguments.length < 3;
if (typeof length != 'number') {
var props = keys(collection);
length = props.length;
}
callback = lodash.createCallback(callback, thisArg, 4);
forEach(collection, function(value, index, collection) {
index = props ? props[--length] : --length;
accumulator = noaccum
? (noaccum = false, iterable[index])
: callback(accumulator, iterable[index], index, collection);
});
return accumulator;
}
/**
* The opposite of `_.filter`, this method returns the elements of a
* `collection` that `callback` does **not** return truthy for.
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns a new array of elements that did **not** pass the
* callback check.
* @example
*
* var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
* // => [1, 3, 5]
*
* var food = [
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
* { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
* ];
*
* // using "_.pluck" callback shorthand
* _.reject(food, 'organic');
* // => [{ 'name': 'apple', 'organic': false, 'type': 'fruit' }]
*
* // using "_.where" callback shorthand
* _.reject(food, { 'type': 'fruit' });
* // => [{ 'name': 'carrot', 'organic': true, 'type': 'vegetable' }]
*/
function reject(collection, callback, thisArg) {
callback = lodash.createCallback(callback, thisArg);
return filter(collection, function(value, index, collection) {
return !callback(value, index, collection);
});
}
/**
* Creates an array of shuffled `array` 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 = floor(nativeRandom() * (++index + 1));
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('curly');
* // => 5
*/
function size(collection) {
var length = collection ? collection.length : 0;
return typeof length == 'number' ? length : keys(collection).length;
}
/**
* Checks if the `callback` returns a truthy value for **any** element of a
* `collection`. The function returns as soon as it finds 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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Boolean} Returns `true` if any element passes the callback check,
* else `false`.
* @example
*
* _.some([null, 0, 'yes', false], Boolean);
* // => true
*
* var food = [
* { 'name': 'apple', 'organic': false, 'type': 'fruit' },
* { 'name': 'carrot', 'organic': true, 'type': 'vegetable' }
* ];
*
* // using "_.pluck" callback shorthand
* _.some(food, 'organic');
* // => true
*
* // using "_.where" callback shorthand
* _.some(food, { 'type': 'meat' });
* // => false
*/
function some(collection, callback, thisArg) {
var result;
callback = lodash.createCallback(callback, thisArg);
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 the `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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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]
*
* // using "_.pluck" callback shorthand
* _.sortBy(['banana', 'strawberry', 'apple'], 'length');
* // => ['apple', 'banana', 'strawberry']
*/
function sortBy(collection, callback, thisArg) {
var index = -1,
length = collection ? collection.length : 0,
result = Array(typeof length == 'number' ? length : 0);
callback = lodash.createCallback(callback, thisArg);
forEach(collection, function(value, key, collection) {
var object = result[++index] = getObject();
object.criteria = 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;
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);
}
/**
* Examines each element in a `collection`, returning an array of all elements
* that have the given `properties`. When checking `properties`, this method
* performs a deep comparison between values to determine if they are equivalent
* to each other.
*
* @static
* @memberOf _
* @type Function
* @category Collections
* @param {Array|Object|String} collection The collection to iterate over.
* @param {Object} properties The object of property values to filter by.
* @returns {Array} Returns a new array of elements that have the given `properties`.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* _.where(stooges, { 'age': 40 });
* // => [{ 'name': 'moe', 'age': 40 }]
*/
var where = filter;
/*--------------------------------------------------------------------------*/
/**
* Creates an array with all falsey values of `array` 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 filtered array.
* @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 of `array` elements not present in the other arrays
* using strict equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to process.
* @param {Array} [array1, array2, ...] Arrays to check.
* @returns {Array} Returns a new array of `array` elements not present in the
* other arrays.
* @example
*
* _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
* // => [1, 3, 4]
*/
function difference(array) {
var index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0,
seen = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
result = [];
var isLarge = length >= largeArraySize && indexOf === basicIndexOf;
if (isLarge) {
var cache = createCache(seen);
if (cache) {
indexOf = cacheIndexOf;
seen = cache;
} else {
isLarge = false;
}
}
while (++index < length) {
var value = array[index];
if (indexOf(seen, value) < 0) {
result.push(value);
}
}
if (isLarge) {
releaseObject(seen);
}
return result;
}
/**
* This method is similar to `_.find`, except that it returns the index of
* the element that passes the callback check, instead of the element itself.
*
* @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 passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} Returns the index of the found element, else `-1`.
* @example
*
* _.findIndex(['apple', 'banana', 'beet'], function(food) {
* return /^b/.test(food);
* });
* // => 1
*/
function findIndex(array, callback, thisArg) {
var index = -1,
length = array ? array.length : 0;
callback = lodash.createCallback(callback, thisArg);
while (++index < length) {
if (callback(array[index], index, array)) {
return index;
}
}
return -1;
}
/**
* Gets the first element of the `array`. If a number `n` is passed, the first
* `n` elements of the `array` are returned. If a `callback` function is passed,
* elements at the beginning of the array are returned as long as the `callback`
* returns truthy. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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|n] The function called
* per element or the number of elements to return. If a property name or
* object is passed, it will be used to create a "_.pluck" or "_.where"
* style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} 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 food = [
* { 'name': 'banana', 'organic': true },
* { 'name': 'beet', 'organic': false },
* ];
*
* // using "_.pluck" callback shorthand
* _.first(food, 'organic');
* // => [{ 'name': 'banana', 'organic': true }]
*
* var food = [
* { 'name': 'apple', 'type': 'fruit' },
* { 'name': 'banana', 'type': 'fruit' },
* { 'name': 'beet', 'type': 'vegetable' }
* ];
*
* // using "_.where" callback shorthand
* _.first(food, { 'type': 'fruit' });
* // => [{ 'name': 'apple', 'type': 'fruit' }, { 'name': 'banana', 'type': 'fruit' }]
*/
function first(array, callback, thisArg) {
if (array) {
var n = 0,
length = array.length;
if (typeof callback != 'number' && callback != null) {
var index = -1;
callback = lodash.createCallback(callback, thisArg);
while (++index < length && callback(array[index], index, array)) {
n++;
}
} else {
n = callback;
if (n == null || thisArg) {
return array[0];
}
}
return slice(array, 0, nativeMin(nativeMax(0, n), length));
}
}
/**
* Flattens a nested array (the nesting can be to any depth). If `isShallow`
* is truthy, `array` will only be flattened a single level. If `callback`
* is passed, each element of `array` is passed through a `callback` before
* flattening. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 indicate only flattening a single level.
* @param {Function|Object|String} [callback=identity] The function called per
* iteration. If a property name or object is passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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 stooges = [
* { 'name': 'curly', 'quotes': ['Oh, a wise guy, eh?', 'Poifect!'] },
* { 'name': 'moe', 'quotes': ['Spread out!', 'You knucklehead!'] }
* ];
*
* // using "_.pluck" callback shorthand
* _.flatten(stooges, 'quotes');
* // => ['Oh, a wise guy, eh?', 'Poifect!', 'Spread out!', 'You knucklehead!']
*/
var flatten = overloadWrapper(function flatten(array, isShallow, callback) {
var index = -1,
length = array ? array.length : 0,
result = [];
while (++index < length) {
var value = array[index];
if (callback) {
value = callback(value, index, array);
}
// recursively flatten arrays (susceptible to call stack limits)
if (isArray(value)) {
push.apply(result, isShallow ? value : flatten(value));
} else {
result.push(value);
}
}
return result;
});
/**
* 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, passing `true` for `fromIndex` will run a faster binary search.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {Mixed} 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 array ? basicIndexOf(array, value, fromIndex) : -1;
}
/**
* Gets all but the last element of `array`. If a number `n` is passed, the
* last `n` elements are excluded from the result. If a `callback` function
* is passed, elements at the end of the array are excluded from the result
* as long as the `callback` returns truthy. The `callback` is bound to
* `thisArg` and invoked with three arguments; (value, index, array).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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|n=1] The function called
* per element or the number of elements to exclude. If a property name or
* object is passed, it will be used to create a "_.pluck" or "_.where"
* style callback, respectively.
* @param {Mixed} [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 food = [
* { 'name': 'beet', 'organic': false },
* { 'name': 'carrot', 'organic': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.initial(food, 'organic');
* // => [{ 'name': 'beet', 'organic': false }]
*
* var food = [
* { 'name': 'banana', 'type': 'fruit' },
* { 'name': 'beet', 'type': 'vegetable' },
* { 'name': 'carrot', 'type': 'vegetable' }
* ];
*
* // using "_.where" callback shorthand
* _.initial(food, { 'type': 'vegetable' });
* // => [{ 'name': 'banana', 'type': 'fruit' }]
*/
function initial(array, callback, thisArg) {
if (!array) {
return [];
}
var n = 0,
length = array.length;
if (typeof callback != 'number' && callback != null) {
var index = length;
callback = lodash.createCallback(callback, thisArg);
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));
}
/**
* Computes the intersection of all the passed-in arrays using strict equality
* for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of unique elements that are present
* in **all** of the arrays.
* @example
*
* _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
* // => [1, 2]
*/
function intersection(array) {
var args = arguments,
argsLength = args.length,
argsIndex = -1,
caches = getArray(),
index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0,
result = [],
seen = getArray();
while (++argsIndex < argsLength) {
var value = args[argsIndex];
caches[argsIndex] = indexOf === basicIndexOf &&
(value ? value.length : 0) >= largeArraySize &&
createCache(argsIndex ? args[argsIndex] : seen);
}
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 of the `array`. If a number `n` is passed, the
* last `n` elements of the `array` are returned. If a `callback` function
* is passed, elements at the end of the array are returned as long as the
* `callback` returns truthy. The `callback` is bound to `thisArg` and
* invoked with three arguments;(value, index, array).
*
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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|n] The function called
* per element or the number of elements to return. If a property name or
* object is passed, it will be used to create a "_.pluck" or "_.where"
* style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Mixed} 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 food = [
* { 'name': 'beet', 'organic': false },
* { 'name': 'carrot', 'organic': true }
* ];
*
* // using "_.pluck" callback shorthand
* _.last(food, 'organic');
* // => [{ 'name': 'carrot', 'organic': true }]
*
* var food = [
* { 'name': 'banana', 'type': 'fruit' },
* { 'name': 'beet', 'type': 'vegetable' },
* { 'name': 'carrot', 'type': 'vegetable' }
* ];
*
* // using "_.where" callback shorthand
* _.last(food, { 'type': 'vegetable' });
* // => [{ 'name': 'beet', 'type': 'vegetable' }, { 'name': 'carrot', 'type': 'vegetable' }]
*/
function last(array, callback, thisArg) {
if (array) {
var n = 0,
length = array.length;
if (typeof callback != 'number' && callback != null) {
var index = length;
callback = lodash.createCallback(callback, thisArg);
while (index-- && callback(array[index], index, array)) {
n++;
}
} else {
n = callback;
if (n == null || thisArg) {
return array[length - 1];
}
}
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.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
* @param {Mixed} 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;
}
/**
* Creates an array of numbers (positive and/or negative) progressing from
* `start` up to but not including `end`.
*
* @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(10);
* // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*
* _.range(1, 11);
* // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
*
* _.range(0, 30, 5);
* // => [0, 5, 10, 15, 20, 25]
*
* _.range(0, -10, -1);
* // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
*
* _.range(0);
* // => []
*/
function range(start, end, step) {
start = +start || 0;
step = +step || 1;
if (end == null) {
end = start;
start = 0;
}
// use `Array(length)` so V8 will avoid the slower "dictionary" mode
// http://youtu.be/XAqIpGU8ZZk#t=17m25s
var index = -1,
length = nativeMax(0, ceil((end - start) / step)),
result = Array(length);
while (++index < length) {
result[index] = start;
start += step;
}
return result;
}
/**
* The opposite of `_.initial`, this method gets all but the first value of
* `array`. If a number `n` is passed, the first `n` values are excluded from
* the result. If a `callback` function is passed, elements at the beginning
* of the array are excluded from the result as long as the `callback` returns
* truthy. The `callback` is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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|n=1] The function called
* per element or the number of elements to exclude. If a property name or
* object is passed, it will be used to create a "_.pluck" or "_.where"
* style callback, respectively.
* @param {Mixed} [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 food = [
* { 'name': 'banana', 'organic': true },
* { 'name': 'beet', 'organic': false },
* ];
*
* // using "_.pluck" callback shorthand
* _.rest(food, 'organic');
* // => [{ 'name': 'beet', 'organic': false }]
*
* var food = [
* { 'name': 'apple', 'type': 'fruit' },
* { 'name': 'banana', 'type': 'fruit' },
* { 'name': 'beet', 'type': 'vegetable' }
* ];
*
* // using "_.where" callback shorthand
* _.rest(food, { 'type': 'fruit' });
* // => [{ 'name': 'beet', 'type': 'vegetable' }]
*/
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);
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 the `value`
* should be inserted into `array` in order to maintain the sort order of the
* sorted `array`. If `callback` is passed, it will be executed for `value` and
* each element in `array` to compute their sort ranking. The `callback` is
* bound to `thisArg` and invoked with one argument; (value).
*
* If a property name is passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 {Mixed} value The value to evaluate.
* @param {Function|Object|String} [callback=identity] The function called per
* iteration. If a property name or object is passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Number} Returns the index at which the 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;
}
/**
* Computes the union of the passed-in arrays using strict equality for
* comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of unique values, in order, that are
* present in one or more of the arrays.
* @example
*
* _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
* // => [1, 2, 3, 101, 10]
*/
function union(array) {
if (!isArray(array)) {
arguments[0] = array ? nativeSlice.call(array) : arrayRef;
}
return uniq(concat.apply(arrayRef, arguments));
}
/**
* Creates a duplicate-value-free version of the `array` using strict equality
* for comparisons, i.e. `===`. If the `array` is already sorted, passing `true`
* for `isSorted` will run a faster algorithm. If `callback` is passed, 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 passed for `callback`, the created "_.pluck" style
* callback will return the property value of the given element.
*
* If an object is passed 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 the `array` is already sorted.
* @param {Function|Object|String} [callback=identity] The function called per
* iteration. If a property name or object is passed, it will be used to create
* a "_.pluck" or "_.where" style callback, respectively.
* @param {Mixed} [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 }]
*/
var uniq = overloadWrapper(function(array, isSorted, callback) {
var index = -1,
indexOf = getIndexOf(),
length = array ? array.length : 0,
result = [];
var isLarge = !isSorted && length >= largeArraySize && indexOf === basicIndexOf,
seen = (callback || isLarge) ? getArray() : result;
if (isLarge) {
var cache = createCache(seen);
if (cache) {
indexOf = cacheIndexOf;
seen = cache;
} else {
isLarge = false;
seen = callback ? seen : (releaseArray(seen), result);
}
}
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;
});
/**
* The inverse of `_.zip`, this method splits groups of elements into arrays
* composed of elements from each group at their corresponding indexes.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to process.
* @returns {Array} Returns a new array of the composed arrays.
* @example
*
* _.unzip([['moe', 30, true], ['larry', 40, false]]);
* // => [['moe', 'larry'], [30, 40], [true, false]];
*/
function unzip(array) {
var 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 array with all occurrences of the passed values removed using
* strict equality for comparisons, i.e. `===`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to filter.
* @param {Mixed} [value1, value2, ...] Values to remove.
* @returns {Array} Returns a new filtered array.
* @example
*
* _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
* // => [2, 3, 4]
*/
function without(array) {
return difference(array, nativeSlice.call(arguments, 1));
}
/**
* Groups the elements of each array at their corresponding indexes. Useful for
* separate data sources that are coordinated through matching array indexes.
* For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix
* in a similar fashion.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} [array1, array2, ...] Arrays to process.
* @returns {Array} Returns a new array of grouped elements.
* @example
*
* _.zip(['moe', 'larry'], [30, 40], [true, false]);
* // => [['moe', 30, true], ['larry', 40, false]]
*/
function zip(array) {
return array ? unzip(arguments) : [];
}
/**
* Creates an object composed from arrays of `keys` and `values`. Pass 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(['moe', 'larry'], [30, 40]);
* // => { 'moe': 30, 'larry': 40 }
*/
function zipObject(keys, values) {
var index = -1,
length = keys ? keys.length : 0,
result = {};
while (++index < length) {
var key = keys[index];
if (values) {
result[key] = values[index];
} else {
result[key[0]] = key[1];
}
}
return result;
}
/*--------------------------------------------------------------------------*/
/**
* If `n` is greater than `0`, a function is created that is restricted to
* executing `func`, with the `this` binding and arguments of the created
* function, only after it is called `n` times. If `n` is less than `1`,
* `func` is executed immediately, without a `this` binding or additional
* arguments, and its result is returned.
*
* @static
* @memberOf _
* @category Functions
* @param {Number} n The number of times the function must be called before
* it is executed.
* @param {Function} func The function to restrict.
* @returns {Function} Returns the new restricted function.
* @example
*
* var renderNotes = _.after(notes.length, render);
* _.forEach(notes, function(note) {
* note.asyncSave({ 'success': renderNotes });
* });
* // `renderNotes` is run once, after all notes have saved
*/
function after(n, func) {
if (n < 1) {
return func();
}
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
* passed to the bound function.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to bind.
* @param {Mixed} [thisArg] The `this` binding of `func`.
* @param {Mixed} [arg1, arg2, ...] 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': 'moe' }, 'hi');
* func();
* // => 'hi moe'
*/
function bind(func, thisArg) {
// use `Function#bind` if it exists and is fast
// (in V8 `Function#bind` is slower except when partially applied)
return support.fastBind || (nativeBind && arguments.length > 2)
? nativeBind.call.apply(nativeBind, arguments)
: createBound(func, thisArg, nativeSlice.call(arguments, 2));
}
/**
* Binds methods on `object` to `object`, 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} [methodName1, methodName2, ...] Method names on the object to bind.
* @returns {Object} Returns `object`.
* @example
*
* var view = {
* 'label': 'docs',
* 'onClick': function() { alert('clicked ' + this.label); }
* };
*
* _.bindAll(view);
* jQuery('#docs').on('click', view.onClick);
* // => alerts 'clicked docs', when the button is clicked
*/
function bindAll(object) {
var funcs = arguments.length > 1 ? concat.apply(arrayRef, nativeSlice.call(arguments, 1)) : functions(object),
index = -1,
length = funcs.length;
while (++index < length) {
var key = funcs[index];
object[key] = bind(object[key], object);
}
return object;
}
/**
* Creates a function that, when called, invokes the method at `object[key]`
* and prepends any additional `bindKey` arguments to those passed 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 {Mixed} [arg1, arg2, ...] Arguments to be partially applied.
* @returns {Function} Returns the new bound function.
* @example
*
* var object = {
* 'name': 'moe',
* 'greet': function(greeting) {
* return greeting + ' ' + this.name;
* }
* };
*
* var func = _.bindKey(object, 'greet', 'hi');
* func();
* // => 'hi moe'
*
* object.greet = function(greeting) {
* return greeting + ', ' + this.name + '!';
* };
*
* func();
* // => 'hi, moe!'
*/
function bindKey(object, key) {
return createBound(object, key, nativeSlice.call(arguments, 2), indicatorObject);
}
/**
* Creates a function that is the composition of the passed 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} [func1, func2, ...] Functions to compose.
* @returns {Function} Returns the new composed function.
* @example
*
* var greet = function(name) { return 'hi ' + name; };
* var exclaim = function(statement) { return statement + '!'; };
* var welcome = _.compose(exclaim, greet);
* welcome('moe');
* // => 'hi moe!'
*/
function compose() {
var funcs = arguments;
return function() {
var args = arguments,
length = funcs.length;
while (length--) {
args = [funcs[length].apply(this, args)];
}
return args[0];
};
}
/**
* 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`.
*
* Note: All Lo-Dash methods, that accept a `callback` argument, use `_.createCallback`.
*
* @static
* @memberOf _
* @category Functions
* @param {Mixed} [func=identity] The value to convert to a callback.
* @param {Mixed} [thisArg] The `this` binding of the created callback.
* @param {Number} [argCount=3] The number of arguments the callback accepts.
* @returns {Function} Returns a callback function.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 }
* ];
*
* // 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(stooges, 'age__gt45');
* // => [{ 'name': 'larry', 'age': 50 }]
*
* // create mixins with support for "_.pluck" and "_.where" callback shorthands
* _.mixin({
* 'toLookup': function(collection, callback, thisArg) {
* callback = _.createCallback(callback, thisArg);
* return _.reduce(collection, function(result, value, index, collection) {
* return (result[callback(value, index, collection)] = value, result);
* }, {});
* }
* });
*
* _.toLookup(stooges, 'name');
* // => { 'moe': { 'name': 'moe', 'age': 40 }, 'larry': { 'name': 'larry', 'age': 50 } }
*/
function createCallback(func, thisArg, argCount) {
if (func == null) {
return identity;
}
var type = typeof func;
if (type != 'function') {
if (type != 'object') {
return function(object) {
return object[func];
};
}
var props = keys(func);
return function(object) {
var length = props.length,
result = false;
while (length--) {
if (!(result = isEqual(object[props[length]], func[props[length]], indicatorObject))) {
break;
}
}
return result;
};
}
if (typeof thisArg == 'undefined' || (reThis && !reThis.test(fnToString.call(func)))) {
return func;
}
if (argCount === 1) {
return function(value) {
return func.call(thisArg, value);
};
}
if (argCount === 2) {
return function(a, b) {
return func.call(thisArg, a, b);
};
}
if (argCount === 4) {
return function(accumulator, value, index, collection) {
return func.call(thisArg, accumulator, value, index, collection);
};
}
return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
};
}
/**
* Creates a function that will delay the execution of `func` until after
* `wait` milliseconds have elapsed since the last time it was invoked. Pass
* 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.
* [leading=false] A boolean to specify execution on the leading edge of the timeout.
* [maxWait] The maximum time `func` is allowed to be delayed before it's called.
* [trailing=true] A boolean to specify execution on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* var lazyLayout = _.debounce(calculateLayout, 300);
* jQuery(window).on('resize', lazyLayout);
*
* jQuery('#postbox').on('click', _.debounce(sendMail, 200, {
* 'leading': true,
* 'trailing': false
* });
*/
function debounce(func, wait, options) {
var args,
result,
thisArg,
callCount = 0,
lastCalled = 0,
maxWait = false,
maxTimeoutId = null,
timeoutId = null,
trailing = true;
function clear() {
clearTimeout(maxTimeoutId);
clearTimeout(timeoutId);
callCount = 0;
maxTimeoutId = timeoutId = null;
}
function delayed() {
var isCalled = trailing && (!leading || callCount > 1);
clear();
if (isCalled) {
if (maxWait !== false) {
lastCalled = new Date;
}
result = func.apply(thisArg, args);
}
}
function maxDelayed() {
clear();
if (trailing || (maxWait !== wait)) {
lastCalled = new Date;
result = func.apply(thisArg, args);
}
}
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;
}
return function() {
args = arguments;
thisArg = this;
callCount++;
// avoid issues with Titanium and `undefined` timeout ids
// https://github.com/appcelerator/titanium_mobile/blob/3_1_0_GA/android/titanium/src/java/ti/modules/titanium/TitaniumModule.java#L185-L192
clearTimeout(timeoutId);
if (maxWait === false) {
if (leading && callCount < 2) {
result = func.apply(thisArg, args);
}
} else {
var now = new Date;
if (!maxTimeoutId && !leading) {
lastCalled = now;
}
var remaining = maxWait - (now - lastCalled);
if (remaining <= 0) {
clearTimeout(maxTimeoutId);
maxTimeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
}
else if (!maxTimeoutId) {
maxTimeoutId = setTimeout(maxDelayed, remaining);
}
}
if (wait !== maxWait) {
timeoutId = setTimeout(delayed, wait);
}
return result;
};
}
/**
* Defers executing the `func` function until the current call stack has cleared.
* Additional arguments will be passed to `func` when it is invoked.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to defer.
* @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
* @returns {Number} Returns the timer id.
* @example
*
* _.defer(function() { alert('deferred'); });
* // returns from the function before `alert` is called
*/
function defer(func) {
var args = nativeSlice.call(arguments, 1);
return setTimeout(function() { func.apply(undefined, args); }, 1);
}
// use `setImmediate` if it's available in Node.js
if (isV8 && freeModule && typeof setImmediate == 'function') {
defer = bind(setImmediate, context);
}
/**
* Executes the `func` function after `wait` milliseconds. Additional arguments
* will be passed 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 {Mixed} [arg1, arg2, ...] Arguments to invoke the function with.
* @returns {Number} Returns the timer id.
* @example
*
* var log = _.bind(console.log, console);
* _.delay(log, 1000, 'logged later');
* // => 'logged later' (Appears after one second.)
*/
function delay(func, wait) {
var args = nativeSlice.call(arguments, 2);
return setTimeout(function() { func.apply(undefined, args); }, wait);
}
/**
* Creates a function that memoizes the result of `func`. If `resolver` is
* passed, it will be used to determine the cache key for storing the result
* based on the arguments passed to the memoized function. By default, the first
* argument passed 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);
* });
*/
function memoize(func, resolver) {
function memoized() {
var cache = memoized.cache,
key = keyPrefix + (resolver ? resolver.apply(this, arguments) : 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;
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 passed 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 {Mixed} [arg1, arg2, ...] 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('moe');
* // => 'hi moe'
*/
function partial(func) {
return createBound(func, nativeSlice.call(arguments, 1));
}
/**
* This method is similar to `_.partial`, except that `partial` arguments are
* appended to those passed to the new function.
*
* @static
* @memberOf _
* @category Functions
* @param {Function} func The function to partially apply arguments to.
* @param {Mixed} [arg1, arg2, ...] 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 createBound(func, nativeSlice.call(arguments, 1), null, indicatorObject);
}
/**
* Creates a function that, when executed, will only call the `func` function
* at most once per every `wait` milliseconds. Pass 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.
* [leading=true] A boolean to specify execution on the leading edge of the timeout.
* [trailing=true] A boolean to specify execution on the trailing edge of the timeout.
* @returns {Function} Returns the new throttled function.
* @example
*
* var throttled = _.throttle(updatePosition, 100);
* jQuery(window).on('scroll', throttled);
*
* jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
* 'trailing': false
* }));
*/
function throttle(func, wait, options) {
var leading = true,
trailing = true;
if (options === false) {
leading = false;
} else if (isObject(options)) {
leading = 'leading' in options ? options.leading : leading;
trailing = 'trailing' in options ? options.trailing : trailing;
}
options = getObject();
options.leading = leading;
options.maxWait = wait;
options.trailing = trailing;
var result = debounce(func, wait, options);
releaseObject(options);
return result;
}
/**
* Creates a function that passes `value` to the `wrapper` function as its
* first argument. Additional arguments passed to the function are appended
* to those passed to the `wrapper` function. The `wrapper` is executed with
* the `this` binding of the created function.
*
* @static
* @memberOf _
* @category Functions
* @param {Mixed} value The value to wrap.
* @param {Function} wrapper The wrapper function.
* @returns {Function} Returns the new function.
* @example
*
* var hello = function(name) { return 'hello ' + name; };
* hello = _.wrap(hello, function(func) {
* return 'before, ' + func('moe') + ', after';
* });
* hello();
* // => 'before, hello moe, after'
*/
function wrap(value, wrapper) {
return function() {
var args = [value];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
}
/*--------------------------------------------------------------------------*/
/**
* 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('Moe, Larry & Curly');
* // => 'Moe, Larry & Curly'
*/
function escape(string) {
return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
}
/**
* This method returns the first argument passed to it.
*
* @static
* @memberOf _
* @category Utilities
* @param {Mixed} value Any value.
* @returns {Mixed} Returns `value`.
* @example
*
* var moe = { 'name': 'moe' };
* moe === _.identity(moe);
* // => true
*/
function identity(value) {
return value;
}
/**
* Adds functions properties of `object` to the `lodash` function and chainable
* wrapper.
*
* @static
* @memberOf _
* @category Utilities
* @param {Object} object The object of function properties to add to `lodash`.
* @example
*
* _.mixin({
* 'capitalize': function(string) {
* return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
* }
* });
*
* _.capitalize('moe');
* // => 'Moe'
*
* _('moe').capitalize();
* // => 'Moe'
*/
function mixin(object) {
forEach(functions(object), function(methodName) {
var func = lodash[methodName] = object[methodName];
lodash.prototype[methodName] = function() {
var value = this.__wrapped__,
args = [value];
push.apply(args, arguments);
var result = func.apply(lodash, args);
return (value && typeof value == 'object' && value === result)
? this
: new lodashWrapper(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;
}
/**
* 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.com/#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 and Opera still follow the ES3 specified implementation of `parseInt`
return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
};
/**
* Produces a random number between `min` and `max` (inclusive). If only one
* argument is passed, a number between `0` and the given number will be returned.
*
* @static
* @memberOf _
* @category Utilities
* @param {Number} [min=0] The minimum possible value.
* @param {Number} [max=1] The maximum possible value.
* @returns {Number} Returns a random number.
* @example
*
* _.random(0, 5);
* // => a number between 0 and 5
*
* _.random(5);
* // => also a number between 0 and 5
*/
function random(min, max) {
if (min == null && max == null) {
max = 1;
}
min = +min || 0;
if (max == null) {
max = min;
min = 0;
} else {
max = +max || 0;
}
var rand = nativeRandom();
return (min % 1 || max % 1)
? min + nativeMin(rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1))), max)
: min + floor(rand * (max - min + 1));
}
/**
* Resolves the value of `property` on `object`. If `property` 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} property The property to get the value of.
* @returns {Mixed} Returns the resolved value.
* @example
*
* var object = {
* 'cheese': 'crumpets',
* 'stuff': function() {
* return 'nonsense';
* }
* };
*
* _.result(object, 'cheese');
* // => 'crumpets'
*
* _.result(object, 'stuff');
* // => 'nonsense'
*/
function result(object, property) {
var value = object ? object[property] : undefined;
return isFunction(value) ? object[property]() : 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.
* escape - The "escape" delimiter regexp.
* evaluate - The "evaluate" delimiter regexp.
* interpolate - The "interpolate" delimiter regexp.
* sourceURL - The sourceURL of the template's compiled source.
* 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 a compiled template
* var compiled = _.template('hello <%= name %>');
* compiled({ 'name': 'moe' });
* // => 'hello moe'
*
* var list = '<% _.forEach(people, function(name) { %><li><%= name %></li><% }); %>';
* _.template(list, { 'people': ['moe', 'larry'] });
* // => '<li>moe</li><li>larry</li>'
*
* // using the "escape" delimiter to escape HTML in data property values
* _.template('<b><%- value %></b>', { 'value': '<script>' });
* // => '<b><script></b>'
*
* // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
* _.template('hello ${ name }', { 'name': 'curly' });
* // => 'hello curly'
*
* // using the internal `print` function in "evaluate" delimiters
* _.template('<% print("hello " + epithet); %>!', { 'epithet': 'stooge' });
* // => 'hello stooge!'
*
* // using custom template delimiters
* _.templateSettings = {
* 'interpolate': /{{([\s\S]+?)}}/g
* };
*
* _.template('hello {{ name }}!', { 'name': 'mustache' });
* // => 'hello mustache!'
*
* // 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 || (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 and wrap in a multi-line comment to
// avoid issues with Narwhal, IE conditional compilation, and the JS engine
// embedded in Adobe products.
// 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 via 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` function `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 {Mixed} [thisArg] The `this` binding of `callback`.
* @returns {Array} Returns a new 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 = lodash.createCallback(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('Moe, Larry & Curly');
* // => 'Moe, Larry & Curly'
*/
function unescape(string) {
return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
}
/**
* Generates a unique ID. If `prefix` is passed, 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;
}
/*--------------------------------------------------------------------------*/
/**
* 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 {Mixed} value The value to pass to `interceptor`.
* @param {Function} interceptor The function to invoke.
* @returns {Mixed} Returns `value`.
* @example
*
* _([1, 2, 3, 4])
* .filter(function(num) { return num % 2 == 0; })
* .tap(alert)
* .map(function(num) { return num * num; })
* .value();
* // => // [2, 4] (alerted)
* // => [4, 16]
*/
function tap(value, interceptor) {
interceptor(value);
return value;
}
/**
* 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 {Mixed} 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.compact = compact;
lodash.compose = compose;
lodash.countBy = countBy;
lodash.createCallback = createCallback;
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.forIn = forIn;
lodash.forOwn = forOwn;
lodash.functions = functions;
lodash.groupBy = groupBy;
lodash.initial = initial;
lodash.intersection = intersection;
lodash.invert = invert;
lodash.invoke = invoke;
lodash.keys = keys;
lodash.map = map;
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.range = range;
lodash.reject = reject;
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.unzip = unzip;
lodash.values = values;
lodash.where = where;
lodash.without = without;
lodash.wrap = wrap;
lodash.zip = zip;
lodash.zipObject = zipObject;
// add aliases
lodash.collect = map;
lodash.drop = rest;
lodash.each = forEach;
lodash.extend = assign;
lodash.methods = functions;
lodash.object = zipObject;
lodash.select = filter;
lodash.tail = rest;
lodash.unique = uniq;
// add functions to `lodash.prototype`
mixin(lodash);
// add Underscore compat
lodash.chain = lodash;
lodash.prototype.chain = function() { return this; };
/*--------------------------------------------------------------------------*/
// 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.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.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;
forOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
lodash.prototype[methodName] = function() {
var args = [this.__wrapped__];
push.apply(args, arguments);
return func.apply(lodash, args);
};
}
});
/*--------------------------------------------------------------------------*/
// add functions capable of returning wrapped and unwrapped values when chaining
lodash.first = first;
lodash.last = last;
// add aliases
lodash.take = first;
lodash.head = first;
forOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
lodash.prototype[methodName]= function(callback, thisArg) {
var result = func(this.__wrapped__, callback, thisArg);
return callback == null || (thisArg && typeof callback != 'function')
? result
: new lodashWrapper(result);
};
}
});
/*--------------------------------------------------------------------------*/
/**
* The semantic version number.
*
* @static
* @memberOf _
* @type String
*/
lodash.VERSION = '1.3.1';
// add "Chaining" functions to the wrapper
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() {
return func.apply(this.__wrapped__, arguments);
};
});
// add `Array` functions that return the 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));
};
});
return lodash;
}
/*--------------------------------------------------------------------------*/
// expose Lo-Dash
var _ = $._ = runInContext();
// expose Lo-Dash methods to jQuery
$.each(_, function(prop, func){
if (!Object.hasOwnProperty.call(_, prop) || /VERSION/.test(name)) return;
if (!$[prop]) $[prop] = func;
});
});
FD31.plugin("bootstrap", function($) {
/* ===================================================
* bootstrap-transition.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#transitions
* ===================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* CSS TRANSITION SUPPORT (http://www.modernizr.com/)
* ======================================================= */
$(function () {
$.support.transition = (function () {
var transitionEnd = (function () {
var el = document.createElement('bootstrap')
, transEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd'
, 'MozTransition' : 'transitionend'
, 'OTransition' : 'oTransitionEnd otransitionend'
, 'transition' : 'transitionend'
}
, name
for (name in transEndEventNames){
if (el.style[name] !== undefined) {
return transEndEventNames[name]
}
}
}())
return transitionEnd && {
end: transitionEnd
}
})()
})
}($);
/* ==========================================================
* bootstrap-alert.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#alerts
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* ALERT CLASS DEFINITION
* ====================== */
var dismiss = '[data-foundry-dismiss="alert"]'
, Alert = function (el) {
$(el).on('click', dismiss, this.close)
}
Alert.prototype.close = function (e) {
var $this = $(this)
, selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
e && e.preventDefault()
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
$parent.trigger(e = $.Event('close'))
if (e.isDefaultPrevented()) return
$parent.removeClass('in')
function removeElement() {
$parent
.trigger('closed')
.remove()
}
$.support.transition && $parent.hasClass('fade') ?
$parent.on($.support.transition.end, removeElement) :
removeElement()
}
/* ALERT PLUGIN DEFINITION
* ======================= */
$.fn.alert = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('alert')
if (!data) $this.data('alert', (data = new Alert(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.alert.Constructor = Alert
/* ALERT DATA-API
* ============== */
$(document).on('click.alert.data-api', dismiss, Alert.prototype.close)
}($);
/* ============================================================
* bootstrap-button.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#buttons
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* BUTTON PUBLIC CLASS DEFINITION
* ============================== */
var Button = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.button.defaults, options)
}
Button.prototype.setState = function (state) {
var d = 'disabled'
, $el = this.$element
, data = $el.data()
, val = $el.is('input') ? 'val' : 'html'
state = state + 'Text'
data.resetText || $el.data('resetText', $el[val]())
$el[val](data[state] || this.options[state])
// push to event loop to allow forms to submit
setTimeout(function () {
state == 'loadingText' ?
$el.addClass(d).attr(d, d) :
$el.removeClass(d).removeAttr(d)
}, 0)
}
Button.prototype.toggle = function () {
var $parent = this.$element.closest('[data-foundry-toggle="buttons-radio"]')
if ($parent.hasClass('disabled')) return;
$parent && $parent
.find('.active')
.removeClass('active')
this.$element.toggleClass('active')
}
/* BUTTON PLUGIN DEFINITION
* ======================== */
$.fn.button = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('button')
, options = typeof option == 'object' && option
if (!data) $this.data('button', (data = new Button(this, options)))
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)
})
}
$.fn.button.defaults = {
loadingText: 'loading...'
}
$.fn.button.Constructor = Button
/* BUTTON DATA-API
* =============== */
$(document).on('click.button.data-api', '[data-foundry-toggle^=button]', function (e) {
var $btn = $(e.target)
if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
$btn.button('toggle')
})
}($);
/* ==========================================================
* bootstrap-carousel.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#carousel
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* CAROUSEL CLASS DEFINITION
* ========================= */
var Carousel = function (element, options) {
this.$element = $(element)
this.options = options
this.options.slide && this.slide(this.options.slide)
this.options.pause == 'hover' && this.$element
.on('mouseenter', $.proxy(this.pause, this))
.on('mouseleave', $.proxy(this.cycle, this))
}
Carousel.prototype = {
cycle: function (e) {
if (!e) this.paused = false
this.options.interval
&& !this.paused
&& (this.interval = setInterval($.proxy(this.next, this), this.options.interval))
return this
}
, to: function (pos) {
var $active = this.$element.find('.item.active')
, children = $active.parent().children()
, activePos = children.index($active)
, that = this
if (pos > (children.length - 1) || pos < 0) return
if (this.sliding) {
return this.$element.one('slid', function () {
that.to(pos)
})
}
if (activePos == pos) {
return this.pause().cycle()
}
return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
}
, pause: function (e) {
if (!e) this.paused = true
if (this.$element.find('.next, .prev').length && $.support.transition.end) {
this.$element.trigger($.support.transition.end)
this.cycle()
}
clearInterval(this.interval)
this.interval = null
return this
}
, next: function () {
if (this.sliding) return
return this.slide('next')
}
, prev: function () {
if (this.sliding) return
return this.slide('prev')
}
, slide: function (type, next) {
var $active = this.$element.find('.item.active')
, $next = next || $active[type]()
, isCycling = this.interval
, direction = type == 'next' ? 'left' : 'right'
, fallback = type == 'next' ? 'first' : 'last'
, that = this
, e
this.sliding = true
isCycling && this.pause()
$next = $next.length ? $next : this.$element.find('.item')[fallback]()
e = $.Event('slide', {
relatedTarget: $next[0]
})
if ($next.hasClass('active')) return
if ($.support.transition && this.$element.hasClass('slide')) {
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$next.addClass(type)
$next[0].offsetWidth // force reflow
$active.addClass(direction)
$next.addClass(direction)
this.$element.one($.support.transition.end, function () {
$next.removeClass([type, direction].join(' ')).addClass('active')
$active.removeClass(['active', direction].join(' '))
that.sliding = false
setTimeout(function () { that.$element.trigger('slid') }, 0)
})
} else {
this.$element.trigger(e)
if (e.isDefaultPrevented()) return
$active.removeClass('active')
$next.addClass('active')
this.sliding = false
this.$element.trigger('slid')
}
isCycling && this.cycle()
return this
}
}
/* CAROUSEL PLUGIN DEFINITION
* ========================== */
$.fn.carousel = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('carousel')
, options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option)
, action = typeof option == 'string' ? option : options.slide
if (!data) $this.data('carousel', (data = new Carousel(this, options)))
if (typeof option == 'number') data.to(option)
else if (action) data[action]()
else if (options.interval) data.cycle()
})
}
$.fn.carousel.defaults = {
interval: 5000
, pause: 'hover'
}
$.fn.carousel.Constructor = Carousel
/* CAROUSEL DATA-API
* ================= */
$(document).on('click.carousel.data-api', '[data-foundry-slide]', function (e) {
var $this = $(this), href
, $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
, options = $.extend({}, $target.data(), $this.data())
$target.carousel(options)
e.preventDefault()
})
}($);
/* =============================================================
* bootstrap-collapse.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#collapse
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* COLLAPSE PUBLIC CLASS DEFINITION
* ================================ */
var Collapse = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.collapse.defaults, options)
if (this.options.parent) {
this.$parent = $(this.options.parent)
}
this.options.toggle && this.toggle()
}
Collapse.prototype = {
constructor: Collapse
, dimension: function () {
var hasWidth = this.$element.hasClass('width')
return hasWidth ? 'width' : 'height'
}
, show: function () {
var dimension
, scroll
, actives
, hasData
if (this.transitioning) return
dimension = this.dimension()
scroll = $.camelCase(['scroll', dimension].join('-'))
actives = this.$parent && this.$parent.find('> .accordion-group > .in')
if (actives && actives.length) {
hasData = actives.data('collapse')
if (hasData && hasData.transitioning) return
actives.collapse('hide')
hasData || actives.data('collapse', null)
}
this.$element[dimension](0)
$.support.transition && this.$element[dimension](this.$element[0][scroll])
this.transition('addClass', $.Event('bootstrap-show'), 'shown')
}
, hide: function () {
var dimension
if (this.transitioning) return
dimension = this.dimension()
this.reset(this.$element[dimension]())
this.transition('removeClass', $.Event('bootstrap-hide'), 'hidden')
this.$element[dimension](0)
}
, reset: function (size) {
var dimension = this.dimension()
this.$element
.removeClass('collapse')
[dimension](size || 'auto')
[0].offsetWidth
this.$element[size !== null ? 'addClass' : 'removeClass']('collapse')
return this
}
, transition: function (method, startEvent, completeEvent) {
var that = this
, complete = function () {
if (startEvent.type == 'bootstrap-show') that.reset()
that.transitioning = 0
that.$element.trigger(completeEvent)
}
this.$element.trigger(startEvent)
if (startEvent.isDefaultPrevented()) return
this.transitioning = 1
this.$element[method]('in')
$.support.transition && this.$element.hasClass('collapse') ?
this.$element.one($.support.transition.end, complete) :
complete()
}
, toggle: function () {
this[this.$element.hasClass('in') ? 'hide' : 'show']()
}
}
/* COLLAPSIBLE PLUGIN DEFINITION
* ============================== */
$.fn.collapse = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('collapse')
, options = typeof option == 'object' && option
if (!data) $this.data('collapse', (data = new Collapse(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.collapse.defaults = {
toggle: true
}
$.fn.collapse.Constructor = Collapse
/* COLLAPSIBLE DATA-API
* ==================== */
$(document).on('click.collapse.data-api', '[data-foundry-toggle=collapse]', function (e) {
var $this = $(this), href
, target = $this.attr('data-target')
|| e.preventDefault()
|| (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
, option = $(target).data('collapse') ? 'toggle' : $this.data()
$this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
$(target).collapse(option)
})
}($);
/* ============================================================
* bootstrap-dropdown.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#dropdowns
* ============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function ($) {
"use strict"; // jshint ;_;
/* DROPDOWN CLASS DEFINITION
* ========================= */
var toggle = '[data-foundry-toggle=dropdown]'
, Dropdown = function (element) {
var $el = $(element).on('click.dropdown.data-api', this.toggle)
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
}
Dropdown.prototype = {
constructor: Dropdown
, toggle: function (e) {
var $this = $(this)
, $parent
, isActive
if ($this.is('.disabled, :disabled')) return
$parent = getParent($this)
isActive = $parent.hasClass('open')
clearMenus(e);
if (!isActive) {
$parent.toggleClass('open');
$parent.trigger("dropdownOpen", [e.target]);
$this.focus()
}
return false
}
, keydown: function (e) {
var $this
, $items
, $active
, $parent
, isActive
, index
if (!/(38|40|27)/.test(e.keyCode)) return
$this = $(this)
e.preventDefault()
e.stopPropagation()
if ($this.is('.disabled, :disabled')) return
$parent = getParent($this)
isActive = $parent.hasClass('open')
if (!isActive || (isActive && e.keyCode == 27)) return $this.click()
$items = $('[role=menu] li:not(.divider) a', $parent)
if (!$items.length) return
index = $items.index($items.filter(':focus'))
if (e.keyCode == 38 && index > 0) index-- // up
if (e.keyCode == 40 && index < $items.length - 1) index++ // down
if (!~index) index = 0
$items
.eq(index)
.focus()
}
}
function clearMenus(e) {
$(toggle).each(function () {
var $parent = getParent($(this)),
wasActive = $parent.hasClass("open");
$parent.removeClass('open');
if (wasActive) {
$parent.trigger("dropdownClose", [e.target]);
}
})
}
function getParent($this) {
var selector = $this.attr('data-target')
, $parent
if (!selector) {
selector = $this.attr('href')
selector = selector && /#/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
$parent = $(selector)
$parent.length || ($parent = $this.parent())
return $parent
}
/* DROPDOWN PLUGIN DEFINITION
* ========================== */
$.fn.dropdown = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('dropdown')
if (!data) $this.data('dropdown', (data = new Dropdown(this)))
if (typeof option == 'string') data[option].call($this)
})
}
$.fn.dropdown.Constructor = Dropdown
/* APPLY TO STANDARD DROPDOWN ELEMENTS
* =================================== */
$(document)
.on('click.dropdown.data-api', clearMenus)
.on('click.dropdown', '.dropdown_ form, .dropdown-static', function (e) { e.stopPropagation() })
.on('click.dropdown.data-api' , toggle, Dropdown.prototype.toggle)
.on('keydown.dropdown.data-api', toggle + ', [role=menu]' , Dropdown.prototype.keydown)
}($);
/* =========================================================
* bootstrap-modal.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#modals
* =========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================= */
!function ($) {
"use strict"; // jshint ;_;
/* MODAL CLASS DEFINITION
* ====================== */
var Modal = function (element, options) {
this.options = options
this.$element = $(element)
.delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
this.options.remote && this.$element.find('.modal-body').load(this.options.remote)
}
Modal.prototype = {
constructor: Modal
, toggle: function () {
return this[!this.isShown ? 'show' : 'hide']()
}
, show: function () {
var that = this
, e = $.Event('show')
this.$element.trigger(e)
if (this.isShown || e.isDefaultPrevented()) return
this.isShown = true
this.escape()
this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')
if (!that.$element.parent().length) {
that.$element.appendTo(document.body) //don't move modals dom position
}
that.$element
.show()
if (transition) {
that.$element[0].offsetWidth // force reflow
}
that.$element
.addClass('in')
.attr('aria-hidden', false)
.focus()
that.enforceFocus()
transition ?
that.$element.one($.support.transition.end, function () { that.$element.focus().trigger('shown') }) :
that.$element.focus().trigger('shown')
})
}
, hide: function (e) {
e && e.preventDefault()
var that = this
e = $.Event('hide')
this.$element.trigger(e)
if (!this.isShown || e.isDefaultPrevented()) return
this.isShown = false
this.escape()
$(document).off('focusin.modal')
this.$element
.removeClass('in')
.attr('aria-hidden', true)
$.support.transition && this.$element.hasClass('fade') ?
this.hideWithTransition() :
this.hideModal()
}
, enforceFocus: function () {
var that = this
$(document).on('focusin.modal', function (e) {
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
that.$element.focus()
}
})
}
, escape: function () {
var that = this
if (this.isShown && this.options.keyboard) {
this.$element.on('keyup.dismiss.modal', function ( e ) {
e.which == 27 && that.hide()
})
} else if (!this.isShown) {
this.$element.off('keyup.dismiss.modal')
}
}
, hideWithTransition: function () {
var that = this
, timeout = setTimeout(function () {
that.$element.off($.support.transition.end)
that.hideModal()
}, 500)
this.$element.one($.support.transition.end, function () {
clearTimeout(timeout)
that.hideModal()
})
}
, hideModal: function (that) {
this.$element
.hide()
.trigger('hidden')
this.backdrop()
}
, removeBackdrop: function () {
this.$backdrop.remove()
this.$backdrop = null
}
, backdrop: function (callback) {
var that = this
, animate = this.$element.hasClass('fade') ? 'fade' : ''
if (this.isShown && this.options.backdrop) {
var doAnimate = $.support.transition && animate
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
.appendTo(document.body)
this.$backdrop.click(
this.options.backdrop == 'static' ?
$.proxy(this.$element[0].focus, this.$element[0])
: $.proxy(this.hide, this)
)
if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
this.$backdrop.addClass('in')
doAnimate ?
this.$backdrop.one($.support.transition.end, callback) :
callback()
} else if (!this.isShown && this.$backdrop) {
this.$backdrop.removeClass('in')
$.support.transition && this.$element.hasClass('fade')?
this.$backdrop.one($.support.transition.end, $.proxy(this.removeBackdrop, this)) :
this.removeBackdrop()
} else if (callback) {
callback()
}
}
}
/* MODAL PLUGIN DEFINITION
* ======================= */
$.fn.modal = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('modal')
, options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('modal', (data = new Modal(this, options)))
if (typeof option == 'string') data[option]()
else if (options.show) data.show()
})
}
$.fn.modal.defaults = {
backdrop: true
, keyboard: true
, show: true
}
$.fn.modal.Constructor = Modal
/* MODAL DATA-API
* ============== */
$(document).on('click.modal.data-api', '[data-foundry-toggle="modal"]', function (e) {
var $this = $(this)
, href = $this.attr('href')
, $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) //strip for ie7
, option = $target.data('modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data())
e.preventDefault()
$target
.modal(option)
.one('hide', function () {
$this.focus()
})
})
}($);
/* ===========================================================
* bootstrap-tooltip.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#tooltips
* Inspired by the original jQuery.tipsy by Jason Frame
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TOOLTIP PUBLIC CLASS DEFINITION
* =============================== */
var Tooltip = function (element, options) {
this.init('tooltip', element, options)
}
Tooltip.prototype = {
constructor: Tooltip
, init: function (type, element, options) {
var eventIn
, eventOut
this.type = type
this.$element = $(element)
this.options = this.getOptions(options)
this.enabled = true
if (this.options.trigger == 'click') {
this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
} else if (this.options.trigger != 'manual') {
eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
}
this.options.selector ?
(this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
this.fixTitle()
}
, getOptions: function (options) {
options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
if (options.delay && typeof options.delay == 'number') {
options.delay = {
show: options.delay
, hide: options.delay
}
}
return options
}
, enter: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (!self.options.delay || !self.options.delay.show) return self.show()
clearTimeout(this.timeout)
self.hoverState = 'in'
this.timeout = setTimeout(function() {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
, leave: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
if (this.timeout) clearTimeout(this.timeout)
if (!self.options.delay || !self.options.delay.hide) return self.hide()
self.hoverState = 'out'
this.timeout = setTimeout(function() {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
, show: function () {
var $tip
, inside
, pos
, actualWidth
, actualHeight
, placement
, tp
if (this.hasContent() && this.enabled) {
$tip = this.tip()
this.setContent()
if (this.options.animation) {
$tip.addClass('fade')
}
placement = typeof this.options.placement == 'function' ?
this.options.placement.call(this, $tip[0], this.$element[0]) :
this.options.placement
inside = /in/.test(placement)
$tip
.detach()
.css({ top: 0, left: 0, display: 'block' })
switch (this.options.container || 'parent') {
case "parent":
$tip.insertAfter(this.$element);
break;
case "body":
$tip.appendTo(document.body);
break;
default:
var container = $(this.options.container);
if (container.length) {
$tip.appendTo(container);
} else {
$tip.insertAfter(this.$element);
}
}
pos = this.getPosition(inside)
actualWidth = $tip[0].offsetWidth
actualHeight = $tip[0].offsetHeight
switch (inside ? placement.split(' ')[1] : placement) {
case 'bottom':
tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'top':
tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
break
case 'left':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
break
case 'right':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
break
}
$tip
.offset(tp)
.addClass(placement)
.addClass('in')
}
}
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
$tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
$tip.removeClass('fade in top bottom left right')
}
, hide: function () {
var that = this
, $tip = this.tip()
$tip.removeClass('in')
function removeWithAnimation() {
var timeout = setTimeout(function () {
$tip.off($.support.transition.end).detach()
}, 500)
$tip.one($.support.transition.end, function () {
clearTimeout(timeout)
$tip.detach()
})
}
$.support.transition && this.$tip.hasClass('fade') ?
removeWithAnimation() :
$tip.detach()
return this
}
, fixTitle: function () {
var $e = this.$element
if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
$e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
}
}
, hasContent: function () {
return this.getTitle()
}
, getPosition: function (inside) {
return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
width: this.$element[0].offsetWidth
, height: this.$element[0].offsetHeight
})
}
, getTitle: function () {
var title
, $e = this.$element
, o = this.options
title = $e.attr('data-original-title')
|| (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
return title
}
, tip: function () {
return this.$tip = this.$tip || $(this.options.template)
}
, validate: function () {
if (!this.$element[0].parentNode) {
this.hide()
this.$element = null
this.options = null
}
}
, enable: function () {
this.enabled = true
}
, disable: function () {
this.enabled = false
}
, toggleEnabled: function () {
this.enabled = !this.enabled
}
, toggle: function (e) {
var self = $(e.currentTarget)[this.type](this._options).data(this.type)
self[self.tip().hasClass('in') ? 'hide' : 'show']()
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
}
/* TOOLTIP PLUGIN DEFINITION
* ========================= */
$.fn.tooltip = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tooltip')
, options = typeof option == 'object' && option
if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tooltip.Constructor = Tooltip
$.fn.tooltip.defaults = {
animation: true
, placement: 'top'
, selector: false
, template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
, trigger: 'hover'
, title: ''
, delay: 0
, html: false
, container: 'parent'
}
}($);
/* ===========================================================
* bootstrap-popover.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#popovers
* ===========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* =========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* POPOVER PUBLIC CLASS DEFINITION
* =============================== */
var Popover = function (element, options) {
this.init('popover', element, options)
}
/* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
========================================== */
Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
constructor: Popover
, setContent: function () {
var $tip = this.tip()
, title = this.getTitle()
, content = this.getContent()
$tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title)
$tip.find('.popover-content > *')[this.options.html ? 'html' : 'text'](content)
$tip.removeClass('fade top bottom left right in')
}
, hasContent: function () {
return this.getTitle() || this.getContent()
}
, getContent: function () {
var content
, $e = this.$element
, o = this.options
content = $e.attr('data-content')
|| (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
return content
}
, tip: function () {
if (!this.$tip) {
this.$tip = $(this.options.template)
}
return this.$tip
}
, destroy: function () {
this.hide().$element.off('.' + this.type).removeData(this.type)
}
})
/* POPOVER PLUGIN DEFINITION
* ======================= */
$.fn.popover = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('popover')
, options = typeof option == 'object' && option
if (!data) $this.data('popover', (data = new Popover(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.popover.Constructor = Popover
$.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
placement: 'right'
, trigger: 'click'
, content: ''
, template: '<div class="popover popover-fd"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
})
}($);
/* =============================================================
* bootstrap-scrollspy.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#scrollspy
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* SCROLLSPY CLASS DEFINITION
* ========================== */
function ScrollSpy(element, options) {
var process = $.proxy(this.process, this)
, $element = $(element).is('body') ? $(window) : $(element)
, href
this.options = $.extend({}, $.fn.scrollspy.defaults, options)
this.$scrollElement = $element.on('scroll.scroll-spy.data-api', process)
this.selector = (this.options.target
|| ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
|| '') + ' .nav li > a'
this.$body = $('body')
this.refresh()
this.process()
}
ScrollSpy.prototype = {
constructor: ScrollSpy
, refresh: function () {
var self = this
, $targets
this.offsets = $([])
this.targets = $([])
$targets = this.$body
.find(this.selector)
.map(function () {
var $el = $(this)
, href = $el.data('target') || $el.attr('href')
, $href = /^#\w/.test(href) && $(href)
return ( $href
&& $href.length
&& [[ $href.position().top, href ]] ) || null
})
.sort(function (a, b) { return a[0] - b[0] })
.each(function () {
self.offsets.push(this[0])
self.targets.push(this[1])
})
}
, process: function () {
var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
, scrollHeight = this.$scrollElement[0].scrollHeight || this.$body[0].scrollHeight
, maxScroll = scrollHeight - this.$scrollElement.height()
, offsets = this.offsets
, targets = this.targets
, activeTarget = this.activeTarget
, i
if (scrollTop >= maxScroll) {
return activeTarget != (i = targets.last()[0])
&& this.activate ( i )
}
for (i = offsets.length; i--;) {
activeTarget != targets[i]
&& scrollTop >= offsets[i]
&& (!offsets[i + 1] || scrollTop <= offsets[i + 1])
&& this.activate( targets[i] )
}
}
, activate: function (target) {
var active
, selector
this.activeTarget = target
$(this.selector)
.parent('.active')
.removeClass('active')
selector = this.selector
+ '[data-target="' + target + '"],'
+ this.selector + '[href="' + target + '"]'
active = $(selector)
.parent('li')
.addClass('active')
if (active.parent('.dropdown-menu').length) {
active = active.closest('li.dropdown').addClass('active')
}
active.trigger('activate')
}
}
/* SCROLLSPY PLUGIN DEFINITION
* =========================== */
$.fn.scrollspy = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('scrollspy')
, options = typeof option == 'object' && option
if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.scrollspy.Constructor = ScrollSpy
$.fn.scrollspy.defaults = {
offset: 10
}
/* SCROLLSPY DATA-API
* ================== */
$(window).on('load', function () {
$('[data-foundry-spy="scroll"]').each(function () {
var $spy = $(this)
$spy.scrollspy($spy.data())
})
})
}($);
/* ========================================================
* bootstrap-tab.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#tabs
* ========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ======================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* TAB CLASS DEFINITION
* ==================== */
var Tab = function (element) {
this.element = $(element)
}
Tab.prototype = {
constructor: Tab
, show: function () {
var $this = this.element
, $ul = $this.closest('ul:not(.dropdown-menu)')
, selector = $this.attr('data-target')
, previous
, $target
, e
if (!selector) {
selector = $this.attr('href')
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
}
if ( $this.parent('li').hasClass('active') ) return
previous = $ul.find('.active:last a')[0]
e = $.Event('show', {
relatedTarget: previous
})
$this.trigger(e)
if (e.isDefaultPrevented()) return
$target = $(selector)
this.activate($this.parent('li'), $ul)
this.activate($target, $target.parent(), function () {
$this.trigger({
type: 'shown'
, relatedTarget: previous
})
})
}
, activate: function ( element, container, callback) {
var $active = container.find('> .active')
, transition = callback
&& $.support.transition
&& $active.hasClass('fade')
function next() {
$active
.removeClass('active')
.find('> .dropdown-menu > .active')
.removeClass('active')
element.addClass('active')
if (transition) {
element[0].offsetWidth // reflow for transition
element.addClass('in')
} else {
element.removeClass('fade')
}
if ( element.parent('.dropdown-menu') ) {
element.closest('li.dropdown').addClass('active')
}
callback && callback()
}
transition ?
$active.one($.support.transition.end, next) :
next()
$active.removeClass('in')
}
}
/* TAB PLUGIN DEFINITION
* ===================== */
$.fn.tab = function ( option ) {
return this.each(function () {
var $this = $(this)
, data = $this.data('tab')
if (!data) $this.data('tab', (data = new Tab(this)))
if (typeof option == 'string') data[option]()
})
}
$.fn.tab.Constructor = Tab
/* TAB DATA-API
* ============ */
$(document).on('click.tab.data-api', '[data-foundry-toggle="tab"], [data-foundry-toggle="pill"]', function (e) {
e.preventDefault()
$(this).tab('show')
})
}($);
/* =============================================================
* bootstrap-typeahead.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#typeahead
* =============================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ============================================================ */
!function($){
"use strict"; // jshint ;_;
/* TYPEAHEAD PUBLIC CLASS DEFINITION
* ================================= */
var Typeahead = function (element, options) {
this.$element = $(element)
this.options = $.extend({}, $.fn.typeahead.defaults, options)
this.matcher = this.options.matcher || this.matcher
this.sorter = this.options.sorter || this.sorter
this.highlighter = this.options.highlighter || this.highlighter
this.updater = this.options.updater || this.updater
this.$menu = $(this.options.menu).appendTo('body')
this.source = this.options.source
this.shown = false
this.listen()
}
Typeahead.prototype = {
constructor: Typeahead
, select: function () {
var val = this.$menu.find('.active').attr('data-value')
this.$element
.val(this.updater(val))
.change()
return this.hide()
}
, updater: function (item) {
return item
}
, show: function () {
var pos = $.extend({}, this.$element.offset(), {
height: this.$element[0].offsetHeight
})
this.$menu.css({
top: pos.top + pos.height
, left: pos.left
})
this.$menu.show()
this.shown = true
return this
}
, hide: function () {
this.$menu.hide()
this.shown = false
return this
}
, lookup: function (event) {
var items
this.query = this.$element.val()
if (!this.query || this.query.length < this.options.minLength) {
return this.shown ? this.hide() : this
}
items = $.isFunction(this.source) ? this.source(this.query, $.proxy(this.process, this)) : this.source
return items ? this.process(items) : this
}
, process: function (items) {
var that = this
items = $.grep(items, function (item) {
return that.matcher(item)
})
items = this.sorter(items)
if (!items.length) {
return this.shown ? this.hide() : this
}
return this.render(items.slice(0, this.options.items)).show()
}
, matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase())
}
, sorter: function (items) {
var beginswith = []
, caseSensitive = []
, caseInsensitive = []
, item
while (item = items.shift()) {
if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
else if (~item.indexOf(this.query)) caseSensitive.push(item)
else caseInsensitive.push(item)
}
return beginswith.concat(caseSensitive, caseInsensitive)
}
, highlighter: function (item) {
var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
, render: function (items) {
var that = this
items = $(items).map(function (i, item) {
i = $(that.options.item).attr('data-value', item)
i.find('a').html(that.highlighter(item))
return i[0]
})
items.first().addClass('active')
this.$menu.html(items)
return this
}
, next: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, next = active.next()
if (!next.length) {
next = $(this.$menu.find('li')[0])
}
next.addClass('active')
}
, prev: function (event) {
var active = this.$menu.find('.active').removeClass('active')
, prev = active.prev()
if (!prev.length) {
prev = this.$menu.find('li').last()
}
prev.addClass('active')
}
, listen: function () {
this.$element
.on('blur', $.proxy(this.blur, this))
.on('keypress', $.proxy(this.keypress, this))
.on('keyup', $.proxy(this.keyup, this))
if (this.eventSupported('keydown')) {
this.$element.on('keydown', $.proxy(this.keydown, this))
}
this.$menu
.on('click', $.proxy(this.click, this))
.on('mouseenter', 'li', $.proxy(this.mouseenter, this))
}
, eventSupported: function(eventName) {
var isSupported = eventName in this.$element
if (!isSupported) {
this.$element.setAttribute(eventName, 'return;')
isSupported = typeof this.$element[eventName] === 'function'
}
return isSupported
}
, move: function (e) {
if (!this.shown) return
switch(e.keyCode) {
case 9: // tab
case 13: // enter
case 27: // escape
e.preventDefault()
break
case 38: // up arrow
e.preventDefault()
this.prev()
break
case 40: // down arrow
e.preventDefault()
this.next()
break
}
e.stopPropagation()
}
, keydown: function (e) {
this.suppressKeyPressRepeat = !~$.inArray(e.keyCode, [40,38,9,13,27])
this.move(e)
}
, keypress: function (e) {
if (this.suppressKeyPressRepeat) return
this.move(e)
}
, keyup: function (e) {
switch(e.keyCode) {
case 40: // down arrow
case 38: // up arrow
case 16: // shift
case 17: // ctrl
case 18: // alt
break
case 9: // tab
case 13: // enter
if (!this.shown) return
this.select()
break
case 27: // escape
if (!this.shown) return
this.hide()
break
default:
this.lookup()
}
e.stopPropagation()
e.preventDefault()
}
, blur: function (e) {
var that = this
setTimeout(function () { that.hide() }, 150)
}
, click: function (e) {
e.stopPropagation()
e.preventDefault()
this.select()
}
, mouseenter: function (e) {
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')
}
}
/* TYPEAHEAD PLUGIN DEFINITION
* =========================== */
$.fn.typeahead = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('typeahead')
, options = typeof option == 'object' && option
if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.typeahead.defaults = {
source: []
, items: 8
, menu: '<ul class="typeahead dropdown-menu"></ul>'
, item: '<li><a href="#"></a></li>'
, minLength: 1
}
$.fn.typeahead.Constructor = Typeahead
/* TYPEAHEAD DATA-API
* ================== */
$(document).on('focus.typeahead.data-api', '[data-foundry-provide="typeahead"]', function (e) {
var $this = $(this)
if ($this.data('typeahead')) return
e.preventDefault()
$this.typeahead($this.data())
})
}($);
/* ==========================================================
* bootstrap-affix.js v2.2.2
* http://twitter.github.com/bootstrap/javascript.html#affix
* ==========================================================
* Copyright 2012 Twitter, Inc.
*
* 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.
* ========================================================== */
!function ($) {
"use strict"; // jshint ;_;
/* AFFIX CLASS DEFINITION
* ====================== */
var Affix = function (element, options) {
this.options = $.extend({}, $.fn.affix.defaults, options)
this.$window = $(window)
.on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
.on('click.affix.data-api', $.proxy(function () { setTimeout($.proxy(this.checkPosition, this), 1) }, this))
this.$element = $(element)
this.checkPosition()
}
Affix.prototype.checkPosition = function () {
if (!this.$element.is(':visible')) return
var scrollHeight = $(document).height()
, scrollTop = this.$window.scrollTop()
, position = this.$element.offset()
, offset = this.options.offset
, offsetBottom = offset.bottom
, offsetTop = offset.top
, reset = 'affix affix-top affix-bottom'
, affix
if (typeof offset != 'object') offsetBottom = offsetTop = offset
if (typeof offsetTop == 'function') offsetTop = offset.top()
if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
false : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
'bottom' : offsetTop != null && scrollTop <= offsetTop ?
'top' : false
if (this.affixed === affix) return
this.affixed = affix
this.unpin = affix == 'bottom' ? position.top - scrollTop : null
this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
}
/* AFFIX PLUGIN DEFINITION
* ======================= */
$.fn.affix = function (option) {
return this.each(function () {
var $this = $(this)
, data = $this.data('affix')
, options = typeof option == 'object' && option
if (!data) $this.data('affix', (data = new Affix(this, options)))
if (typeof option == 'string') data[option]()
})
}
$.fn.affix.Constructor = Affix
$.fn.affix.defaults = {
offset: 0
}
/* AFFIX DATA-API
* ============== */
$(window).on('load', function () {
$('[data-foundry-spy="affix"]').each(function () {
var $spy = $(this)
, data = $spy.data()
data.offset = data.offset || {}
data.offsetBottom && (data.offset.bottom = data.offsetBottom)
data.offsetTop && (data.offset.top = data.offsetTop)
$spy.affix(data)
})
})
}($);
/**
* bootstrap-notify.js v1.0
* --
* http://twitter.com/nijikokun
* Copyright 2012 Nijiko Yonskai, Goodybag
* --
* 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.
*/
!function ($) {
var Notification = function (element, options) {
// Element collection
this.$element = $(element);
this.$note = $('<div class="alert"></div>');
this.options = $.extend(true, $.fn.notify.defaults, options);
// Setup from options
if(this.options.transition)
if(this.options.transition == 'fade')
this.$note.addClass('in').addClass(this.options.transition);
else this.$note.addClass(this.options.transition);
else this.$note.addClass('fade').addClass('in');
if(this.options.type)
this.$note.addClass('alert-' + this.options.type);
else this.$note.addClass('alert-success');
if(!this.options.message && this.$element.data("message") !== '') // dom text
this.$note.html(this.$element.data("message"));
else
if(typeof this.options.message === 'object')
if(this.options.message.html)
this.$note.html(this.options.message.html);
else if(this.options.message.text)
this.$note.text(this.options.message.text);
else
this.$note.html(this.options.message);
if(this.options.closable)
var link = $('<a class="close pull-right" href="javascript: void(0);">×</a>');
$(link).on('click', $.proxy(onClose, this));
this.$note.prepend(link);
return this;
};
onClose = function() {
this.options.onClose();
$(this.$note).remove();
this.options.onClosed();
};
Notification.prototype.show = function () {
if(this.options.fadeOut.enabled)
this.$note.delay(this.options.fadeOut.delay || 3000).fadeOut('slow', $.proxy(onClose, this));
this.$element.append(this.$note);
this.$note.alert();
};
Notification.prototype.hide = function () {
if(this.options.fadeOut.enabled)
this.$note.delay(this.options.fadeOut.delay || 3000).fadeOut('slow', $.proxy(onClose, this));
else onClose.call(this);
};
$.fn.notify = function (options) {
return new Notification(this, options);
};
$.fn.notify.defaults = {
type: 'success',
closable: true,
transition: 'fade',
fadeOut: {
enabled: true,
delay: 3000
},
message: null,
onClose: function () {},
onClosed: function () {}
}
}($);
});
FD31.plugin("responsive", function($) {
// $(selector).responsive({condition});
// $(selector).responsive([{condition1}, {condition2}]);
/*var defaultOptions = {
// main element width to calculate
elementWidth: function() {}, // a function that returns pixel value
// array of conditions of ascending thresholdWidth
conditions: [{
// threshold for this condition
at: 0,// threshold value
// condition specific options
switchTo: '',// classname to apply to the node
alsoSwitch: {
'selector': 'class'
}, // objects with element and class
switchStylesheet: '',
whenApplied: '', // function to run
whenRemoved: '' // reverse function that reverses any action in target function
}]
}*/
$.responsive = function(elem, options) {
return new Responsive(elem, options);
};
$.fn.responsive = function(conditions) {
if (conditions) {
var elem = this,
options = {
elementWidth: function(elem) {
return elem.outerWidth(true);
},
conditions: conditions
};
$.responsive(elem, options);
}
return this;
};
var $window = $(window),
$isFunc = $.isFunction;
var Responsive = function(elem, options) {
var self = this,
elem = $(elem),
instance = elem.data("$responsive");
// If there is an existing instance, kill it.
if (instance) instance.destroy();
// Construct instance
$.extend(self, {
// Accept node, selectors, jQuery elements.
elem : elem,
options : options,
conditions: $.sortBy($.makeArray(options.conditions), function(condition){ return condition.at; }),
event : "resize.responsive" + $.uid(),
handler : $.debounce(function(){ self.set(); }, 250)
});
// Delete conditions prop from options
delete options.conditions;
// Store instance within element
elem.data("$responsive", self)
// Wait until document is ready before
// applying responsive events
$(function(){
// Attach resize handler to window
$window.on(self.event, self.handler);
// Set conditions
self.set();
});
// Set conditions once again
// on window load event.
$(window).load(function(){
self.set();
});
}
$.extend(Responsive.prototype, {
set: function() {
var self = this,
elementWidth = self.options.elementWidth;
self.elem.each(function(){
var elem = $(this),
currentWidth = ($isFunc(elementWidth)) ? elementWidth(elem) : elementWidth;
// Remove current condition
self.removeCondition(elem.data("currentCondition"), elem);
// Analyze all conditions
$.each(self.conditions, function(i, condition) {
var thresholdWidth = condition.at;
if (currentWidth <= thresholdWidth) {
self.applyCondition(condition, elem);
return false;
}
});
});
},
applyCondition: function(condition, elem) {
var switchTo, alsoSwitch, switchStylesheet, whenApplied;
// Classnames to remove
(switchTo = condition.switchTo) &&
elem.addClass(switchTo);
// Classnames to remove on other elements
(alsoSwitch = condition.alsoSwitch) &&
$.each(alsoSwitch, function(selector, classname) {
$(selector).addClass(classname);
});
// Stylesheets to remove
(switchStylesheet = condition.switchStylesheet) &&
$.each($.makeArray(switchStylesheet), function(i, url) {
// Load stylesheet if it hasn't been loaded.
var stylesheet = $('link[href$="' + url + '"]');
if (stylesheet.length < 1) {
$('<link/>')
.attr({
rel : 'stylesheet',
type: 'text/css',
href: url
})
.appendTo('head');
}
});
// Callback to execute when this condition is removed.
(whenApplied = condition.whenApplied) &&
$isFunc(whenApplied) && whenApplied();
elem.data("currentCondition", condition)
.trigger("responsive", [condition]);
},
removeCondition: function(condition, elem) {
if (!condition) return;
var switchTo, alsoSwitch, switchStylesheet, whenRemoved;
// Classnames to remove
(switchTo = condition.switchTo) &&
elem.removeClass(switchTo);
// Classnames to remove on other elements
(alsoSwitch = condition.alsoSwitch) &&
$.each(alsoSwitch, function(selector, classname) {
$(selector).removeClass(classname);
});
// Stylesheets to remove
(switchStylesheet = condition.switchStylesheet) &&
$.each($.makeArray(switchStylesheet), function(i, url) {
$('link[href$="' + url + '"]').remove();
});
// Callback to execute when this condition is removed.
(whenRemoved = condition.whenRemoved) &&
$isFunc(whenRemoved) && whenRemoved();
elem.removeData("currentCondition");
},
resetToDefault: function(current) {
var self = this,
elem = self.elem;
$.each(self.conditions, function(i, condition) {
if (current && i == current) return;
self.removeCondition(condition, elem);
});
},
destroy: function() {
$window.off(this.event);
var self = this;
self.elem.each(function(){
var elem = $(this);
self.removeCondition(elem.data("currentCondition"), elem);
});
}
});
});
FD31.plugin("utils", function($) {
$.IE = (function(){
if (navigator.appVersion.indexOf("MSIE 10") != -1) return 10;
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
v++,
div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());/**
* jquery.uid
* Generates a unique id with optional prefix/suffix.
* https://github.com/jstonne/jquery.uid
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.uid = function(p,s) {
return ((p) ? p : '') + Math.random().toString().replace('.','') + ((s) ? s : '');
};
/**
* jquery.isDeferred
* Tests if an object is a jQuery Deferred object.
* https://github.com/jstonne/jquery.isDeferred
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.isDeferred = function(obj) {
return obj && $.isFunction(obj.always);
};
/**
* jquery.distinct
* Enhanced version of jQuery.unique that also removes
* removes object/string/integer duplicates within an array.
* https://github.com/jstonne/jquery.distinct
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.distinct = function(items) {
var uniqueElements = $.unique;
if (items.length < 1) {
return;
};
// If item is an array of DOM elements
if (items[0].nodeType) {
return uniqueElements.apply(this, arguments);
};
// If item is an array of objects
if (typeof items[0]=='object') {
var unique = Math.random(),
uniqueObjects = [];
$.each(items, function(i) {
if (!items[i][unique]) {
uniqueObjects.push(items[i]);
items[i][unique] = true;
}
});
$.each(uniqueObjects, function(i) {
delete uniqueObjects[i][unique];
});
return uniqueObjects;
};
// Anything else (can be combination of string, integers and boolean)
return $.grep(items, function(item, i) {
return $.inArray(item, items) === i;
});
};
/**
* jquery.trimSeparators
* Trims whitespace and separators.
* https://github.com/jstonne/jquery.trimSeparators
*
* Turns this: ",df ,,, ,,,abc, sdasd sdfsdf , asdsad, ,, , "
* into this : "df,abc,sdasd sdfsdf,asdsad"
*
* Requires jquery.distinct
* https://github.com/jstonne/jquery.distinct
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.trimSeparators = function(keyword, separator, removeDuplicates) {
var s = separator;
keyword = keyword
.replace(new RegExp('^['+s+'\\s]+|['+s+',\\s]+$','g'), '') // /^[,\s]+|[,\s]+$/g
.replace(new RegExp(s+'['+s+'\\s]*'+s,'g'), s) // /,[,\s]*,/g
.replace(new RegExp('[\\s]+'+s,'g'), s) // /[\s]+,/g
.replace(new RegExp(s+'[\\s]+','g'), s); // /,[\s]+/g
if (removeDuplicates) {
keyword = $.distinct(keyword.split(s)).join(s);
}
return keyword;
};
/*!
* jquery.number.
* Helpers for numbers.
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.isNumeric = function(n) {
// http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric
return !isNaN(parseFloat(n)) && isFinite(n);
};
$.Number = {
rotate: function(n, min, max, offset) {
if (offset===undefined)
offset = 0;
n += offset;
if (n < min) {
n += max + 1;
} else if (n > max) {
n -= max + 1;
}
return n;
}
};
/**
* jquery.stretchToFit
* Stretch any element to fit its parent container.
* https://github.com/jstonne/jquery.stretchToFit
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.fn.stretchToFit = function() {
return $.each(this, function()
{
var $this = $(this);
$this
.css('width', '100%')
.css('width', $this.width() * 2 - $this.outerWidth(true) - parseInt($this.css('borderLeftWidth')) - parseInt($this.css('borderRightWidth')));
});
};
/**
* jquery.fn.serializeJSON
* Serializes form values to JSON.
* https://github.com/jstonne/jquery.fn.serializeJSON
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.fn.serializeObject = function() {
var obj = {};
$.each($(this).serializeArray(), function(i, prop)
{
if (obj.hasOwnProperty(prop.name))
{
// Convert it into an array
if (!$.isArray(obj[prop.name]))
{
obj[prop.name] = [obj[prop.name]];
}
obj[prop.name].push(prop.value);
} else {
obj[prop.name] = prop.value;
}
});
return obj;
};
$.fn.serializeJSON = function() {
return JSON.stringify($(this).serializeObject());
}
/**
* jquery.toHTML
* Decode html entities. Get HTML of element.
*
* https://github.com/jstonne/jquery.toHTML
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.toHTML = function(str) {
return $("<div>").html(str).html();
}
$.fn.toHTML = function() {
return $.toHTML(this.clone());
};/**
* jquery.Bloop
* Binary loop helper.
* https://github.com/jstonne/jquery.Bloop
*
* Copyright (c) 2012 Jensen Tonne & Jason Rey
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function(){
var Bloop = function(items) {
this.items = items;
this.start = 0;
this.end = items.length - 1;
this.node = null;
this.stopped = false;
};
$.extend(Bloop.prototype, {
isLooping: function() {
if (this.stopped) return false;
if (Math.abs(this.start - this.end) > 1) {
this.node = Math.floor((this.start + this.end) / 2);
return true;
}
return false;
},
flip: function(flip) {
if (flip) {
this.end = this.node - 1;
} else {
this.start = this.node + 1;
}
},
stop: function() {
this.stop = true;
}
});
$.Bloop = function(items){
return new Bloop(items);
}
})();
/**
* jquery.remap
* Utility for remapping properties of an object selectively from another object.
* https://github.com/jstonne/jquery.remap
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.remap = function(to, from, props) {
$.each(props, function(i, prop){
to[prop] = from[prop];
});
return obj;
};
/**
* jquery.deletes
* A mass delete version of the native javascript delete method.
* https://github.com/jstonne/jquery.deletes
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.deletes = function(obj, props) {
$.each(props, function(i, prop){
delete obj[prop];
});
};
/**
* jquery.Threads
* A manager that controls threads a.k.a. execution of function simultaneously.
* https://github.com/jstonne/jquery.Threads
*
* Copyright (c) 2012 Jensen Tonne & Jason Rey
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function() {
var Threads = function(options) {
this.threads = [];
this.threadCount = 0;
this.threadLimit = options.threadLimit || 1;
this.threadDelay = options.threadDelay || 0;
}
$.extend(Threads.prototype, {
add: function(thread, type) {
if (!$.isFunction(thread)) return;
thread.type = type || "normal";
if (type=="deferred") {
thread.deferred = $.Deferred().always($.proxy(this.next, this));
}
this.threads.push(thread);
this.run();
},
addDeferred: function(thread) {
return this.add(thread, "deferred");
},
next: function() {
// Reduce thread count
this.threadCount--;
// And see if there's anymore task to run
this.run();
},
run: function() {
var self = this;
setTimeout(function(){
if (self.threads.length < 1) return;
if (self.threadCount < self.threadLimit) {
self.threadCount++;
var thread = self.threads.shift();
// Wrap in a try catch in case if the thread
// throws an error it doesn't break our chain.
try { thread.call(thread, thread.deferred); }
catch(e) { console.error(e); }
!thread.deferred && self.next();
}
}, self.threadDelay);
}
});
$.Threads = function(options) {
return new Threads(options);
};
})();
/**
* jquery.Enqueue
* Execute only the last added callback.
* https://github.com/jstonne/jquery.Enqueue
*
* Copyright (c) 2012 Jensen Tonne & Jason Rey
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function() {
var isFunction = $.isFunction;
var Enqueue = function() {
this.lastId = 0;
};
Enqueue.prototype.queue = function(filter) {
var self = this,
id = $.uid();
self.lastId = id;
return function() {
if (self.lastId===id) {
var args = arguments,
args = (isFunction(filter)) ? filter.apply(this, args) : args;
return (isFunction(self.fn)) ? self.fn.apply(this, args) : args;
}
}
};
$.Enqueue = function(fn) {
var self = new Enqueue();
if ($.isFunction(fn)) self.fn = fn;
var func = $.proxy(self.queue, self);
func.reset = function() {
self.lastId = 0;
};
return func;
};
})();
/**
* jquery.eventable
* Extend objects with events.
* https://github.com/jstonne/jquery.eventable
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function() {
var instance = "___eventable",
publicMethods = ["on", "off", "fire"],
getEventName = function(name){
return name.split(".")[0];
};
var Eventable = function(mode) {
this.fnList = {};
this.events = {};
this.mode = mode;
}
$.extend(
Eventable.prototype,
{
createEvent: function(name) {
return this.events[name] = $.Callbacks(this.mode);
},
on: function(name, fn) {
if (!name || !$.isFunction(fn)) return this;
var fnList = this.fnList;
(fnList[name] || (fnList[name] = [])).push(fn);
// Translate into base event name
var basename = getEventName(name);
// Add the event
(this.events[basename] || this.createEvent(basename)).add(fn);
return this;
},
off: function(name) {
if (!name) return this;
var basename = getEventName(name),
event = this.events[basename];
if (!event) return this;
var removeCallbacks = function(fnList) {
$.each(fnList, function(i, fn) {
event.remove(fn);
});
}
if (basename!==name) {
$.each(this.fnList, function(name, fnList) {
if (name.indexOf(basename) > -1) {
removeCallbacks(fnList);
}
});
} else {
removeCallbacks(this.fnList[name]);
}
return this;
},
fire: function(name) {
var event = this.events[name];
if (!event) return;
event.fire.apply(event, $.makeArray(arguments).slice(1));
return this;
},
destroy: function() {
for (name in this.events) {
this.events[name].disable();
}
}
}
);
$.eventable = function(obj, mode) {
var eventable = obj[instance];
if (eventable && mode==="destroy") {
eventable.destroy();
$.deletes(obj, publicMethods);
return delete obj[instance];
}
eventable = obj[instance] = new Eventable(mode);
obj.on = $.proxy(eventable.on, eventable);
obj.off = $.proxy(eventable.off, eventable);
obj.fire = $.proxy(eventable.fire, eventable);
return obj;
}
})();
/*!
* jquery.Chunk.
* Utility to handle large arrays by processing them in smaller manageable chunks.
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.Chunk = function(array, options) {
if ($.isArray(array)) {
array = [];
}
var options = $.extend({},
{
size: 256,
every: 1000
},
options
);
var self = $.extend($.Deferred(), {
size: options.size,
every: options.every,
from: 0,
to: array.length,
process: function(callback) {
self.process.fn = callback;
return self;
},
chunkStart: function(callback) {
self.chunkStart.fn = callback;
return self;
},
chunkEnd: function(callback) {
self.chunkEnd.fn = callback;
return self;
},
start: function() {
self.stopped = false;
self.iterate();
return self;
},
iterate: function() {
if (self.stopped) return;
var iterator = self.process.fn;
if (!iterator) return;
self.to = from.size + self.size;
var max = array.length;
if (self.to > max) {
self.to = max;
}
var range = {from: self.from, to: self.to};
// Trigger chunkStart event
self.chunkStart.fn && self.chunkStart.fn.call(self, range.from, range.to);
while (self.from < self.to) {
if (self.stopped) break;
iterator.call(self, array[self.from]);
self.from++;
}
// Trigger chunkEnd event
self.chunkEnd.fn && self.chunkEnd.fn.call(self, range.from, range.to);
// Always get the latest array length because
// it may change through iteration
self.completed = (self.from >= array.length - 1);
if (self.completed) {
self.resolveWith(self);
} else {
self.nextIteration = setTimeout(self.iterate, self.every);
}
return self;
},
pause: function() {
self.stopped = true;
clearTimeout(self.nextIteration);
return self;
},
restart: function() {
if (self.state()==="rejected") return self;
self.from = 0;
self.start();
return self;
},
stop: function() {
self.pause();
self.rejectWith(self, [self.from]);
return self;
}
});
return self;
};
/**
* jquery.disabled
* Checks if element is disabled and adds a disable class
*
*/
$.fn.disabled = function(state) {
return (state===undefined) ?
(this.is(":disabled") || this.hasClass('disabled')) :
this.prop('disabled', !!state).toggleClass("disabled", !!state);
};
$.fn.enabled = function(state) {
return (state===undefined) ? !this.disabled() : this.disabled(!state);
};
/**
* jquery.throttledAjax
* jQuery AJAX with throttling.
* https://github.com/jstonne/jquery.throttledAjax
*
* Requires jquery.Threads
* https://github.com/jstonne/jquery.Threads
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(function(){
var self = $.Ajax = function(options) {
var request = $.Deferred(),
args = arguments;
// Allow others to decorate the request object
$.isPlainObject(options) && $.isFunction(options.beforeCreate) && options.beforeCreate(request);
self.queue.addDeferred(function(queue){
request.xhr =
$.ajax.apply(null, args)
.pipe(
request.resolve,
request.reject,
request.notify
);
// Mark this queue as resolved
setTimeout(queue.resolve, self.requestInterval);
});
return request;
}
self.queue = $.Threads({threadLimit: 1});
self.requestInterval = 1200;
})();
$.callback = function(func, persist){
// Create callback
if ($.isFunction(func)) {
var funcName = $.uid("cb");
window[funcName] = function(){
// Destroy itself after callback has been called
if (!persist) {
delete window[funcName];
}
return func.apply(null, arguments);
}
return funcName;
}
// Callback method
if ($.isString(func)) {
switch (func) {
case "destroy":
var funcName = persist;
delete window[funcName];
break;
}
}
};$.fn.visible = function(partial){
var $t = $(this),
$w = $(window);
if ($t.length < 1) return;
var viewTop = $w.scrollTop(),
viewBottom = viewTop + $w.height(),
_top = $t.offset().top,
_bottom = _top + $t.height(),
compareTop = partial === true ? _bottom : _top,
compareBottom = partial === true ? _top : _bottom;
return ((compareBottom <= viewBottom) && (compareTop >= viewTop));
};
$.fn.selectAll = function() {
return this.each(function(){ this.select() });
};
$.fn.unselect = function() {
return this.each(function(){
var input = this,
value = input.value;
input.value += " ";
input.value = value;
});
};$.fn.addTransitionClass = function(classname, duration){
var $el = this.addClass(classname);
setTimeout(function(){
$el.removeClass(classname);
}, duration || 0);
return this;
};
$.fn.switchClass = function(classname, delimiter){
var delimiter = delimiter || "-",
prefix = classname.split(delimiter)[0] + delimiter,
length = prefix.length;
return this.each(function(){
var $el = $(this),
classnames =
$.map($el.attr("class").split(' '), function(classname){
return (classname.slice(0, length)==prefix || classname=="") ? null : classname;
});
classnames.push(classname);
$el.attr("class", classnames.join(" "));
});
};
$.sanitizeHTML = function(html) {
return $($.parseHTML(html, document, true)).toHTML();
};$.buildHTML = function(html, keepScripts) {
// If a jquery element was passed in, return as it is.
if (html instanceof $) return html;
// Trim out any whitespace so no unusable text nodes are introduced.
var html = $.trim(html),
// Build html fragment while keeping a separate reference to the script
scripts = [],
fragment = $.buildFragment([html], document, scripts),
// Convert childNodes into a proper array
nodes = $.merge([], fragment.childNodes);
// If we want to remove the script after
// it is appended to the DOM & executed
if (!keepScripts && scripts.length > 0) {
// Create script remover
var script = document.createElement("script");
script.text = $.callback(function(){$(scripts).remove();}) + "();";
// Go through nodes in reverse
var i = nodes.length-1, node, inserted;
while (node = nodes[i--]) {
// If a script node is found first, we'll just append
// script remover next to it to ensure this last script
// executes before any script removal happens.
if (node.nodeName==="SCRIPT") {
inserted = nodes.push(script);
} else if (node.nodeType===1) {
inserted = node.appendChild(script);
}
if (inserted) break;
}
// If script remover was not inserted,
// then just add it to the array of nodes
if (!inserted) nodes.push(script);
// Add script remover itself to the
// array of scripts to be removed.
scripts.push(script);
}
// Convert nodes into jquery instance and return
return $(nodes);
};$.fn.filterBy = function(key, val, operator) {
var operator = operator || "=",
selector = "[data-" + key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() + operator + val + "]";
return this.filter(selector);
}
$.intersects = function(a, b) {
if ($.isArray(b)) {
b = {top: b.y, left: b.x, bottom: b.y, right: b.x}
}
return (
b.left <= a.right &&
a.left <= b.right &&
b.top <= a.bottom &&
a.top <= b.bottom
);
};
$.fn.intersectsWith = function(top, left, width, height) {
// TODO: intersectsWith(element)
var offset = this.offset(),
reference = {
top : offset.top,
left : offset.left,
bottom: offset.top + (sourceHeight = this.height()),
right : offset.left + (sourceWidth = this.width()),
width : sourceWidth,
height: sourceHeight
},
subject = {
top : top,
left : left,
bottom: top + (height || (height = 0)),
right : left + (width || (width = 0)),
width : width,
height: height
};
return ($.intersects(reference, subject)) ? {reference: reference, subject: subject} : false;
};$.download = function(src) {
return $("<iframe>").hide().appendTo("body").bind("load", function(){$(this).remove()}).attr("src", src);
};
});
FD31.plugin("uri", function($) {
$.isUrl = function(s)
{
var regexp = /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
return regexp.test(s);
};
var Query = function (queryString) {
// query string parsing, parameter manipulation and stringification
'use strict';
var // parseQuery(q) parses the uri query string and returns a multi-dimensional array of the components
parseQuery = function (q) {
var arr = [], i, ps, p, kvp, k, v;
if (typeof (q) === 'undefined' || q === null || q === '') {
return arr;
}
if (q.indexOf('?') === 0) {
q = q.substring(1);
}
ps = q.toString().split(/[&;]/);
for (i = 0; i < ps.length; i++) {
p = ps[i];
kvp = p.split('=');
k = kvp[0];
v = p.indexOf('=') === -1 ? null : (kvp[1] === null ? '' : kvp[1]);
arr.push([k, v]);
}
return arr;
},
params = parseQuery(queryString),
// toString() returns a string representation of the internal state of the object
toString = function () {
var s = '', i, param;
for (i = 0; i < params.length; i++) {
param = params[i];
if (s.length > 0) {
s += '&';
}
if (param[1] === null) {
s += param[0];
}
else {
s += param.join('=');
}
}
return s.length > 0 ? '?' + s : s;
},
decode = function (s) {
s = decodeURIComponent(s);
s = s.replace('+', ' ');
return s;
},
// getParamValues(key) returns the first query param value found for the key 'key'
getParamValue = function (key) {
var param, i;
for (i = 0; i < params.length; i++) {
param = params[i];
if (decode(key) === decode(param[0])) {
return param[1];
}
}
},
// getParamValues(key) returns an array of query param values for the key 'key'
getParamValues = function (key) {
var arr = [], i, param;
for (i = 0; i < params.length; i++) {
param = params[i];
if (decode(key) === decode(param[0])) {
arr.push(param[1]);
}
}
return arr;
},
// deleteParam(key) removes all instances of parameters named (key)
// deleteParam(key, val) removes all instances where the value matches (val)
deleteParam = function (key, val) {
var arr = [], i, param, keyMatchesFilter, valMatchesFilter;
for (i = 0; i < params.length; i++) {
param = params[i];
keyMatchesFilter = decode(param[0]) === decode(key);
valMatchesFilter = decode(param[1]) === decode(val);
if ((arguments.length === 1 && !keyMatchesFilter) || (arguments.length === 2 && !keyMatchesFilter && !valMatchesFilter)) {
arr.push(param);
}
}
params = arr;
return this;
},
// addParam(key, val) Adds an element to the end of the list of query parameters
// addParam(key, val, index) adds the param at the specified position (index)
addParam = function (key, val, index) {
if (arguments.length === 3 && index !== -1) {
index = Math.min(index, params.length);
params.splice(index, 0, [key, val]);
} else if (arguments.length > 0) {
params.push([key, val]);
}
return this;
},
// replaceParam(key, newVal) deletes all instances of params named (key) and replaces them with the new single value
// replaceParam(key, newVal, oldVal) deletes only instances of params named (key) with the value (val) and replaces them with the new single value
// this function attempts to preserve query param ordering
replaceParam = function (key, newVal, oldVal) {
var index = -1, i, param;
if (arguments.length === 3) {
for (i = 0; i < params.length; i++) {
param = params[i];
if (decode(param[0]) === decode(key) && decodeURIComponent(param[1]) === decode(oldVal)) {
index = i;
break;
}
}
deleteParam(key, oldVal).addParam(key, newVal, index);
} else {
for (i = 0; i < params.length; i++) {
param = params[i];
if (decode(param[0]) === decode(key)) {
index = i;
break;
}
}
deleteParam(key);
addParam(key, newVal, index);
}
return this;
};
// public api
return {
getParamValue: getParamValue,
getParamValues: getParamValues,
deleteParam: deleteParam,
addParam: addParam,
replaceParam: replaceParam,
toString: toString
};
};
var Uri = function (uriString) {
// uri string parsing, attribute manipulation and stringification
'use strict';
/*global Query: true */
/*jslint regexp: false, plusplus: false */
var strictMode = false,
urlExtractor = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi,
// parseUri(str) parses the supplied uri and returns an object containing its components
parseUri = function (str) {
/*jslint unparam: true */
var parsers = {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
},
keys = ["source", "protocol", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "anchor"],
q = {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
m = parsers[strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) {
uri[keys[i]] = m[i] || "";
}
uri[q.name] = {};
uri[keys[12]].replace(q.parser, function ($0, $1, $2) {
if ($1) {
uri[q.name][$1] = $2;
}
});
return uri;
},
uriParts = parseUri(uriString || ''),
queryObj = new Query(uriParts.query),
/*
Basic get/set functions for all properties
*/
protocol = function (val) {
if (typeof val !== 'undefined') {
uriParts.protocol = val;
}
return uriParts.protocol;
},
hasAuthorityPrefixUserPref = null,
// hasAuthorityPrefix: if there is no protocol, the leading // can be enabled or disabled
hasAuthorityPrefix = function (val) {
if (typeof val !== 'undefined') {
hasAuthorityPrefixUserPref = val;
}
if (hasAuthorityPrefixUserPref === null) {
return (uriParts.source.indexOf('//') !== -1);
} else {
return hasAuthorityPrefixUserPref;
}
},
userInfo = function (val) {
if (typeof val !== 'undefined') {
uriParts.userInfo = val;
}
return uriParts.userInfo;
},
host = function (val) {
if (typeof val !== 'undefined') {
uriParts.host = val;
}
return uriParts.host;
},
port = function (val) {
if (typeof val !== 'undefined') {
uriParts.port = val;
}
return uriParts.port;
},
path = function (val) {
if (typeof val !== 'undefined') {
uriParts.path = val;
}
return uriParts.path;
},
query = function (val) {
if (typeof val !== 'undefined') {
queryObj = new Query(val);
}
return queryObj;
},
anchor = function (val) {
if (typeof val !== 'undefined') {
uriParts.anchor = val;
}
return uriParts.anchor;
},
/*
Fluent setters for Uri uri properties
*/
setProtocol = function (val) {
protocol(val);
return this;
},
setHasAuthorityPrefix = function (val) {
hasAuthorityPrefix(val);
return this;
},
setUserInfo = function (val) {
userInfo(val);
return this;
},
setHost = function (val) {
host(val);
return this;
},
setPort = function (val) {
port(val);
return this;
},
setPath = function (val) {
path(val);
return this;
},
setQuery = function (val) {
query(val);
return this;
},
setAnchor = function (val) {
anchor(val);
return this;
},
/*
Query method wrappers
*/
getQueryParamValue = function (key) {
return query().getParamValue(key);
},
getQueryParamValues = function (key) {
return query().getParamValues(key);
},
deleteQueryParam = function (key, val) {
if (arguments.length === 2) {
query().deleteParam(key, val);
} else {
query().deleteParam(key);
}
return this;
},
addQueryParam = function (key, val, index) {
if (arguments.length === 3) {
query().addParam(key, val, index);
} else {
query().addParam(key, val);
}
return this;
},
replaceQueryParam = function (key, newVal, oldVal) {
if (arguments.length === 3) {
query().replaceParam(key, newVal, oldVal);
} else {
query().replaceParam(key, newVal);
}
return this;
},
/*
Converters
*/
// toPath() converts a relative path into its absolute path, e.g.
//
// Current path: /foo/bar/today
// Relative path: ../tomorrow
// Result: /foo/bar/tomorrow
toPath = function (val) {
if (val===undefined) {
return uriParts.path;
}
// If relative path starts with '/'
if (val.substring(0,1)=='/') {
return uriParts.path = val;
}
var base_path = uriParts.path.split('/'),
rel_path = val.split('/');
if (base_path.slice(-1)[0]==='') {
base_path.pop();
}
var part;
while (part = rel_path.shift()) {
switch (part) {
case '..':
if (base_path.length > 1) {
base_path.pop();
}
break;
case '.':
// skip
break;
default:
base_path.push(part);
}
}
uriParts.path = base_path.join('/');
return this;
},
/*
Serialization
*/
// toString() stringifies the current state of the uri
toString = function () {
var s = '',
is = function (s) {
return (s !== null && s !== '');
};
if (is(protocol())) {
s += protocol();
if (protocol().indexOf(':') !== protocol().length - 1) {
s += ':';
}
s += '//';
} else {
if (hasAuthorityPrefix() && is(host())) {
s += '//';
}
}
if (is(userInfo()) && is(host())) {
s += userInfo();
if (userInfo().indexOf('@') !== userInfo().length - 1) {
s += '@';
}
}
if (is(host())) {
s += host();
if (is(port())) {
s += ':' + port();
}
}
if (is(path())) {
s += path();
} else {
if (is(host()) && (is(query().toString()) || is(anchor()))) {
s += '/';
}
}
if (is(query().toString())) {
if (query().toString().indexOf('?') !== 0) {
s += '?';
}
s += query().toString();
}
if (is(anchor())) {
if (anchor().indexOf('#') !== 0) {
s += '#';
}
s += anchor();
}
return s;
},
extract = function(i) {
var urls = uriString.match(urlExtractor) || [];
return (i===undefined) ? urls : (urls[i] || "");
},
/*
Cloning
*/
// clone() returns a new, identical Uri instance
clone = function () {
return new Uri(toString());
};
// public api
return {
protocol: protocol,
hasAuthorityPrefix: hasAuthorityPrefix,
userInfo: userInfo,
host: host,
port: port,
path: path,
query: query,
anchor: anchor,
setProtocol: setProtocol,
setHasAuthorityPrefix: setHasAuthorityPrefix,
setUserInfo: setUserInfo,
setHost: setHost,
setPort: setPort,
setPath: setPath,
setQuery: setQuery,
setAnchor: setAnchor,
getQueryParamValue: getQueryParamValue,
getQueryParamValues: getQueryParamValues,
deleteQueryParam: deleteQueryParam,
addQueryParam: addQueryParam,
replaceQueryParam: replaceQueryParam,
extract: extract,
toPath: toPath,
toString: toString,
clone: clone
};
};
$.uri = function (s) {
return new Uri(s);
}
});
FD31.plugin("mvc", function($) {
(function(){
// Several of the methods in this plugin use code adapated from Prototype
// Prototype JavaScript framework, version 1.6.0.1
// (c) 2005-2007 Sam Stephenson
var regs = {
undHash: /_|-/,
colons: /::/,
words: /([A-Z]+)([A-Z][a-z])/g,
lowUp: /([a-z\d])([A-Z])/g,
dash: /([a-z\d])([A-Z])/g,
replacer: /\{([^\}]+)\}/g,
dot: /\./
},
// gets the nextPart property from current
// add - if true and nextPart doesnt exist, create it as an empty object
getNext = function(current, nextPart, add){
return current[nextPart] !== undefined ? current[nextPart] : ( add && (current[nextPart] = {}) );
},
// returns true if the object can have properties (no nulls)
isContainer = function(current){
var type = typeof current;
return current && ( type == 'function' || type == 'object' );
},
// a reference
getObject,
/**
* @class jQuery.String
* @parent jquerymx.lang
*
* A collection of useful string helpers. Available helpers are:
* <ul>
* <li>[jQuery.String.capitalize|capitalize]: Capitalizes a string (some_string » Some_string)</li>
* <li>[jQuery.String.camelize|camelize]: Capitalizes a string from something undercored
* (some_string » someString, some-string » someString)</li>
* <li>[jQuery.String.classize|classize]: Like [jQuery.String.camelize|camelize],
* but the first part is also capitalized (some_string » SomeString)</li>
* <li>[jQuery.String.niceName|niceName]: Like [jQuery.String.classize|classize], but a space separates each 'word' (some_string » Some String)</li>
* <li>[jQuery.String.underscore|underscore]: Underscores a string (SomeString » some_string)</li>
* <li>[jQuery.String.sub|sub]: Returns a string with {param} replaced values from data.
* <code><pre>
* $.String.sub("foo {bar}",{bar: "far"})
* //-> "foo far"</pre></code>
* </li>
* </ul>
*
*/
str = $.String = $.extend( $.String || {} , {
/**
* @function getObject
* Gets an object from a string. It can also modify objects on the
* 'object path' by removing or adding properties.
*
* Foo = {Bar: {Zar: {"Ted"}}}
* $.String.getObject("Foo.Bar.Zar") //-> "Ted"
*
* @param {String} name the name of the object to look for
* @param {Array} [roots] an array of root objects to look for the
* name. If roots is not provided, the window is used.
* @param {Boolean} [add] true to add missing objects to
* the path. false to remove found properties. undefined to
* not modify the root object
* @return {Object} The object.
*/
getObject : getObject = function( name, roots, add ) {
// the parts of the name we are looking up
// ['App','Models','Recipe']
var parts = name ? name.split(regs.dot) : [],
length = parts.length,
current,
ret,
i,
r = 0,
type;
// make sure roots is an array
roots = $.isArray(roots) ? roots : [roots || window];
if(length == 0){
return roots[0];
}
// for each root, mark it as current
while( current = roots[r++] ) {
// walk current to the 2nd to last object
// or until there is not a container
for (i =0; i < length - 1 && isContainer(current); i++ ) {
current = getNext(current, parts[i], add);
}
// if we can get a property from the 2nd to last object
if( isContainer(current) ) {
// get (and possibly set) the property
ret = getNext(current, parts[i], add);
// if there is a value, we exit
if( ret !== undefined ) {
// if add is false, delete the property
if ( add === false ) {
delete current[parts[i]];
}
return ret;
}
}
}
},
/**
* Capitalizes a string
* @param {String} s the string.
* @return {String} a string with the first character capitalized.
*/
capitalize: function( s, cache ) {
return s.charAt(0).toUpperCase() + s.substr(1);
},
/**
* Capitalizes a string from something undercored. Examples:
* @codestart
* jQuery.String.camelize("one_two") //-> "oneTwo"
* "three-four".camelize() //-> threeFour
* @codeend
* @param {String} s
* @return {String} a the camelized string
*/
camelize: function( s ) {
s = str.classize(s);
return s.charAt(0).toLowerCase() + s.substr(1);
},
/**
* Like [jQuery.String.camelize|camelize], but the first part is also capitalized
* @param {String} s
* @return {String} the classized string
*/
classize: function( s , join) {
var parts = s.split(regs.undHash),
i = 0;
for (; i < parts.length; i++ ) {
parts[i] = str.capitalize(parts[i]);
}
return parts.join(join || '');
},
/**
* Like [jQuery.String.classize|classize], but a space separates each 'word'
* @codestart
* jQuery.String.niceName("one_two") //-> "One Two"
* @codeend
* @param {String} s
* @return {String} the niceName
*/
niceName: function( s ) {
return str.classize(s,' ');
},
/**
* Underscores a string.
* @codestart
* jQuery.String.underscore("OneTwo") //-> "one_two"
* @codeend
* @param {String} s
* @return {String} the underscored string
*/
underscore: function( s ) {
return s.replace(regs.colons, '/').replace(regs.words, '$1_$2').replace(regs.lowUp, '$1_$2').replace(regs.dash, '_').toLowerCase();
},
/**
* Returns a string with {param} replaced values from data.
*
* $.String.sub("foo {bar}",{bar: "far"})
* //-> "foo far"
*
* @param {String} s The string to replace
* @param {Object} data The data to be used to look for properties. If it's an array, multiple
* objects can be used.
* @param {Boolean} [remove] if a match is found, remove the property from the object
*/
sub: function( s, data, remove ) {
var obs = [];
obs.push(s.replace(regs.replacer, function( whole, inside ) {
// !-- FOUNDRY HACK --! //
// Prefer {foobar} over foobar
//convert inside to type
var ob = getObject(whole, data, typeof remove == 'boolean' ? !remove : remove) ||
getObject(inside, data, typeof remove == 'boolean' ? !remove : remove),
type = typeof ob;
if ((type === 'object' || type === 'function') && type !== null) {
obs.push(ob);
return "";
} else {
return ""+ob;
}
}));
return obs.length <= 1 ? obs[0] : obs;
},
_regs : regs
});
})();(function(){
/**
* @add jQuery.String
*/
$.String.
/**
* Splits a string with a regex correctly cross browser
*
* $.String.rsplit("a.b.c.d", /\./) //-> ['a','b','c','d']
*
* @param {String} string The string to split
* @param {RegExp} regex A regular expression
* @return {Array} An array of strings
*/
rsplit = function( string, regex ) {
var result = regex.exec(string),
retArr = [],
first_idx, last_idx;
while ( result !== null ) {
first_idx = result.index;
last_idx = regex.lastIndex;
if ( first_idx !== 0 ) {
retArr.push(string.substring(0, first_idx));
string = string.slice(first_idx);
}
retArr.push(result[0]);
string = string.slice(result[0].length);
result = regex.exec(string);
}
if ( string !== '' ) {
retArr.push(string);
}
return retArr;
};
})();(function(){
var digitTest = /^\d+$/,
keyBreaker = /([^\[\]]+)|(\[\])/g,
plus = /\+/g,
paramTest = /([^?#]*)(#.*)?$/;
/**
* @add jQuery.String
*/
$.String = $.extend($.String || {}, {
/**
* @function deparam
*
* Takes a string of name value pairs and returns a Object literal that represents those params.
*
* @param {String} params a string like <code>"foo=bar&person[age]=3"</code>
* @return {Object} A JavaScript Object that represents the params:
*
* {
* foo: "bar",
* person: {
* age: "3"
* }
* }
*/
deparam: function(params){
if(! params || ! paramTest.test(params) ) {
return {};
}
var data = {},
pairs = params.split('&'),
current;
for(var i=0; i < pairs.length; i++){
current = data;
var pair = pairs[i].split('=');
// if we find foo=1+1=2
if(pair.length != 2) {
pair = [pair[0], pair.slice(1).join("=")]
}
var key = decodeURIComponent(pair[0].replace(plus, " ")),
value = decodeURIComponent(pair[1].replace(plus, " ")),
parts = key.match(keyBreaker);
for ( var j = 0; j < parts.length - 1; j++ ) {
var part = parts[j];
if (!current[part] ) {
// if what we are pointing to looks like an array
current[part] = digitTest.test(parts[j+1]) || parts[j+1] == "[]" ? [] : {}
}
current = current[part];
}
lastPart = parts[parts.length - 1];
if(lastPart == "[]"){
current.push(value)
}else{
current[lastPart] = value;
}
}
return data;
}
});
})();(function(){
/**
* @attribute destroyed
* @parent specialevents
* @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/dom/destroyed/destroyed.js
* @test jquery/event/destroyed/qunit.html
* Provides a destroyed event on an element.
* <p>
* The destroyed event is called when the element
* is removed as a result of jQuery DOM manipulators like remove, html,
* replaceWith, etc. Destroyed events do not bubble, so make sure you don't use live or delegate with destroyed
* events.
* </p>
* <h2>Quick Example</h2>
* @codestart
* $(".foo").bind("destroyed", function(){
* //clean up code
* })
* @codeend
* <h2>Quick Demo</h2>
* @demo jquery/event/destroyed/destroyed.html
* <h2>More Involved Demo</h2>
* @demo jquery/event/destroyed/destroyed_menu.html
*/
var oldClean = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem;
(elem = elems[i]) !== undefined; i++ ) {
$(elem).triggerHandler("destroyed");
//$.event.remove( elem, 'destroyed' );
}
oldClean(elems);
};
})();(function(){
/**
* @function closest
* @parent dom
* @plugin jquery/dom/closest
* Overwrites closest to allow open > selectors. This allows controller
* actions such as:
*
* ">li click" : function( el, ev ) { ... }
*/
var oldClosest = $.fn.closest;
$.fn.closest = function(selectors, context){
var rooted = {}, res, result, thing, i, j, selector, rootedIsEmpty = true, selector, selectorsArr = selectors;
if(typeof selectors == "string") selectorsArr = [selectors];
$.each(selectorsArr, function(i, selector){
if(selector.indexOf(">") == 0 ){
if(selector.indexOf(" ") != -1){
throw " closest does not work with > followed by spaces!"
}
rooted[( selectorsArr[i] = selector.substr(1) )] = selector;
if(typeof selectors == "string") selectors = selector.substr(1);
rootedIsEmpty = false;
}
})
res = oldClosest.call(this, selectors, context);
if(rootedIsEmpty) return res;
i =0;
while(i < res.length){
result = res[i], selector = result.selector;
if (rooted[selector] !== undefined) {
result.selector = rooted[selector];
rooted[selector] = false;
if(typeof result.selector !== "string" || result.elem.parentNode !== context ){
res.splice(i,1);
continue;
}
}
i++;
}
return res;
}
})();(function(){
// break
/**
* @function jQuery.cookie
* @parent dom
* @plugin jquery/dom/cookie
* @author Klaus Hartl/klaus.hartl@stilbuero.de
*
* JavaScriptMVC's packaged cookie plugin is written by
* Klaus Hartl (stilbuero.de)<br />
* Dual licensed under the MIT and GPL licenses:<br />
* http://www.opensource.org/licenses/mit-license.php<br />
* http://www.gnu.org/licenses/gpl.html
* </p>
* <p>
* Create a cookie with the given name and value and other optional parameters.
* / Get the value of a cookie with the given name.
* </p>
* <h3>Quick Examples</h3>
*
* Set the value of a cookie.
*
* $.cookie('the_cookie', 'the_value');
*
* Create a cookie with all available options.
* @codestart
* $.cookie('the_cookie', 'the_value',
* { expires: 7, path: '/', domain: 'jquery.com', secure: true });
* @codeend
*
* Create a session cookie.
* @codestart
* $.cookie('the_cookie', 'the_value');
* @codeend
*
* Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
* used when the cookie was set.
* @codestart
* $.cookie('the_cookie', null);
* @codeend
*
* Get the value of a cookie.
* @codestart
* $.cookie('the_cookie');
* @codeend
*
*
* @param {String} [name] The name of the cookie.
* @param {String} [value] The value of the cookie.
* @param {Object} [options] An object literal containing key/value pairs to provide optional cookie attributes.<br />
* @param {Number|Date} [expires] Either an integer specifying the expiration date from now on in days or a Date object.
* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
* If set to null or omitted, the cookie will be a session cookie and will not be retained
* when the the browser exits.<br />
* @param {String} [path] The value of the path atribute of the cookie (default: path of page that created the cookie).<br />
* @param {String} [domain] The value of the domain attribute of the cookie (default: domain of page that created the cookie).<br />
* @param {Boolean} secure If true, the secure attribute of the cookie will be set and the cookie transmission will
* require a secure protocol (like HTTPS).<br />
* @return {String} the value of the cookie or {undefined} when setting the cookie.
*/
$.cookie = function(name, value, options) {
if (typeof value != 'undefined') { // name and value given, set cookie
options = options ||
{};
if (value === null) {
value = '';
options.expires = -1;
}
if (typeof value == 'object' && jQuery.toJSON) {
value = jQuery.toJSON(value);
}
var expires = '';
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
var date;
if (typeof options.expires == 'number') {
date = new Date();
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
}
else {
date = options.expires;
}
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
}
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
// in the packed version for some reason...
var path = options.path ? '; path=' + (options.path) : '';
var domain = options.domain ? '; domain=' + (options.domain) : '';
var secure = options.secure ? '; secure' : '';
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
}
else { // only name given, get cookie
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
if (jQuery.evalJSON && cookieValue && cookieValue.match(/^\s*\{/)) {
try {
cookieValue = jQuery.evalJSON(cookieValue);
}
catch (e) {
}
}
return cookieValue;
}
};
})();(function(){
// =============== HELPERS =================
// if we are initializing a new class
var initializing = false,
makeArray = $.makeArray,
isFunction = $.isFunction,
isArray = $.isArray,
extend = $.extend,
getObject = $.String.getObject,
concatArgs = function(arr, args){
return arr.concat(makeArray(args));
},
// tests if we can get super in .toString()
fnTest = /xyz/.test(function() {
xyz;
}) ? /\b_super\b/ : /.*/,
// overwrites an object with methods, sets up _super
// newProps - new properties
// oldProps - where the old properties might be
// addTo - what we are adding to
inheritProps = function( newProps, oldProps, addTo ) {
addTo = addTo || newProps
for ( var name in newProps ) {
// Check if we're overwriting an existing function
addTo[name] = isFunction(newProps[name]) &&
isFunction(oldProps[name]) &&
fnTest.test(newProps[name]) ? (function( name, fn ) {
return function() {
var tmp = this._super,
ret;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = oldProps[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, newProps[name]) : newProps[name];
}
},
STR_PROTOTYPE = 'prototype'
/**
* @class jQuery.Class
* @plugin jquery/class
* @parent jquerymx
* @download dist/jquery/jquery.class.js
* @test jquery/class/qunit.html
* @description Easy inheritance in JavaScript.
*
* Class provides simulated inheritance in JavaScript. Use clss to bridge the gap between
* jQuery's functional programming style and Object Oriented Programming. It
* is based off John Resig's [http://ejohn.org/blog/simple-javascript-inheritance/|Simple Class]
* Inheritance library. Besides prototypal inheritance, it includes a few important features:
*
* - Static inheritance
* - Introspection
* - Namespaces
* - Setup and initialization methods
* - Easy callback function creation
*
*
* The [mvc.class Get Started with jQueryMX] has a good walkthrough of $.Class.
*
* ## Static v. Prototype
*
* Before learning about Class, it's important to
* understand the difference between
* a class's __static__ and __prototype__ properties.
*
* //STATIC
* MyClass.staticProperty //shared property
*
* //PROTOTYPE
* myclass = new MyClass()
* myclass.prototypeMethod() //instance method
*
* A static (or class) property is on the Class constructor
* function itself
* and can be thought of being shared by all instances of the
* Class. Prototype propertes are available only on instances of the Class.
*
* ## A Basic Class
*
* The following creates a Monster class with a
* name (for introspection), static, and prototype members.
* Every time a monster instance is created, the static
* count is incremented.
*
* @codestart
* $.Class('Monster',
* /* @static *|
* {
* count: 0
* },
* /* @prototype *|
* {
* init: function( name ) {
*
* // saves name on the monster instance
* this.name = name;
*
* // sets the health
* this.health = 10;
*
* // increments count
* this.constructor.count++;
* },
* eat: function( smallChildren ){
* this.health += smallChildren;
* },
* fight: function() {
* this.health -= 2;
* }
* });
*
* hydra = new Monster('hydra');
*
* dragon = new Monster('dragon');
*
* hydra.name // -> hydra
* Monster.count // -> 2
* Monster.shortName // -> 'Monster'
*
* hydra.eat(2); // health = 12
*
* dragon.fight(); // health = 8
*
* @codeend
*
*
* Notice that the prototype <b>init</b> function is called when a new instance of Monster is created.
*
*
* ## Inheritance
*
* When a class is extended, all static and prototype properties are available on the new class.
* If you overwrite a function, you can call the base class's function by calling
* <code>this._super</code>. Lets create a SeaMonster class. SeaMonsters are less
* efficient at eating small children, but more powerful fighters.
*
*
* Monster("SeaMonster",{
* eat: function( smallChildren ) {
* this._super(smallChildren / 2);
* },
* fight: function() {
* this.health -= 1;
* }
* });
*
* lockNess = new SeaMonster('Lock Ness');
* lockNess.eat(4); //health = 12
* lockNess.fight(); //health = 11
*
* ### Static property inheritance
*
* You can also inherit static properties in the same way:
*
* $.Class("First",
* {
* staticMethod: function() { return 1;}
* },{})
*
* First("Second",{
* staticMethod: function() { return this._super()+1;}
* },{})
*
* Second.staticMethod() // -> 2
*
* ## Namespaces
*
* Namespaces are a good idea! We encourage you to namespace all of your code.
* It makes it possible to drop your code into another app without problems.
* Making a namespaced class is easy:
*
*
* $.Class("MyNamespace.MyClass",{},{});
*
* new MyNamespace.MyClass()
*
*
* <h2 id='introspection'>Introspection</h2>
*
* Often, it's nice to create classes whose name helps determine functionality. Ruby on
* Rails's [http://api.rubyonrails.org/classes/ActiveRecord/Base.html|ActiveRecord] ORM class
* is a great example of this. Unfortunately, JavaScript doesn't have a way of determining
* an object's name, so the developer must provide a name. Class fixes this by taking a String name for the class.
*
* $.Class("MyOrg.MyClass",{},{})
* MyOrg.MyClass.shortName //-> 'MyClass'
* MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
*
* The fullName (with namespaces) and the shortName (without namespaces) are added to the Class's
* static properties.
*
*
* ## Setup and initialization methods
*
* <p>
* Class provides static and prototype initialization functions.
* These come in two flavors - setup and init.
* Setup is called before init and
* can be used to 'normalize' init's arguments.
* </p>
* <div class='whisper'>PRO TIP: Typically, you don't need setup methods in your classes. Use Init instead.
* Reserve setup methods for when you need to do complex pre-processing of your class before init is called.
*
* </div>
* @codestart
* $.Class("MyClass",
* {
* setup: function() {} //static setup
* init: function() {} //static constructor
* },
* {
* setup: function() {} //prototype setup
* init: function() {} //prototype constructor
* })
* @codeend
*
* ### Setup
*
* Setup functions are called before init functions. Static setup functions are passed
* the base class followed by arguments passed to the extend function.
* Prototype static functions are passed the Class constructor
* function arguments.
*
* If a setup function returns an array, that array will be used as the arguments
* for the following init method. This provides setup functions the ability to normalize
* arguments passed to the init constructors. They are also excellent places
* to put setup code you want to almost always run.
*
*
* The following is similar to how [jQuery.Controller.prototype.setup]
* makes sure init is always called with a jQuery element and merged options
* even if it is passed a raw
* HTMLElement and no second parameter.
*
* $.Class("jQuery.Controller",{
* ...
* },{
* setup: function( el, options ) {
* ...
* return [$(el),
* $.extend(true,
* this.Class.defaults,
* options || {} ) ]
* }
* })
*
* Typically, you won't need to make or overwrite setup functions.
*
* ### Init
*
* Init functions are called after setup functions.
* Typically, they receive the same arguments
* as their preceding setup function. The Foo class's <code>init</code> method
* gets called in the following example:
*
* $.Class("Foo", {
* init: function( arg1, arg2, arg3 ) {
* this.sum = arg1+arg2+arg3;
* }
* })
* var foo = new Foo(1,2,3);
* foo.sum //-> 6
*
* ## Proxies
*
* Similar to jQuery's proxy method, Class provides a
* [jQuery.Class.static.proxy proxy]
* function that returns a callback to a method that will always
* have
* <code>this</code> set to the class or instance of the class.
*
*
* The following example uses this.proxy to make sure
* <code>this.name</code> is available in <code>show</code>.
*
* $.Class("Todo",{
* init: function( name ) {
* this.name = name
* },
* get: function() {
* $.get("/stuff",this.proxy('show'))
* },
* show: function( txt ) {
* alert(this.name+txt)
* }
* })
* new Todo("Trash").get()
*
* Callback is available as a static and prototype method.
*
* ## Demo
*
* @demo jquery/class/class.html
*
*
* @constructor
*
* To create a Class call:
*
* $.Class( [NAME , STATIC,] PROTOTYPE ) -> Class
*
* <div class='params'>
* <div class='param'><label>NAME</label><code>{optional:String}</code>
* <p>If provided, this sets the shortName and fullName of the
* class and adds it and any necessary namespaces to the
* window object.</p>
* </div>
* <div class='param'><label>STATIC</label><code>{optional:Object}</code>
* <p>If provided, this creates static properties and methods
* on the class.</p>
* </div>
* <div class='param'><label>PROTOTYPE</label><code>{Object}</code>
* <p>Creates prototype methods on the class.</p>
* </div>
* </div>
*
* When a Class is created, the static [jQuery.Class.static.setup setup]
* and [jQuery.Class.static.init init] methods are called.
*
* To create an instance of a Class, call:
*
* new Class([args ... ]) -> instance
*
* The created instance will have all the
* prototype properties and methods defined by the PROTOTYPE object.
*
* When an instance is created, the prototype [jQuery.Class.prototype.setup setup]
* and [jQuery.Class.prototype.init init] methods
* are called.
*/
clss = $.Class = function() {
if (arguments.length) {
clss.extend.apply(clss, arguments);
}
};
/* @Static*/
extend(clss, {
/**
* @function proxy
* Returns a callback function for a function on this Class.
* Proxy ensures that 'this' is set appropriately.
* @codestart
* $.Class("MyClass",{
* getData: function() {
* this.showing = null;
* $.get("data.json",this.proxy('gotData'),'json')
* },
* gotData: function( data ) {
* this.showing = data;
* }
* },{});
* MyClass.showData();
* @codeend
* <h2>Currying Arguments</h2>
* Additional arguments to proxy will fill in arguments on the returning function.
* @codestart
* $.Class("MyClass",{
* getData: function( <b>callback</b> ) {
* $.get("data.json",this.proxy('process',<b>callback</b>),'json');
* },
* process: function( <b>callback</b>, jsonData ) { //callback is added as first argument
* jsonData.processed = true;
* callback(jsonData);
* }
* },{});
* MyClass.getData(showDataFunc)
* @codeend
* <h2>Nesting Functions</h2>
* Proxy can take an array of functions to call as
* the first argument. When the returned callback function
* is called each function in the array is passed the return value of the prior function. This is often used
* to eliminate currying initial arguments.
* @codestart
* $.Class("MyClass",{
* getData: function( callback ) {
* //calls process, then callback with value from process
* $.get("data.json",this.proxy(['process2',callback]),'json')
* },
* process2: function( type,jsonData ) {
* jsonData.processed = true;
* return [jsonData];
* }
* },{});
* MyClass.getData(showDataFunc);
* @codeend
* @param {String|Array} fname If a string, it represents the function to be called.
* If it is an array, it will call each function in order and pass the return value of the prior function to the
* next function.
* @return {Function} the callback function.
*/
proxy: function( funcs ) {
//args that should be curried
var args = makeArray(arguments),
self;
// get the functions to callback
funcs = args.shift();
// if there is only one function, make funcs into an array
if (!isArray(funcs) ) {
funcs = [funcs];
}
// keep a reference to us in self
self = this;
return function class_cb() {
// add the arguments after the curried args
var cur = concatArgs(args, arguments),
isString,
length = funcs.length,
f = 0,
func;
// go through each function to call back
for (; f < length; f++ ) {
func = funcs[f];
if (!func ) {
continue;
}
// set called with the name of the function on self (this is how this.view works)
isString = typeof func == "string";
if ( isString && self._set_called ) {
self.called = func;
}
// call the function
cur = (isString ? self[func] : func).apply(self, cur || []);
// pass the result to the next function (if there is a next function)
if ( f < length - 1 ) {
cur = !isArray(cur) || cur._use_call ? [cur] : cur
}
}
return cur;
}
},
/**
* @function newInstance
* Creates a new instance of the class. This method is useful for creating new instances
* with arbitrary parameters.
* <h3>Example</h3>
* @codestart
* $.Class("MyClass",{},{})
* var mc = MyClass.newInstance.apply(null, new Array(parseInt(Math.random()*10,10))
* @codeend
* @return {class} instance of the class
*/
newInstance: function() {
// get a raw instance objet (init is not called)
var inst = this.rawInstance(),
args;
// call setup if there is a setup
if ( inst.setup ) {
args = inst.setup.apply(inst, arguments);
}
// call init if there is an init, if setup returned args, use those as the arguments
if ( inst.init ) {
inst.init.apply(inst, isArray(args) ? args : arguments);
}
return inst;
},
/**
* Setup gets called on the inherting class with the base class followed by the
* inheriting class's raw properties.
*
* Setup will deeply extend a static defaults property on the base class with
* properties on the base class. For example:
*
* $.Class("MyBase",{
* defaults : {
* foo: 'bar'
* }
* },{})
*
* MyBase("Inheriting",{
* defaults : {
* newProp : 'newVal'
* }
* },{}
*
* Inheriting.defaults -> {foo: 'bar', 'newProp': 'newVal'}
*
* @param {Object} baseClass the base class that is being inherited from
* @param {String} fullName the name of the new class
* @param {Object} staticProps the static properties of the new class
* @param {Object} protoProps the prototype properties of the new class
*/
setup: function( baseClass, fullName ) {
// set defaults as the merger of the parent defaults and this object's defaults
this.defaults = extend(true, {}, baseClass.defaults, this.defaults);
return arguments;
},
rawInstance: function() {
// prevent running init
initializing = true;
var inst = new this();
initializing = false;
// allow running init
return inst;
},
/**
* Extends a class with new static and prototype functions. There are a variety of ways
* to use extend:
*
* // with className, static and prototype functions
* $.Class('Task',{ STATIC },{ PROTOTYPE })
* // with just classname and prototype functions
* $.Class('Task',{ PROTOTYPE })
* // with just a className
* $.Class('Task')
*
* You no longer have to use <code>.extend</code>. Instead, you can pass those options directly to
* $.Class (and any inheriting classes):
*
* // with className, static and prototype functions
* $.Class('Task',{ STATIC },{ PROTOTYPE })
* // with just classname and prototype functions
* $.Class('Task',{ PROTOTYPE })
* // with just a className
* $.Class('Task')
*
* @param {String} [fullName] the classes name (used for classes w/ introspection)
* @param {Object} [klass] the new classes static/class functions
* @param {Object} [proto] the new classes prototype functions
*
* @return {jQuery.Class} returns the new class
*/
extend: function( fullName, klass, proto ) {
// figure out what was passed and normalize it
if ( typeof fullName != 'string' ) {
proto = klass;
klass = fullName;
fullName = null;
}
if (!proto ) {
proto = klass;
klass = null;
}
proto = proto || {};
var _super_class = this,
_super = this[STR_PROTOTYPE],
name, shortName, namespace, prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
inheritProps(proto, _super, prototype);
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( initializing ) return;
// we are being called w/o new, we are extending
if ( this.constructor !== Class && arguments.length ) {
return arguments.callee.extend.apply(arguments.callee, arguments)
} else { //we are being called w/ new
return this.Class.newInstance.apply(this.Class, arguments)
}
}
// Copy old stuff onto class
for ( name in this ) {
if ( this.hasOwnProperty(name) ) {
Class[name] = this[name];
}
}
// copy new static props on class
inheritProps(klass, this, Class);
// do namespace stuff
if ( fullName ) {
var root;
if (klass && klass.root) {
root = klass.root;
if ($.isString(root)) {
root = getObject(root, window, true);
}
}
var parts = fullName.split(/\./),
shortName = parts.pop(),
current = getObject(parts.join('.'), root || window, true),
namespace = current;
// !-- FOUNDRY HACK --! //
// Inherit any existing properties from the namespace where Class is being assigned to.
extend(true, Class, current[shortName]);
current[shortName] = Class;
}
// set things that can't be overwritten
extend(Class, {
prototype: prototype,
/**
* @attribute namespace
* The namespaces object
*
* $.Class("MyOrg.MyClass",{},{})
* MyOrg.MyClass.namespace //-> MyOrg
*
*/
namespace: namespace,
/**
* @attribute shortName
* The name of the class without its namespace, provided for introspection purposes.
*
* $.Class("MyOrg.MyClass",{},{})
* MyOrg.MyClass.shortName //-> 'MyClass'
* MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
*
*/
shortName: shortName,
constructor: Class,
/**
* @attribute fullName
* The full name of the class, including namespace, provided for introspection purposes.
*
* $.Class("MyOrg.MyClass",{},{})
* MyOrg.MyClass.shortName //-> 'MyClass'
* MyOrg.MyClass.fullName //-> 'MyOrg.MyClass'
*
*/
fullName: fullName
});
//make sure our prototype looks nice
Class[STR_PROTOTYPE].Class = Class[STR_PROTOTYPE].constructor = Class;
// call the class setup
var args = Class.setup.apply(Class, concatArgs([_super_class],arguments));
// call the class init
if ( Class.init ) {
Class.init.apply(Class, args || concatArgs([_super_class],arguments));
}
/* @Prototype*/
return Class;
/**
* @function setup
* If a setup method is provided, it is called when a new
* instances is created. It gets passed the same arguments that
* were given to the Class constructor function (<code> new Class( arguments ... )</code>).
*
* $.Class("MyClass",
* {
* setup: function( val ) {
* this.val = val;
* }
* })
* var mc = new MyClass("Check Check")
* mc.val //-> 'Check Check'
*
* Setup is called before [jQuery.Class.prototype.init init]. If setup
* return an array, those arguments will be used for init.
*
* $.Class("jQuery.Controller",{
* setup : function(htmlElement, rawOptions){
* return [$(htmlElement),
* $.extend({}, this.Class.defaults, rawOptions )]
* }
* })
*
* <div class='whisper'>PRO TIP:
* Setup functions are used to normalize constructor arguments and provide a place for
* setup code that extending classes don't have to remember to call _super to
* run.
* </div>
*
* Setup is not defined on $.Class itself, so calling super in inherting classes
* will break. Don't do the following:
*
* $.Class("Thing",{
* setup : function(){
* this._super(); // breaks!
* }
* })
*
* @return {Array|undefined} If an array is return, [jQuery.Class.prototype.init] is
* called with those arguments; otherwise, the original arguments are used.
*/
//break up
/**
* @function init
* If an <code>init</code> method is provided, it gets called when a new instance
* is created. Init gets called after [jQuery.Class.prototype.setup setup], typically with the
* same arguments passed to the Class
* constructor: (<code> new Class( arguments ... )</code>).
*
* $.Class("MyClass",
* {
* init: function( val ) {
* this.val = val;
* }
* })
* var mc = new MyClass(1)
* mc.val //-> 1
*
* [jQuery.Class.prototype.setup Setup] is able to modify the arguments passed to init. Read
* about it there.
*
*/
//Breaks up code
/**
* @attribute constructor
*
* A reference to the Class (or constructor function). This allows you to access
* a class's static properties from an instance.
*
* ### Quick Example
*
* // a class with a static property
* $.Class("MyClass", {staticProperty : true}, {});
*
* // a new instance of myClass
* var mc1 = new MyClass();
*
* // read the static property from the instance:
* mc1.constructor.staticProperty //-> true
*
* Getting static properties with the constructor property, like
* [jQuery.Class.static.fullName fullName], is very common.
*
*/
}
})
clss.callback = clss[STR_PROTOTYPE].callback = clss[STR_PROTOTYPE].
/**
* @function proxy
* Returns a method that sets 'this' to the current instance. This does the same thing as
* and is described better in [jQuery.Class.static.proxy].
* The only difference is this proxy works
* on a instance instead of a class.
* @param {String|Array} fname If a string, it represents the function to be called.
* If it is an array, it will call each function in order and pass the return value of the prior function to the
* next function.
* @return {Function} the callback function
*/
proxy = clss.proxy;
})();(function(){
// ------- HELPER FUNCTIONS ------
// Binds an element, returns a function that unbinds
var bind = function( el, ev, callback ) {
var wrappedCallback,
binder = el.bind && el.unbind ? el : $(isFunction(el) ? [el] : el);
//this is for events like >click.
if ( ev.indexOf(">") === 0 ) {
ev = ev.substr(1);
wrappedCallback = function( event ) {
if ( event.target === el ) {
callback.apply(this, arguments);
}
};
}
binder.bind(ev, wrappedCallback || callback);
// if ev name has >, change the name and bind
// in the wrapped callback, check that the element matches the actual element
return function() {
binder.unbind(ev, wrappedCallback || callback);
el = ev = callback = wrappedCallback = null;
};
},
makeArray = $.makeArray,
isArray = $.isArray,
isFunction = $.isFunction,
isString = $.isString,
extend = $.extend,
Str = $.String,
each = $.each,
getObject = Str.getObject,
STR_PROTOTYPE = 'prototype',
STR_CONSTRUCTOR = 'constructor',
slice = Array[STR_PROTOTYPE].slice,
// Binds an element, returns a function that unbinds
delegate = function( el, selector, ev, callback ) {
// !-- FOUNDRY HACK --! //
// Make event delegation work with direct child selector
if ( selector.indexOf(">") === 0 ) {
selector = (el.data("directSelector") + " " || "") + selector;
}
var binder = el.delegate && el.undelegate ? el : $(isFunction(el) ? [el] : el)
binder.delegate(selector, ev, callback);
return function() {
binder.undelegate(selector, ev, callback);
binder = el = ev = callback = selector = null;
};
},
// calls bind or unbind depending if there is a selector
binder = function( el, ev, callback, selector ) {
return selector ? delegate(el, selector, ev, callback) : bind(el, ev, callback);
},
// moves 'this' to the first argument, wraps it with jQuery if it's an element
shifter = function shifter(context, name) {
var method = typeof name == "string" ? context[name] : name;
return function() {
context.called = name;
return method.apply(context, [this.nodeName ? $(this) : this].concat( slice.call(arguments, 0) ) );
};
},
// matches dots
dotsReg = /\./g,
// matches controller
controllersReg = /_?controllers?/ig,
//used to remove the controller from the name
underscoreAndRemoveController = function( className ) {
return Str.underscore(className.replace($.globalNamespace + ".", "").replace(dotsReg, '_').replace(controllersReg, ""));
},
// checks if it looks like an action
// actionMatcher = /[^\w]/,
// !-- FOUNDRY HACK --! //
// Prevent inclusion of single word property name that starts with a symbol, e.g. $family from MooTools.
// This is coming from an environment where jQuery and MooTools may coexist.
actionMatcher = /^\S(.*)\s(.*)/,
// handles parameterized action names
parameterReplacer = /\{([^\}]+)\}/g,
controllerReplacer = /\{([^\.]+[\.][^\.]+)\}/g,
breaker = /^(?:(.*?)\s)?([\w\.\:>]+)$/,
basicProcessor,
data = function(el, data){
return $.data(el, "controllers", data)
};
/**
* @class jQuery.Controller
* @parent jquerymx
* @plugin jquery/controller
* @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/controller/controller.js
* @test jquery/controller/qunit.html
* @inherits jQuery.Class
* @description jQuery widget factory.
*
* jQuery.Controller helps create organized, memory-leak free, rapidly performing
* jQuery widgets. Its extreme flexibility allows it to serve as both
* a traditional View and a traditional Controller.
*
* This means it is used to
* create things like tabs, grids, and contextmenus as well as
* organizing them into higher-order business rules.
*
* Controllers make your code deterministic, reusable, organized and can tear themselves
* down auto-magically. Read about [http://jupiterjs.com/news/writing-the-perfect-jquery-plugin
* the theory behind controller] and
* a [http://jupiterjs.com/news/organize-jquery-widgets-with-jquery-controller walkthrough of its features]
* on Jupiter's blog. [mvc.controller Get Started with jQueryMX] also has a great walkthrough.
*
* Controller inherits from [jQuery.Class $.Class] and makes heavy use of
* [http://api.jquery.com/delegate/ event delegation]. Make sure
* you understand these concepts before using it.
*
* ## Basic Example
*
* Instead of
*
*
* $(function(){
* $('#tabs').click(someCallbackFunction1)
* $('#tabs .tab').click(someCallbackFunction2)
* $('#tabs .delete click').click(someCallbackFunction3)
* });
*
* do this
*
* $.Controller('Tabs',{
* click: function() {...},
* '.tab click' : function() {...},
* '.delete click' : function() {...}
* })
* $('#tabs').tabs();
*
*
* ## Tabs Example
*
* @demo jquery/controller/controller.html
*
* ## Using Controller
*
* Controller helps you build and organize jQuery plugins. It can be used
* to build simple widgets, like a slider, or organize multiple
* widgets into something greater.
*
* To understand how to use Controller, you need to understand
* the typical lifecycle of a jQuery widget and how that maps to
* controller's functionality:
*
* ### A controller class is created.
*
* $.Controller("MyWidget",
* {
* defaults : {
* message : "Remove Me"
* }
* },
* {
* init : function(rawEl, rawOptions){
* this.element.append(
* "<div>"+this.options.message+"</div>"
* );
* },
* "div click" : function(div, ev){
* div.remove();
* }
* })
*
* This creates a <code>$.fn.my_widget</code> jQuery helper function
* that can be used to create a new controller instance on an element. Find
* more information [jquery.controller.plugin here] about the plugin gets created
* and the rules around its name.
*
* ### An instance of controller is created on an element
*
* $('.thing').my_widget(options) // calls new MyWidget(el, options)
*
* This calls <code>new MyWidget(el, options)</code> on
* each <code>'.thing'</code> element.
*
* When a new [jQuery.Class Class] instance is created, it calls the class's
* prototype setup and init methods. Controller's [jQuery.Controller.prototype.setup setup]
* method:
*
* - Sets [jQuery.Controller.prototype.element this.element] and adds the controller's name to element's className.
* - Merges passed in options with defaults object and sets it as [jQuery.Controller.prototype.options this.options]
* - Saves a reference to the controller in <code>$.data</code>.
* - [jquery.controller.listening Binds all event handler methods].
*
*
* ### The controller responds to events
*
* Typically, Controller event handlers are automatically bound. However, there are
* multiple ways to [jquery.controller.listening listen to events] with a controller.
*
* Once an event does happen, the callback function is always called with 'this'
* referencing the controller instance. This makes it easy to use helper functions and
* save state on the controller.
*
*
* ### The widget is destroyed
*
* If the element is removed from the page, the
* controller's [jQuery.Controller.prototype.destroy] method is called.
* This is a great place to put any additional teardown functionality.
*
* You can also teardown a controller programatically like:
*
* $('.thing').my_widget('destroy');
*
* ## Todos Example
*
* Lets look at a very basic example -
* a list of todos and a button you want to click to create a new todo.
* Your HTML might look like:
*
* @codestart html
* <div id='todos'>
* <ol>
* <li class="todo">Laundry</li>
* <li class="todo">Dishes</li>
* <li class="todo">Walk Dog</li>
* </ol>
* <a class="create">Create</a>
* </div>
* @codeend
*
* To add a mousover effect and create todos, your controller might look like:
*
* $.Controller('Todos',{
* ".todo mouseover" : function( el, ev ) {
* el.css("backgroundColor","red")
* },
* ".todo mouseout" : function( el, ev ) {
* el.css("backgroundColor","")
* },
* ".create click" : function() {
* this.find("ol").append("<li class='todo'>New Todo</li>");
* }
* })
*
* Now that you've created the controller class, you've must attach the event handlers on the '#todos' div by
* creating [jQuery.Controller.prototype.setup|a new controller instance]. There are 2 ways of doing this.
*
* @codestart
* //1. Create a new controller directly:
* new Todos($('#todos'));
* //2. Use jQuery function
* $('#todos').todos();
* @codeend
*
* ## Controller Initialization
*
* It can be extremely useful to add an init method with
* setup functionality for your widget.
*
* In the following example, I create a controller that when created, will put a message as the content of the element:
*
* $.Controller("SpecialController",
* {
* init: function( el, message ) {
* this.element.html(message)
* }
* })
* $(".special").special("Hello World")
*
* ## Removing Controllers
*
* Controller removal is built into jQuery. So to remove a controller, you just have to remove its element:
*
* @codestart
* $(".special_controller").remove()
* $("#containsControllers").html("")
* @codeend
*
* It's important to note that if you use raw DOM methods (<code>innerHTML, removeChild</code>), the controllers won't be destroyed.
*
* If you just want to remove controller functionality, call destroy on the controller instance:
*
* @codestart
* $(".special_controller").controller().destroy()
* @codeend
*
* ## Accessing Controllers
*
* Often you need to get a reference to a controller, there are a few ways of doing that. For the
* following example, we assume there are 2 elements with <code>className="special"</code>.
*
* @codestart
* //creates 2 foo controllers
* $(".special").foo()
*
* //creates 2 bar controllers
* $(".special").bar()
*
* //gets all controllers on all elements:
* $(".special").controllers() //-> [foo, bar, foo, bar]
*
* //gets only foo controllers
* $(".special").controllers(FooController) //-> [foo, foo]
*
* //gets all bar controllers
* $(".special").controllers(BarController) //-> [bar, bar]
*
* //gets first controller
* $(".special").controller() //-> foo
*
* //gets foo controller via data
* $(".special").data("controllers")["FooController"] //-> foo
* @codeend
*
* ## Calling methods on Controllers
*
* Once you have a reference to an element, you can call methods on it. However, Controller has
* a few shortcuts:
*
* @codestart
* //creates foo controller
* $(".special").foo({name: "value"})
*
* //calls FooController.prototype.update
* $(".special").foo({name: "value2"})
*
* //calls FooController.prototype.bar
* $(".special").foo("bar","something I want to pass")
* @codeend
*
* These methods let you call one controller from another controller.
*
*/
var controllerRoot = $.globalNamespace + ".Controller";
$.Controller = function(name) {
// !-- FOUNDRY HACK --! //
// By default, all controllers are created under the
// $.Controller root namespace.
var args = makeArray(arguments),
_static = {
root: controllerRoot
},
_prototype;
if (args.length > 2) {
// Namespace can be overriden
_static = $.extend(_static, args[1]);
_prototype = args[2];
} else {
_prototype = args[1];
}
if (_static.namespace) {
name = _static.namespace + "." + name;
}
return $.Controller.Class(name, _static, _prototype);
}
var controllerClass = controllerRoot + ".Class";
$.Class(controllerClass,
/**
* @Static
*/
{
/**
* Does 2 things:
*
* - Creates a jQuery helper for this controller.</li>
* - Calculates and caches which functions listen for events.</li>
*
* ### jQuery Helper Naming Examples
*
*
* "TaskController" -> $().task_controller()
* "Controllers.Task" -> $().controllers_task()
*
*/
setup: function(baseClass, name) {
// Allow contollers to inherit "defaults" from superclasses as it done in $.Class
this._super.apply(this, arguments);
// if you didn't provide a name, or are controller, don't do anything
if (!this.shortName || this.fullName == controllerClass) {
return;
}
// cache the underscored names
this._fullName = underscoreAndRemoveController(this.fullName);
this._shortName = underscoreAndRemoveController(this.shortName);
var controller = this,
/**
* @attribute pluginName
* Setting the <code>pluginName</code> property allows you
* to change the jQuery plugin helper name from its
* default value.
*
* $.Controller("Mxui.Layout.Fill",{
* pluginName: "fillWith"
* },{});
*
* $("#foo").fillWith();
*/
funcName, forLint;
// !-- FOUNDRY HACK --! //
// Make creation of jQuery plugin by testing the existence of pluginName.
if (isString(this.pluginName)) {
// !-- FOUNDRY HACK --! //
// Add a reference to the fullname
var _fullName = this._fullName;
var pluginname = this.pluginName;
// create jQuery plugin
if (!$.fn[pluginname] ) {
$.fn[pluginname] = function( options ) {
var args = makeArray(arguments);
// Returning controller instance if it exists
if ($.isString(options) && options==="controller") {
var controllers = data(this[0]),
instance = controllers && controllers[_fullName];
return instance;
}
return this.each(function() {
//check if created
var controllers = data(this),
//plugin is actually the controller instance
//plugin = controllers && controllers[pluginname];
// !-- FOUNDRY HACK --! //
// Check using controller full name
instance = controllers && controllers[_fullName];
if (instance) {
// call a method on the controller with the remaining args
if ($.isString(options)) {
var method = instance[options];
$.isFunction(method) && method.apply(instance, args.slice(1));
return;
}
// call the plugin's update method
instance.update.apply(instance, args);
} else {
//create a new controller instance
controller.newInstance.apply(controller, [this].concat(args));
}
});
};
}
}
// !-- FOUNDRY HACK --! //
// If a prototype factory function was given instead of a prototype object,
// we expect the factory function to return the prototype object upon execution
// of the factory function. This factory function gets executed during the
// instantiation of the controller.
var args = makeArray(arguments),
prototype = this[STR_PROTOTYPE],
protoFactory = args[(args.length > 3) ? 3 : 2];
if (isFunction(protoFactory)) {
// Remap the factory function
this.protoFactory = protoFactory;
// Attempt to execute the prototype factory once to get
// a list of actions that we can cache first.
prototype = this.protoFactory.call(this, null);
}
// calculate and cache actions
this.actions = {};
for (funcName in prototype) {
if (funcName == 'constructor' || !isFunction(prototype[funcName]) ) {
continue;
}
if ( this._isAction(funcName) ) {
this.actions[funcName] = this._action(funcName);
}
}
// !-- FOUNDRY HACK --! //
// Controller has been created. Resolve module.
$.module("$:/Controllers/" + this.fullName).resolve(this);
},
hookup: function( el ) {
return new this(el);
},
/**
* @hide
* @param {String} methodName a prototype function
* @return {Boolean} truthy if an action or not
*/
_isAction: function( methodName ) {
if ( actionMatcher.test(methodName) ) {
return true;
} else {
return $.inArray(methodName, this.listensTo) > -1 || $.event.special[methodName] || processors[methodName];
}
},
/**
* @hide
* This takes a method name and the options passed to a controller
* and tries to return the data necessary to pass to a processor
* (something that binds things).
*
* For performance reasons, this called twice. First, it is called when
* the Controller class is created. If the methodName is templated
* like : "{window} foo", it returns null. If it is not templated
* it returns event binding data.
*
* The resulting data is added to this.actions.
*
* When a controller instance is created, _action is called again, but only
* on templated actions.
*
* @param {Object} methodName the method that will be bound
* @param {Object} [options] first param merged with class default options
* @return {Object} null or the processor and pre-split parts.
* The processor is what does the binding/subscribing.
*/
_action: function( methodName, options ) {
// reset the test index
parameterReplacer.lastIndex = 0;
//if we don't have options (a controller instance), we'll run this later
if (!options && parameterReplacer.test(methodName) ) {
return null;
}
// !-- FOUNDRY HACK --! //
// Ability to bind custom event to self.
// "{self} customEvent"
methodName = methodName.replace("{self} ", "");
// If we have options, run sub to replace templates "{}" with a value from the options
// or the window
var convertedName = methodName;
if (options) {
var bindingOtherController = false;
if (controllerReplacer.test(methodName)) {
var controller, selector = "";
convertedName =
methodName
.replace(controllerReplacer, function(whole, inside){
var parts = inside.split(".");
controller = options["{"+parts[0]+"}"] || {};
if ($.isControllerInstance(controller)) {
selector = (controller[parts[1]] || {})["selector"];
}
return selector;
})
.match(breaker);
// If there is a selector, this will be true.
bindingOtherController = !!selector;
convertedName = [controller.element].concat(convertedName || []);
}
if (!bindingOtherController) {
convertedName = Str.sub(methodName, [options, window]);
}
}
// If a "{}" resolves to an object, convertedName will be an array
var arr = isArray(convertedName),
// get the parts of the function = [convertedName, delegatePart, eventPart]
parts = (arr ? convertedName[1] : convertedName).match(breaker),
event = parts[2],
processor = processors[event] || basicProcessor;
return {
processor: processor,
parts: parts,
delegate : arr ? convertedName[0] : undefined
};
},
/**
* @attribute processors
* An object of {eventName : function} pairs that Controller uses to hook up events
* auto-magically. A processor function looks like:
*
* jQuery.Controller.processors.
* myprocessor = function( el, event, selector, cb, controller ) {
* //el - the controller's element
* //event - the event (myprocessor)
* //selector - the left of the selector
* //cb - the function to call
* //controller - the binding controller
* };
*
* This would bind anything like: "foo~3242 myprocessor".
*
* The processor must return a function that when called,
* unbinds the event handler.
*
* Controller already has processors for the following events:
*
* - change
* - click
* - contextmenu
* - dblclick
* - focusin
* - focusout
* - keydown
* - keyup
* - keypress
* - mousedown
* - mouseenter
* - mouseleave
* - mousemove
* - mouseout
* - mouseover
* - mouseup
* - reset
* - resize
* - scroll
* - select
* - submit
*
* Listen to events on the document or window
* with templated event handlers:
*
*
* $.Controller('Sized',{
* "{window} resize" : function(){
* this.element.width(this.element.parent().width() / 2);
* }
* });
*
* $('.foo').sized();
*/
processors: {},
/**
* @attribute listensTo
* An array of special events this controller
* listens too. You only need to add event names that
* are whole words (ie have no special characters).
*
* $.Controller('TabPanel',{
* listensTo : ['show']
* },{
* 'show' : function(){
* this.element.show();
* }
* })
*
* $('.foo').tab_panel().trigger("show");
*
*/
listensTo: [],
/**
* @attribute defaults
* A object of name-value pairs that act as default values for a controller's
* [jQuery.Controller.prototype.options options].
*
* $.Controller("Message",
* {
* defaults : {
* message : "Hello World"
* }
* },{
* init : function(){
* this.element.text(this.options.message);
* }
* })
*
* $("#el1").message(); //writes "Hello World"
* $("#el12").message({message: "hi"}); //writes hi
*
* In [jQuery.Controller.prototype.setup setup] the options passed to the controller
* are merged with defaults. This is not a deep merge.
*/
defaults: {},
hostname: "parent"
},
/**
* @Prototype
*/
{
/**
* Setup is where most of controller's magic happens. It does the following:
*
* ### 1. Sets this.element
*
* The first parameter passed to new Controller(el, options) is expected to be
* an element. This gets converted to a jQuery wrapped element and set as
* [jQuery.Controller.prototype.element this.element].
*
* ### 2. Adds the controller's name to the element's className.
*
* Controller adds it's plugin name to the element's className for easier
* debugging. For example, if your Controller is named "Foo.Bar", it adds
* "foo_bar" to the className.
*
* ### 3. Saves the controller in $.data
*
* A reference to the controller instance is saved in $.data. You can find
* instances of "Foo.Bar" like:
*
* $("#el").data("controllers")['foo_bar'].
*
* ### Binds event handlers
*
* Setup does the event binding described in [jquery.controller.listening Listening To Events].
*
* @param {HTMLElement} element the element this instance operates on.
* @param {Object} [options] option values for the controller. These get added to
* this.options and merged with [jQuery.Controller.static.defaults defaults].
* @return {Array} return an array if you wan to change what init is called with. By
* default it is called with the element and options passed to the controller.
*/
setup: function(elem, options) {
var instance = this,
Class = instance[STR_CONSTRUCTOR],
prototype = instance[STR_PROTOTYPE];
var _fullName = Class._fullName;
// !-- FOUNDRY HACK --! //
// Unique id for every controller instance.
instance.instanceId = $.uid(_fullName + '_');
// !-- FOUNDRY HACK --! //
// Added defaultOptions as an alternative to defaults
var instanceOptions = instance.options
= extend(true, {}, Class.defaults, Class.defaultOptions, options);
// Convert HTML element into a jQuery element
// and store it inside instance.element.
var element = instance.element
= $(elem);
// !-- FOUNDRY HACK --! //
// Execute factory function if exists, extends the properties
// of the returned object onto the instance.
if (Class.protoFactory) {
// This is where "self" keyword is passed as first argument.
prototype = Class.protoFactory.apply(Class, [instance, instanceOptions, element]);
// Extend the properties of the prototype object onto the instance.
extend(true, instance, prototype);
}
// !-- FOUNDRY HACK --! //
// Use _fullName instead
// This actually does $(e).data("controllers", _fullName);
(data(elem) || data(elem, {}))[_fullName] = instance;
// !-- FOUNDRY HACK --~ //
// Add a unique direct selector for every controller instance.
if (!element.data("directSelector")) {
var selector = $.uid("DS");
element
.addClass(selector)
.data("directSelector", "." + selector);
}
// !-- FOUNDRY HACK --! //
// Augment selector properties into selector functions.
// The rest are passed in as controller properties.
for (name in instanceOptions) {
if (!name.match(/^\{.+\}$/)) continue;
var key = name.replace(/^\{|\}$/g,''),
val = instanceOptions[name];
// Augmented selector function
if (isString(val)) {
instance[key] = (function(instance, selector, funcName) {
// Selector shorthand for controllers
selector = /^(\.|\#)$/.test(selector) ? selector + funcName : selector;
// Create selector function
var selectorFunc = function(filter) {
var elements = instance.element.find(selector);
if (filter) {
elements = elements.filter(filter);
}
return elements;
};
// Keep the selector as a property of the function
selectorFunc.selector = selector;
selectorFunc.css = function() {
var cssRule = selectorFunc.cssRule;
if (!cssRule) {
var directSelector = element.data("directSelector"),
ruleSelector = $.map(selector.split(","), function(selector) {
return directSelector + " " + selector
});
cssRule = selectorFunc.cssRule = $.cssRule(ruleSelector);
cssRule.important = true;
}
return (arguments.length) ? cssRule.css.apply(cssRule, arguments) : cssRule;
};
return selectorFunc;
})(instance, val, key);
// Else just reference it, e.g. controller instance
} else {
instance[key] = val;
}
}
// !-- FOUNDRY HACK --! //
// Augment view properties into view functions.
// self.view.listItem(useHtml, data, callback);
var views = instanceOptions.view;
// Prevent augmented functions from being
// extended onto the prototype view function.
var __view = instance.view;
instance.view = function() {
return __view.apply(this, arguments);
};
each(views || {}, function(name, view){
instance.view[name] = function(useHtml) {
var args = makeArray(arguments);
if ($.isBoolean(useHtml)) {
args = args.slice(1);
} else {
useHtml = false;
}
return instance.view.apply(instance, [useHtml, name].concat(args));
}
});
// !-- FOUNDRY HACK --! //
// Instance property override
$.extend(instance, instanceOptions.controller);
// !--- FOUNDRY HACK --! //
instance.pluginInstances = {};
/**
* @attribute called
* String name of current function being called on controller instance. This is
* used for picking the right view in render.
* @hide
*/
instance.called = "init";
// bind all event handlers
instance._bind();
var __init = instance.init || $.noop;
// !-- FOUNDRY HACK --! //
// Trigger init event when controller is created.
instance.init = function(){
instance.init = __init;
result = __init.apply(instance, arguments);
instance.trigger("init." + Class.fullName.toLowerCase(), [instance]);
return result;
}
/**
* @attribute element
* The controller instance's delegated element. This
* is set by [jQuery.Controller.prototype.setup setup]. It
* is a jQuery wrapped element.
*
* For example, if I add MyWidget to a '#myelement' element like:
*
* $.Controller("MyWidget",{
* init : function(){
* this.element.css("color","red")
* }
* })
*
* $("#myelement").my_widget()
*
* MyWidget will turn #myelement's font color red.
*
* ## Using a different element.
*
* Sometimes, you want a different element to be this.element. A
* very common example is making progressively enhanced form widgets.
*
* To change this.element, overwrite Controller's setup method like:
*
* $.Controller("Combobox",{
* setup : function(el, options){
* this.oldElement = $(el);
* var newEl = $('<div/>');
* this.oldElement.wrap(newEl);
* this._super(newEl, options);
* },
* init : function(){
* this.element //-> the div
* },
* ".option click" : function(){
* // event handler bound on the div
* },
* destroy : function(){
* var div = this.element; //save reference
* this._super();
* div.replaceWith(this.oldElement);
* }
* }
*/
return [element, instanceOptions].concat(makeArray(arguments).slice(2));
/**
* @function init
*
* Implement this.
*/
},
/**
* Bind attaches event handlers that will be
* removed when the controller is removed.
*
* This used to be a good way to listen to events outside the controller's
* [jQuery.Controller.prototype.element element]. However,
* using templated event listeners is now the prefered way of doing this.
*
* ### Example:
*
* init: function() {
* // calls somethingClicked(el,ev)
* this.bind('click','somethingClicked')
*
* // calls function when the window is clicked
* this.bind(window, 'click', function(ev){
* //do something
* })
* },
* somethingClicked: function( el, ev ) {
*
* }
*
* @param {HTMLElement|jQuery.fn|Object} [el=this.element]
* The element to be bound. If an eventName is provided,
* the controller's element is used instead.
*
* @param {String} eventName The event to listen for.
* @param {Function|String} func A callback function or the String name of a controller function. If a controller
* function name is given, the controller function is called back with the bound element and event as the first
* and second parameter. Otherwise the function is called back like a normal bind.
* @return {Integer} The id of the binding in this._bindings
*/
on: function(eventName) {
var args = makeArray(arguments),
element = this.element,
length = args.length;
// Listen to the controller's element
// on(eventName, eventHandler);
if (length==2) {
return this._binder(element, eventName, args[1]);
}
// Listen to controller's child elements matching the selector
// on(eventName, selector, eventHandler);
// args[1] == selector, jquery collection or dom node.
// args[2] == eventHandler.
if (length==3 && isString(args[1])) {
return this._binder(element, eventName, args[2], args[1]);
} else {
return this._binder(args[1], eventName, args[2]);
}
// Listen to an element from another element
// on(eventName, element, selector, eventHandler);
if (length==4) {
return this._binder($(args[1]), eventName, args[3], args[2]);
}
},
// !-- FOUNDRY HACK --! //
// Rename this.bind from this_bind. Conflict with mootools.
// _bind: function( el, eventName, func ) {
_bind: function() {
var instance = this,
Class = instance[STR_CONSTRUCTOR],
actions = Class.actions,
bindings = instance._bindings = [],
element = instance.element;
each(actions || {}, function(name, action){
if (!actions.hasOwnProperty(name)) return;
var ready = Class.actions[name] || Class._action(name, instance.options);
// Translate to the controller element first
if ($.isControllerInstance(ready.delegate)) {
ready.delegate = ready.delegate.element;
}
bindings.push(
ready.processor(
ready.delegate || element,
ready.parts[2],
ready.parts[1],
name,
instance
)
);
});
//setup to be destroyed ... don't bind b/c we don't want to remove it
var destroyCB = shifter(this,"destroy");
element.bind("destroyed", destroyCB);
bindings.push(function( el ) {
$(el).unbind("destroyed", destroyCB);
});
return bindings.length;
},
_binder: function( el, eventName, func, selector ) {
if ( typeof func == 'string' ) {
func = shifter(this,func);
}
this._bindings.push(binder(el, eventName, func, selector));
return this._bindings.length;
},
_unbind : function(){
var el = this.element[0];
each(this._bindings, function( key, value ) {
value(el);
});
//adds bindings
this._bindings = [];
},
// !-- FOUNDRY HACK --! //
// Element event triggering
trigger: function(name) {
var el = this.element;
if (!el) return;
var event = $.Event(name);
el.trigger.apply(el, [event].concat($.makeArray(arguments).slice(1)));
return event;
},
/**
* Delegate will delegate on an elememt and will be undelegated when the controller is removed.
* This is a good way to delegate on elements not in a controller's element.<br/>
* <h3>Example:</h3>
* @codestart
* // calls function when the any 'a.foo' is clicked.
* this.delegate(document.documentElement,'a.foo', 'click', function(ev){
* //do something
* })
* @codeend
* @param {HTMLElement|jQuery.fn} [element=this.element] the element to delegate from
* @param {String} selector the css selector
* @param {String} eventName the event to bind to
* @param {Function|String} func A callback function or the String name of a controller function. If a controller
* function name is given, the controller function is called back with the bound element and event as the first
* and second parameter. Otherwise the function is called back like a normal bind.
* @return {Integer} The id of the binding in this._bindings
*/
delegate: function( element, selector, eventName, func ) {
if ( typeof element == 'string' ) {
func = eventName;
eventName = selector;
selector = element;
element = this.element;
}
return this._binder(element, eventName, func, selector);
},
/**
* Update extends [jQuery.Controller.prototype.options this.options]
* with the `options` argument and rebinds all events. It basically
* re-configures the controller.
*
* For example, the following controller wraps a recipe form. When the form
* is submitted, it creates the recipe on the server. When the recipe
* is `created`, it resets the form with a new instance.
*
* $.Controller('Creator',{
* "{recipe} created" : function(){
* this.update({recipe : new Recipe()});
* this.element[0].reset();
* this.find("[type=submit]").val("Create Recipe")
* },
* "submit" : function(el, ev){
* ev.preventDefault();
* var recipe = this.options.recipe;
* recipe.attrs( this.element.formParams() );
* this.find("[type=submit]").val("Saving...")
* recipe.save();
* }
* });
* $('#createRecipes').creator({recipe : new Recipe()})
*
*
* @demo jquery/controller/demo-update.html
*
* Update is called if a controller's [jquery.controller.plugin jQuery helper] is
* called on an element that already has a controller instance
* of the same type.
*
* For example, a widget that listens for model updates
* and updates it's html would look like.
*
* $.Controller('Updater',{
* // when the controller is created, update the html
* init : function(){
* this.updateView();
* },
*
* // update the html with a template
* updateView : function(){
* this.element.html( "content.ejs",
* this.options.model );
* },
*
* // if the model is updated
* "{model} updated" : function(){
* this.updateView();
* },
* update : function(options){
* // make sure you call super
* this._super(options);
*
* this.updateView();
* }
* })
*
* // create the controller
* // this calls init
* $('#item').updater({model: recipe1});
*
* // later, update that model
* // this calls "{model} updated"
* recipe1.update({name: "something new"});
*
* // later, update the controller with a new recipe
* // this calls update
* $('#item').updater({model: recipe2});
*
* // later, update the new model
* // this calls "{model} updated"
* recipe2.update({name: "something newer"});
*
* _NOTE:_ If you overwrite `update`, you probably need to call
* this._super.
*
* ### Example
*
* $.Controller("Thing",{
* init: function( el, options ) {
* alert( 'init:'+this.options.prop )
* },
* update: function( options ) {
* this._super(options);
* alert('update:'+this.options.prop)
* }
* });
* $('#myel').thing({prop : 'val1'}); // alerts init:val1
* $('#myel').thing({prop : 'val2'}); // alerts update:val2
*
* @param {Object} options A list of options to merge with
* [jQuery.Controller.prototype.options this.options]. Often, this method
* is called by the [jquery.controller.plugin jQuery helper function].
*/
update: function( options ) {
extend(this.options, options);
this._unbind();
this._bind();
},
/**
* Destroy unbinds and undelegates all event handlers on this controller,
* and prevents memory leaks. This is called automatically
* if the element is removed. You can overwrite it to add your own
* teardown functionality:
*
* $.Controller("ChangeText",{
* init : function(){
* this.oldText = this.element.text();
* this.element.text("Changed!!!")
* },
* destroy : function(){
* this.element.text(this.oldText);
* this._super(); //Always call this!
* })
*
* Make sure you always call <code>_super</code> when overwriting
* controller's destroy event. The base destroy functionality unbinds
* all event handlers the controller has created.
*
* You could call destroy manually on an element with ChangeText
* added like:
*
* $("#changed").change_text("destroy");
*
*/
destroy: function() {
if ( this._destroyed ) {
return;
}
var fname = this[STR_CONSTRUCTOR]._fullName,
controllers;
// remove all plugins
for (pname in this.pluginInstances) {
this.removePlugin(pname);
}
// mark as destroyed
this._destroyed = true;
// remove the className
this.element.removeClass(fname);
// unbind bindings
this._unbind();
// clean up
delete this._actions;
delete this.element.data("controllers")[fname];
$(this).triggerHandler("destroyed"); //in case we want to know if the controller is removed
// !-- FOUNDRY HACK --! //
// Reassign this.element to an empty jQuery element instead.
this.element = $();
},
/**
* Queries from the controller's element.
* @codestart
* ".destroy_all click" : function() {
* this.find(".todos").remove();
* }
* @codeend
* @param {String} selector selection string
* @return {jQuery.fn} returns the matched elements
*/
find: function( selector ) {
return this.element.find(selector);
},
// !-- FOUNDRY HACK --! //
// Quick acccess to views.
view: function() {
var args = makeArray(arguments),
name,
options = args,
useHtml = false,
context = this[STR_CONSTRUCTOR].component || $,
html = "",
view = this.options.view || {};
if (typeof args[0] == "boolean") {
useHtml = args[0];
options = args.slice(1);
}
name = options[0] = view[options[0]];
// If view is not assigned, return empty string.
if (name==undefined) {
return (useHtml) ? "" : $("");
}
html = context.View.apply(context, options);
return (useHtml) ? html : $($.parseHTML($.trim(html)));
},
getPlugin: function(name) {
return this.pluginInstances[name];
},
addSubscriber: function(instance) {
var instances = ($.isArray(instance)) ? instance : [instance || {}];
// Prep options
var host = this,
hostname = this.Class.hostname,
options = {};
options["{" + hostname + "}"] = host;
$.map(instances, function(instance, i){
// If this is not a controller instance.
if (!$.isControllerInstance(instance)) return false;
// If instance is already a subscriber,skip.
if (instance.options[hostname]===this) return instance;
// Also map itself as a method name
instance[hostname] = host;
// Attach publisher to subscriber
return instance.update(options);
});
return instances;
},
// addPlugin(name, object, [options]);
// The object should consist of a method called destroy();
// addPlugin(name, function, [options]);
// The function should return an object with a method called destroy();
addPlugin: function(name, plugin, options) {
if (!name) return;
// This means we are working with plugin shorthand
if ((!plugin && !options) || $.isPlainObject(plugin)) {
options = plugin;
plugin = [this.Class.root, this.Class.fullName, $.String.capitalize(name)].join(".");
}
// If plugin is a string, get the controller from it.
if ($.isString(plugin)) {
plugin = $.getController(plugin);
}
var isPluginInstance = $.isControllerInstance(plugin);
// Controller class are also functions,
// so this simple test is good enough.
if (!isFunction(plugin) && !isPluginInstance) return;
// Normalize plugin options
options = $.extend(true, {element: this.element}, options, ((this.options.plugin || {})[name] || {}));
// Determine plugin type
var type =
((isPluginInstance) ? "instance" :
(($.isController(plugin)) ? "controller" : "function"));
// Trigger addPlugin event so controller can decorate the options
this.trigger("addPlugin", [name, plugin, options, type]);
var hostname = this.Class.hostname;
// Subcontrollers should have a way to listen back to host controller
options["{" + hostname + "}"] = this;
var pluginInstance;
switch(type) {
// Plugin instance
case "instance":
pluginInstance = plugin;
// Update child plugin with custom plugin options from host
plugin.update(options);
plugin[hostname] = this;
break;
// Plugin controller
case "controller":
pluginInstance = options.element.addController(plugin, options);
break;
// Plugin function
case "function":
pluginInstance = plugin(this, options);
break;
}
// If pluginInstance could not be created, stop.
if (!pluginInstance) return;
// Register plugin
this.pluginInstances[name] = pluginInstance;
// Host controller should also have a way to listen back to the child controller
if (type!=="function") {
var hostOptions = {};
hostOptions["{" + name + "}"] = pluginInstance;
this.update(hostOptions);
}
// Trigger registerPlugin
this.trigger("registerPlugin", [name, pluginInstance, options, type]);
return pluginInstance;
},
removePlugin: function(name) {
var plugin = this.getPlugin(name);
if (!plugin) return;
// Trigger removePlugin
this.trigger("removePlugin", [name, plugin]);
delete this.pluginInstances[name];
return $.isFunction(plugin.destroy) ? plugin.destroy() : null;
},
invokePlugin: function(name, method, args) {
var plugin = this.getPlugin(name);
// If plugin not exist, stop.
if (!plugin) return;
// If plugin method not exist, stop.
if (!$.isFunction(plugin[method])) return;
// Let any third party modify the arguments if required
this.trigger("invokePlugin", [name, plugin, args]);
return plugin[method].apply(this, args);
},
getMessageGroup: function() {
// Find parent element
var messageGroup = ($.isFunction(this.messageGroup)) ? this.messageGroup() : this.element.find("[data-message-group]");
if (messageGroup.length < 1) {
messageGroup = $("<div data-message-group></div>").prependTo(this.element);
}
return messageGroup;
},
setMessage: function(message, type) {
// Normalize arguments
var defaultOptions = {
type : "warning", // type: info, error, success
message: "",
parent : this.getMessageGroup(),
element: $('<div class="alert fade in"><button type="button" class="close" data-foundry-dismiss="alert">×</button></div>')
},
userOptions = {},
isDeferred = $.isDeferred(message);
// Normalize user options
if ($.isPlainObject(message) && !isDeferred) {
userOptions = message;
} else {
userOptions = {
message: message,
type : type || "warning"
}
}
var options = $.extend({}, defaultOptions, userOptions),
element = options.element;
if ($.isDeferred(message)) {
var myself = arguments.callee,
context = this;
message.done(function(message, type) {
options.message = message;
options.type = type || "warning";
myself.call(context, options);
element.show();
});
} else {
element
.addClass("alert-" + options.type)
.append(options.message);
if ($('html').has(element).length < 1) {
element.appendTo(options.parent);
}
}
return element;
},
clearMessage: function() {
this.getMessageGroup().empty();
},
//tells callback to set called on this. I hate this.
_set_called: true
});
var processors = $.Controller.Class.processors,
//------------- PROCESSSORS -----------------------------
//processors do the binding. They return a function that
//unbinds when called.
//the basic processor that binds events
basicProcessor = function( el, event, selector, methodName, controller ) {
return binder(el, event, shifter(controller, methodName), selector);
};
//set common events to be processed as a basicProcessor
each("change click contextmenu dblclick keydown keyup keypress mousedown mousemove mouseout mouseover mouseup reset resize scroll select submit focusin focusout mouseenter mouseleave".split(" "), function( i, v ) {
processors[v] = basicProcessor;
});
/**
* @add jQuery.fn
*/
//used to determine if a controller instance is one of controllers
//controllers can be strings or classes
var normalizeController = function(controller) {
return controller.replace("$.Controller", controllerRoot);
}
var getController = function(controller) {
if (isString(controller)) {
controller = normalizeController(controller);
controller = getObject(controller) || getObject(controllerRoot + "." + controller);
};
if (isController(controller)) {
return controller;
};
}
var isController = function(controller) {
return isFunction(controller) && controller.hasOwnProperty("_fullName");
}
var flattenControllers = function(controllers) {
return $.map(controllers, function(controller){
return (isArray(controller)) ? flattenControllers(controller) : getController(controller);
});
};
$.getController = getController;
$.isController = function(controller) {
return !!getController(controller);
}
$.isControllerInstance = function(instance) {
return instance && instance[STR_CONSTRUCTOR] && isController(instance[STR_CONSTRUCTOR]);
}
$.isControllerOf = function(instance, controllers) {
if (!controllers) return false;
if (!isArray(controllers)) {
controllers = [controllers];
}
for (var i=0; i<controllers.length; i++) {
var controller = getController(controllers[i]);
if (instance instanceof controller) return true;
}
return false;
};
$.fn.extend({
/**
* @function controllers
* Gets all controllers in the jQuery element.
* @return {Array} an array of controller instances.
*/
controllers: function() {
var candidates = flattenControllers(makeArray(arguments)),
instances = [];
this.each(function() {
var controllers = $.data(this, "controllers");
each(controllers || {}, function(_fullName, instance){
if (!controllers.hasOwnProperty(_fullName)) return;
if (!candidates.length || $.isControllerOf(instance, candidates)) {
instances.push(instance);
}
});
});
return instances;
},
/**
* @function controller
* Gets a controller in the jQuery element. With no arguments, returns the first one found.
* @param {Object} controller (optional) if exists, the first controller instance with this class type will be returned.
* @return {jQuery.Controller} the first controller.
*/
controller: function(controller, options) {
// Getter
if (options===undefined) {
return this.controllers(controller)[0];
}
// Setter
this.addController.apply(this, arguments);
return this;
},
hasController: function(controller) {
var _fullName =
(getController(controller) || {})._fullName ||
(isString(controller) ? underscoreAndRemoveController(normalizeController(controller)) : "");
return (!_fullName) ? false : (($(this).data("controllers") || {}).hasOwnProperty(_fullName));
},
addController: function(controller, options, callback) {
var Controller = getController(controller);
if (!Controller) return;
var instances = [];
this.each(function(){
// Do not add controller on script node or non-element nodes.
if (this.nodeType!==1 || this.nodeName=="SCRIPT") return;
// Just return existing instance
var existingInstance = $(this).controller(controller);
if (existingInstance) {
instances.push(existingInstance);
return;
}
// Or create a new instance
var instance = new Controller(this, options);
isFunction(callback) && callback.apply(instance, [$(this), instance]);
instances.push(instance);
});
return (instances.length > 1) ? instances : instances[0];
},
removeController: function(controller) {
this.each(function(){
var instances = $(this).controllers(controller);
while (instances.length) {
instances.shift().destroy();
}
});
return this;
},
addControllerWhenAvailable: function(controller) {
var elements = this,
args = arguments,
task = $.Deferred();
if ($.isController(controller)) {
controller = controller.fullName;
}
if (!isString(controller)) {
return task.reject();
}
$.module("$:/Controllers/" + controller)
.pipe(
function(){
var instance = elements.addController.apply(elements, args);
task.resolveWith(instance, [elements, instance]);
},
task.reject,
task.fail
);
return task;
},
// @deprecated 2.2
implement: function() {
this.addController.apply(this, arguments);
return this;
}
});
})();(function(){
// a path like string into something that's ok for an element ID
var toId = function( src ) {
return src.replace(/^\/\//, "").replace(/[\/\.]/g, "_");
},
makeArray = $.makeArray,
// used for hookup ids
id = 1;
// this might be useful for testing if html
// htmlTest = /^[\s\n\r\xA0]*<(.|[\r\n])*>[\s\n\r\xA0]*$/
/**
* @class jQuery.View
* @parent jquerymx
* @plugin jquery/view
* @test jquery/view/qunit.html
* @download dist/jquery.view.js
*
* @description A JavaScript template framework.
*
* View provides a uniform interface for using templates with
* jQuery. When template engines [jQuery.View.register register]
* themselves, you are able to:
*
* - Use views with jQuery extensions [jQuery.fn.after after], [jQuery.fn.append append],
* [jQuery.fn.before before], [jQuery.fn.html html], [jQuery.fn.prepend prepend],
* [jQuery.fn.replaceWith replaceWith], [jQuery.fn.text text].
* - Template loading from html elements and external files.
* - Synchronous and asynchronous template loading.
* - [view.deferreds Deferred Rendering].
* - Template caching.
* - Bundling of processed templates in production builds.
* - Hookup jquery plugins directly in the template.
*
* The [mvc.view Get Started with jQueryMX] has a good walkthrough of $.View.
*
* ## Use
*
*
* When using views, you're almost always wanting to insert the results
* of a rendered template into the page. jQuery.View overwrites the
* jQuery modifiers so using a view is as easy as:
*
* $("#foo").html('mytemplate.ejs',{message: 'hello world'})
*
* This code:
*
* - Loads the template a 'mytemplate.ejs'. It might look like:
* <pre><code><h2><%= message %></h2></pre></code>
*
* - Renders it with {message: 'hello world'}, resulting in:
* <pre><code><div id='foo'>"<h2>hello world</h2></div></pre></code>
*
* - Inserts the result into the foo element. Foo might look like:
* <pre><code><div id='foo'><h2>hello world</h2></div></pre></code>
*
* ## jQuery Modifiers
*
* You can use a template with the following jQuery modifiers:
*
* <table>
* <tr><td>[jQuery.fn.after after]</td><td> <code>$('#bar').after('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.append append] </td><td> <code>$('#bar').append('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.before before] </td><td> <code>$('#bar').before('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.html html] </td><td> <code>$('#bar').html('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.prepend prepend] </td><td> <code>$('#bar').prepend('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.replaceWith replaceWith] </td><td> <code>$('#bar').replaceWith('temp.jaml',{});</code></td></tr>
* <tr><td>[jQuery.fn.text text] </td><td> <code>$('#bar').text('temp.jaml',{});</code></td></tr>
* </table>
*
* You always have to pass a string and an object (or function) for the jQuery modifier
* to user a template.
*
* ## Template Locations
*
* View can load from script tags or from files.
*
* ## From Script Tags
*
* To load from a script tag, create a script tag with your template and an id like:
*
* <pre><code><script type='text/ejs' id='recipes'>
* <% for(var i=0; i < recipes.length; i++){ %>
* <li><%=recipes[i].name %></li>
* <%} %>
* </script></code></pre>
*
* Render with this template like:
*
* @codestart
* $("#foo").html('recipes',recipeData)
* @codeend
*
* Notice we passed the id of the element we want to render.
*
* ## From File
*
* You can pass the path of a template file location like:
*
* $("#foo").html('templates/recipes.ejs',recipeData)
*
* However, you typically want to make the template work from whatever page they
* are called from. To do this, use // to look up templates from JMVC root:
*
* $("#foo").html('//app/views/recipes.ejs',recipeData)
*
* Finally, the [jQuery.Controller.prototype.view controller/view] plugin can make looking
* up a thread (and adding helpers) even easier:
*
* $("#foo").html( this.view('recipes', recipeData) )
*
* ## Packaging Templates
*
* If you're making heavy use of templates, you want to organize
* them in files so they can be reused between pages and applications.
*
* But, this organization would come at a high price
* if the browser has to
* retrieve each template individually. The additional
* HTTP requests would slow down your app.
*
* Fortunately, [steal.static.views steal.views] can build templates
* into your production files. You just have to point to the view file like:
*
* steal.views('path/to/the/view.ejs');
*
* ## Asynchronous
*
* By default, retrieving requests is done synchronously. This is
* fine because StealJS packages view templates with your JS download.
*
* However, some people might not be using StealJS or want to delay loading
* templates until necessary. If you have the need, you can
* provide a callback paramter like:
*
* $("#foo").html('recipes',recipeData, function(result){
* this.fadeIn()
* });
*
* The callback function will be called with the result of the
* rendered template and 'this' will be set to the original jQuery object.
*
* ## Deferreds (3.0.6)
*
* If you pass deferreds to $.View or any of the jQuery
* modifiers, the view will wait until all deferreds resolve before
* rendering the view. This makes it a one-liner to make a request and
* use the result to render a template.
*
* The following makes a request for todos in parallel with the
* todos.ejs template. Once todos and template have been loaded, it with
* render the view with the todos.
*
* $('#todos').html("todos.ejs",Todo.findAll());
*
* ## Just Render Templates
*
* Sometimes, you just want to get the result of a rendered
* template without inserting it, you can do this with $.View:
*
* var out = $.View('path/to/template.jaml',{});
*
* ## Preloading Templates
*
* You can preload templates asynchronously like:
*
* $.get('path/to/template.jaml',{},function(){},'view');
*
* ## Supported Template Engines
*
* JavaScriptMVC comes with the following template languages:
*
* - EmbeddedJS
* <pre><code><h2><%= message %></h2></code></pre>
*
* - JAML
* <pre><code>h2(data.message);</code></pre>
*
* - Micro
* <pre><code><h2>{%= message %}</h2></code></pre>
*
* - jQuery.Tmpl
* <pre><code><h2>${message}</h2></code></pre>
*
* The popular <a href='http://awardwinningfjords.com/2010/08/09/mustache-for-javascriptmvc-3.html'>Mustache</a>
* template engine is supported in a 2nd party plugin.
*
* ## Using other Template Engines
*
* It's easy to integrate your favorite template into $.View and Steal. Read
* how in [jQuery.View.register].
*
* @constructor
*
* Looks up a template, processes it, caches it, then renders the template
* with data and optional helpers.
*
* With [stealjs StealJS], views are typically bundled in the production build.
* This makes it ok to use views synchronously like:
*
* @codestart
* $.View("//myplugin/views/init.ejs",{message: "Hello World"})
* @codeend
*
* If you aren't using StealJS, it's best to use views asynchronously like:
*
* @codestart
* $.View("//myplugin/views/init.ejs",
* {message: "Hello World"}, function(result){
* // do something with result
* })
* @codeend
*
* @param {String} view The url or id of an element to use as the template's source.
* @param {Object} data The data to be passed to the view.
* @param {Object} [helpers] Optional helper functions the view might use. Not all
* templates support helpers.
* @param {Object} [callback] Optional callback function. If present, the template is
* retrieved asynchronously. This is a good idea if you aren't compressing the templates
* into your view.
* @return {String} The rendered result of the view or if deferreds
* are passed, a deferred that will resolve to
* the rendered result of the view.
*/
var $view = $.View = function( view, data, helpers, callback ) {
// if helpers is a function, it is actually a callback
if ( typeof helpers === 'function' ) {
callback = helpers;
helpers = undefined;
}
// see if we got passed any deferreds
var deferreds = getDeferreds(data);
if ( deferreds.length ) { // does data contain any deferreds?
// the deferred that resolves into the rendered content ...
var deferred = $.Deferred();
// add the view request to the list of deferreds
deferreds.push(get(view, true))
// wait for the view and all deferreds to finish
$.when.apply($, deferreds).then(function( resolved ) {
// get all the resolved deferreds
var objs = makeArray(arguments),
// renderer is last [0] is the data
renderer = objs.pop()[0],
// the result of the template rendering with data
result;
// make data look like the resolved deferreds
if ( isDeferred(data) ) {
data = usefulPart(resolved);
}
else {
// go through each prop in data again,
// replace the defferreds with what they resolved to
for ( var prop in data ) {
if ( isDeferred(data[prop]) ) {
data[prop] = usefulPart(objs.shift());
}
}
}
// get the rendered result
result = renderer(data, helpers);
//resolve with the rendered view
deferred.resolve(result);
// if there's a callback, call it back with the result
callback && callback(result);
});
// return the deferred ....
return deferred.promise();
}
else {
// no deferreds, render this bad boy
var response,
// if there's a callback function
async = typeof callback === "function",
// get the 'view' type
deferred = get(view, async);
// if we are async,
if ( async ) {
// return the deferred
response = deferred;
// and callback callback with the rendered result
deferred.done(function( renderer ) {
callback(renderer(data, helpers))
})
} else {
// otherwise, the deferred is complete, so
// set response to the result of the rendering
deferred.done(function( renderer ) {
response = renderer(data, helpers);
});
}
return response;
}
},
// makes sure there's a template, if not, has steal provide a warning
checkText = function( text, url ) {
if (!text.match(/[^\s]/) ) {
throw "$.View ERROR: There is no template or an empty template at " + url;
}
},
// returns a 'view' renderer deferred
// url - the url to the view template
// async - if the ajax request should be synchronous
get = function( url, async ) {
return $.ajax({
url: url,
dataType: "view",
async: async
});
},
// returns true if something looks like a deferred
isDeferred = function( obj ) {
return obj && $.isFunction(obj.always) // check if obj is a $.Deferred
},
// gets an array of deferreds from an object
// this only goes one level deep
getDeferreds = function( data ) {
var deferreds = [];
// pull out deferreds
if ( isDeferred(data) ) {
return [data]
} else {
for ( var prop in data ) {
if ( isDeferred(data[prop]) ) {
deferreds.push(data[prop]);
}
}
}
return deferreds;
},
// gets the useful part of deferred
// this is for Models and $.ajax that resolve to array (with success and such)
// returns the useful, content part
usefulPart = function( resolved ) {
return $.isArray(resolved) && resolved.length === 3 && resolved[1] === 'success' ? resolved[0] : resolved
};
// you can request a view renderer (a function you pass data to and get html)
// Creates a 'view' transport. These resolve to a 'view' renderer
// a 'view' renderer takes data and returns a string result.
// For example:
//
// $.ajax({dataType : 'view', src: 'foo.ejs'}).then(function(renderer){
// renderer({message: 'hello world'})
// })
$.ajaxTransport("view", function( options, orig ) {
// the url (or possibly id) of the view content
var url = orig.url,
// check if a suffix exists (ex: "foo.ejs")
suffix = url.match(/\.[\w\d]+$/),
type,
// if we are reading a script element for the content of the template
// el will be set to that script element
el,
// a unique identifier for the view (used for caching)
// this is typically derived from the element id or
// the url for the template
id,
// the AJAX request used to retrieve the template content
jqXHR,
// used to generate the response
response = function( text ) {
// get the renderer function
var func = type.renderer(id, text);
// cache if if we are caching
if ( $view.cache ) {
$view.cached[id] = func;
}
// return the objects for the response's dataTypes
// (in this case view)
return {
view: func
};
};
// if we have an inline template, derive the suffix from the 'text/???' part
// this only supports '<script></script>' tags
if ( el = document.getElementById(url) ) {
suffix = "."+el.type.match(/\/(x\-)?(.+)/)[2];
}
// if there is no suffix, add one
if (!suffix ) {
suffix = $view.ext;
url = url + $view.ext;
}
// convert to a unique and valid id
id = toId(url);
// if a absolute path, use steal to get it
// you should only be using // if you are using steal
if ( url.match(/^\/\//) ) {
var sub = url.substr(2);
url = typeof steal === "undefined" ?
url = "/" + sub :
steal.root.mapJoin(sub) +'';
}
//set the template engine type
type = $view.types[suffix];
// !-- FOUNDRY HACK --! //
// Retrieve templates stored within $.template
var template = $.template()[orig.url];
// return the ajax transport contract: http://api.jquery.com/extending-ajax/
return {
send: function( headers, callback ) {
// !-- FOUNDRY HACK --! //
// Retrieve templates stored within $.template
if ( template ) {
type = $view.types["." + template.type];
return callback(200, "success", response(template.content));
// if it is cached,
} else if ( $view.cached[id] ) {
// return the catched renderer
return callback(200, "success", {
view: $view.cached[id]
});
// otherwise if we are getting this from a script elment
} else if ( el ) {
// resolve immediately with the element's innerHTML
callback(200, "success", response(el.innerHTML));
} else {
// make an ajax request for text
jqXHR = $.ajax({
async: orig.async,
url: url,
dataType: "text",
error: function() {
checkText("", url);
callback(404);
},
success: function( text ) {
// make sure we got some text back
checkText(text, url);
// cache and send back text
callback(200, "success", response(text))
}
});
}
},
abort: function() {
jqXHR && jqXHR.abort();
}
}
})
$.extend($view, {
/**
* @attribute hookups
* @hide
* A list of pending 'hookups'
*/
hookups: {},
/**
* @function hookup
* Registers a hookup function that can be called back after the html is
* put on the page. Typically this is handled by the template engine. Currently
* only EJS supports this functionality.
*
* var id = $.View.hookup(function(el){
* //do something with el
* }),
* html = "<div data-view-id='"+id+"'>"
* $('.foo').html(html);
*
*
* @param {Function} cb a callback function to be called with the element
* @param {Number} the hookup number
*/
hookup: function( cb ) {
var myid = ++id;
$view.hookups[myid] = cb;
return myid;
},
/**
* @attribute cached
* @hide
* Cached are put in this object
*/
cached: {},
/**
* @attribute cache
* Should the views be cached or reloaded from the server. Defaults to true.
*/
cache: true,
/**
* @function register
* Registers a template engine to be used with
* view helpers and compression.
*
* ## Example
*
* @codestart
* $.View.register({
* suffix : "tmpl",
* plugin : "jquery/view/tmpl",
* renderer: function( id, text ) {
* return function(data){
* return jQuery.render( text, data );
* }
* },
* script: function( id, text ) {
* var tmpl = $.tmpl(text).toString();
* return "function(data){return ("+
* tmpl+
* ").call(jQuery, jQuery, data); }";
* }
* })
* @codeend
* Here's what each property does:
*
* * plugin - the location of the plugin
* * suffix - files that use this suffix will be processed by this template engine
* * renderer - returns a function that will render the template provided by text
* * script - returns a string form of the processed template function.
*
* @param {Object} info a object of method and properties
*
* that enable template integration:
* <ul>
* <li>plugin - the location of the plugin. EX: 'jquery/view/ejs'</li>
* <li>suffix - the view extension. EX: 'ejs'</li>
* <li>script(id, src) - a function that returns a string that when evaluated returns a function that can be
* used as the render (i.e. have func.call(data, data, helpers) called on it).</li>
* <li>renderer(id, text) - a function that takes the id of the template and the text of the template and
* returns a render function.</li>
* </ul>
*/
register: function( info ) {
this.types["." + info.suffix] = info;
if ( window.steal ) {
steal.type(info.suffix + " view js", function( options, success, error ) {
var type = $view.types["." + options.type],
id = toId(options.rootSrc+'');
options.text = type.script(id, options.text)
success();
})
}
},
types: {},
/**
* @attribute ext
* The default suffix to use if none is provided in the view's url.
* This is set to .ejs by default.
*/
ext: ".ejs",
/**
* Returns the text that
* @hide
* @param {Object} type
* @param {Object} id
* @param {Object} src
*/
registerScript: function( type, id, src ) {
return "$.View.preload('" + id + "'," + $view.types["." + type].script(id, src) + ");";
},
/**
* @hide
* Called by a production script to pre-load a renderer function
* into the view cache.
* @param {String} id
* @param {Function} renderer
*/
preload: function( id, renderer ) {
$view.cached[id] = function( data, helpers ) {
return renderer.call(data, data, helpers);
};
}
});
if ( window.steal ) {
steal.type("view js", function( options, success, error ) {
var type = $view.types["." + options.type],
id = toId(options.rootSrc+'');
options.text = "steal('" + (type.plugin || "jquery/view/" + options.type) + "').then(function($){" + "$.View.preload('" + id + "'," + options.text + ");\n})";
success();
})
}
//---- ADD jQUERY HELPERS -----
//converts jquery functions to use views
var convert, modify, isTemplate, isHTML, isDOM, getCallback, hookupView, funcs,
// text and val cannot produce an element, so don't run hookups on them
noHookup = {'val':true,'text':true};
convert = function( func_name ) {
// save the old jQuery helper
var old = $.fn[func_name];
// replace it wiht our new helper
$.fn[func_name] = function() {
var args = makeArray(arguments),
callbackNum,
callback,
self = this,
result;
// if the first arg is a deferred
// wait until it finishes, and call
// modify with the result
if ( isDeferred(args[0]) ) {
args[0].done(function( res ) {
modify.call(self, [res], old);
})
return this;
}
//check if a template
else if ( isTemplate(args) ) {
// if we should operate async
if ((callbackNum = getCallback(args))) {
callback = args[callbackNum];
args[callbackNum] = function( result ) {
modify.call(self, [result], old);
callback.call(self, result);
};
$view.apply($view, args);
return this;
}
// call view with args (there might be deferreds)
result = $view.apply($view, args);
// if we got a string back
if (!isDeferred(result) ) {
// we are going to call the old method with that string
args = [result];
} else {
// if there is a deferred, wait until it is done before calling modify
result.done(function( res ) {
modify.call(self, [res], old);
})
return this;
}
}
return noHookup[func_name] ? old.apply(this,args) :
modify.call(this, args, old);
};
};
// modifies the content of the element
// but also will run any hookup
modify = function( args, old ) {
var res, stub, hooks;
//check if there are new hookups
for ( var hasHookups in $view.hookups ) {
break;
}
//if there are hookups, get jQuery object
if ( hasHookups && args[0] && isHTML(args[0]) ) {
hooks = $view.hookups;
$view.hookups = {};
args[0] = $(args[0]);
}
res = old.apply(this, args);
//now hookup the hookups
if ( hooks
/* && args.length*/
) {
hookupView(args[0], hooks);
}
return res;
};
// returns true or false if the args indicate a template is being used
// $('#foo').html('/path/to/template.ejs',{data})
// in general, we want to make sure the first arg is a string
// and the second arg is data
isTemplate = function( args ) {
// save the second arg type
var secArgType = typeof args[1];
// the first arg is a string
return typeof args[0] == "string" &&
// the second arg is an object or function
(secArgType == 'object' || secArgType == 'function') &&
// but it is not a dom element
!isDOM(args[1]);
};
// returns true if the arg is a jQuery object or HTMLElement
isDOM = function(arg){
return arg.nodeType || arg.jquery
};
// returns whether the argument is some sort of HTML data
isHTML = function( arg ) {
if ( isDOM(arg) ) {
// if jQuery object or DOM node we're good
return true;
} else if ( typeof arg === "string" ) {
// if string, do a quick sanity check that we're HTML
arg = $.trim(arg);
return arg.substr(0, 1) === "<" && arg.substr(arg.length - 1, 1) === ">" && arg.length >= 3;
} else {
// don't know what you are
return false;
}
};
//returns the callback arg number if there is one (for async view use)
getCallback = function( args ) {
return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2;
};
hookupView = function( els, hooks ) {
//remove all hookups
var hookupEls, len, i = 0,
id, func;
els = els.filter(function() {
return this.nodeType != 3; //filter out text nodes
})
hookupEls = els.add("[data-view-id]", els);
len = hookupEls.length;
for (; i < len; i++ ) {
if ( hookupEls[i].getAttribute && (id = hookupEls[i].getAttribute('data-view-id')) && (func = hooks[id]) ) {
func(hookupEls[i], id);
delete hooks[id];
hookupEls[i].removeAttribute('data-view-id');
}
}
//copy remaining hooks back
$.extend($view.hookups, hooks);
};
/**
* @add jQuery.fn
* @parent jQuery.View
* Called on a jQuery collection that was rendered with $.View with pending hookups. $.View can render a
* template with hookups, but not actually perform the hookup, because it returns a string without actual DOM
* elements to hook up to. So hookup performs the hookup and clears the pending hookups, preventing errors in
* future templates.
*
* @codestart
* $($.View('//views/recipes.ejs',recipeData)).hookup()
* @codeend
*/
$.fn.hookup = function() {
var hooks = $view.hookups;
$view.hookups = {};
hookupView(this, hooks);
return this;
};
/**
* @add jQuery.fn
*/
$.each([
/**
* @function prepend
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/prepend/ jQuery().prepend()]
* to render [jQuery.View] templates inserted at the beginning of each element in the set of matched elements.
*
* $('#test').prepend('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"prepend",
/**
* @function append
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/append/ jQuery().append()]
* to render [jQuery.View] templates inserted at the end of each element in the set of matched elements.
*
* $('#test').append('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"append",
/**
* @function after
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/after/ jQuery().after()]
* to render [jQuery.View] templates inserted after each element in the set of matched elements.
*
* $('#test').after('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"after",
/**
* @function before
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/before/ jQuery().before()]
* to render [jQuery.View] templates inserted before each element in the set of matched elements.
*
* $('#test').before('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"before",
/**
* @function text
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/text/ jQuery().text()]
* to render [jQuery.View] templates as the content of each matched element.
* Unlike [jQuery.fn.html] jQuery.fn.text also works with XML, escaping the provided
* string as necessary.
*
* $('#test').text('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"text",
/**
* @function html
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/html/ jQuery().html()]
* to render [jQuery.View] templates as the content of each matched element.
*
* $('#test').html('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"html",
/**
* @function replaceWith
* @parent jQuery.View
*
* Extending the original [http://api.jquery.com/replaceWith/ jQuery().replaceWith()]
* to render [jQuery.View] templates replacing each element in the set of matched elements.
*
* $('#test').replaceWith('path/to/template.ejs', { name : 'javascriptmvc' });
*
* @param {String|Object|Function} content A template filename or the id of a view script tag
* or a DOM element, array of elements, HTML string, or jQuery object.
* @param {Object} [data] The data to render the view with.
* If rendering a view template this parameter always has to be present
* (use the empty object initializer {} for no data).
*/
"replaceWith", "val"],function(i, func){
convert(func);
});
//go through helper funcs and convert
})();(function(){
// HELPER METHODS ==============
var myEval = function( script ) {
eval(script);
},
// removes the last character from a string
// this is no longer needed
// chop = function( string ) {
// return string.substr(0, string.length - 1);
//},
rSplit = $.String.rsplit,
extend = $.extend,
isArray = $.isArray,
// regular expressions for caching
returnReg = /\r\n/g,
retReg = /\r/g,
newReg = /\n/g,
nReg = /\n/,
slashReg = /\\/g,
quoteReg = /"/g,
singleQuoteReg = /'/g,
tabReg = /\t/g,
leftBracket = /\{/g,
rightBracket = /\}/g,
quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/,
// escapes characters starting with \
clean = function( content ) {
return content.replace(slashReg, '\\\\').replace(newReg, '\\n').replace(quoteReg, '\\"').replace(tabReg, '\\t');
},
// escapes html
// - from prototype http://www.prototypejs.org/
escapeHTML = function( content ) {
return content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(quoteReg, '"').replace(singleQuoteReg, "'");
},
$View = $.View,
bracketNum = function(content){
var lefts = content.match(leftBracket),
rights = content.match(rightBracket);
return (lefts ? lefts.length : 0) -
(rights ? rights.length : 0);
},
/**
* @class jQuery.EJS
*
* @plugin jquery/view/ejs
* @parent jQuery.View
* @download http://jmvcsite.heroku.com/pluginify?plugins[]=jquery/view/ejs/ejs.js
* @test jquery/view/ejs/qunit.html
*
*
* Ejs provides <a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/">ERB</a>
* style client side templates. Use them with controllers to easily build html and inject
* it into the DOM.
*
* ### Example
*
* The following generates a list of tasks:
*
* @codestart html
* <ul>
* <% for(var i = 0; i < tasks.length; i++){ %>
* <li class="task <%= tasks[i].identity %>"><%= tasks[i].name %></li>
* <% } %>
* </ul>
* @codeend
*
* For the following examples, we assume this view is in <i>'views\tasks\list.ejs'</i>.
*
*
* ## Use
*
* ### Loading and Rendering EJS:
*
* You should use EJS through the helper functions [jQuery.View] provides such as:
*
* - [jQuery.fn.after after]
* - [jQuery.fn.append append]
* - [jQuery.fn.before before]
* - [jQuery.fn.html html],
* - [jQuery.fn.prepend prepend],
* - [jQuery.fn.replaceWith replaceWith], and
* - [jQuery.fn.text text].
*
* or [jQuery.Controller.prototype.view].
*
* ### Syntax
*
* EJS uses 5 types of tags:
*
* - <code><% CODE %></code> - Runs JS Code.
* For example:
*
* <% alert('hello world') %>
*
* - <code><%= CODE %></code> - Runs JS Code and writes the _escaped_ result into the result of the template.
* For example:
*
* <h1><%= 'hello world' %></h1>
*
* - <code><%== CODE %></code> - Runs JS Code and writes the _unescaped_ result into the result of the template.
* For example:
*
* <h1><%== '<span>hello world</span>' %></h1>
*
* - <code><%%= CODE %></code> - Writes <%= CODE %> to the result of the template. This is very useful for generators.
*
* <%%= 'hello world' %>
*
* - <code><%# CODE %></code> - Used for comments. This does nothing.
*
* <%# 'hello world' %>
*
* ## Hooking up controllers
*
* After drawing some html, you often want to add other widgets and plugins inside that html.
* View makes this easy. You just have to return the Contoller class you want to be hooked up.
*
* @codestart
* <ul <%= Mxui.Tabs%>>...<ul>
* @codeend
*
* You can even hook up multiple controllers:
*
* @codestart
* <ul <%= [Mxui.Tabs, Mxui.Filler]%>>...<ul>
* @codeend
*
* To hook up a controller with options or any other jQuery plugin use the
* [jQuery.EJS.Helpers.prototype.plugin | plugin view helper]:
*
* @codestart
* <ul <%= plugin('mxui_tabs', { option: 'value' }) %>>...<ul>
* @codeend
*
* Don't add a semicolon when using view helpers.
*
*
* <h2>View Helpers</h2>
* View Helpers return html code. View by default only comes with
* [jQuery.EJS.Helpers.prototype.view view] and [jQuery.EJS.Helpers.prototype.text text].
* You can include more with the view/helpers plugin. But, you can easily make your own!
* Learn how in the [jQuery.EJS.Helpers Helpers] page.
*
* @constructor Creates a new view
* @param {Object} options A hash with the following options
* <table class="options">
* <tbody><tr><th>Option</th><th>Default</th><th>Description</th></tr>
* <tr>
* <td>text</td>
* <td> </td>
* <td>uses the provided text as the template. Example:<br/><code>new View({text: '<%=user%>'})</code>
* </td>
* </tr>
* <tr>
* <td>type</td>
* <td>'<'</td>
* <td>type of magic tags. Options are '<' or '['
* </td>
* </tr>
* <tr>
* <td>name</td>
* <td>the element ID or url </td>
* <td>an optional name that is used for caching.
* </td>
* </tr>
* </tbody></table>
*/
EJS = function( options ) {
// If called without new, return a function that
// renders with data and helpers like
// EJS({text: '<%= message %>'})({message: 'foo'});
// this is useful for steal's build system
if ( this.constructor != EJS ) {
var ejs = new EJS(options);
return function( data, helpers ) {
return ejs.render(data, helpers);
};
}
// if we get a function directly, it probably is coming from
// a steal-packaged view
if ( typeof options == "function" ) {
this.template = {
fn: options
};
return;
}
//set options on self
extend(this, EJS.options, options);
this.template = compile(this.text, this.type, this.name);
};
// add EJS to jQuery if it exists
$ && ($.EJS = EJS);
/**
* @Prototype
*/
EJS.prototype.
/**
* Renders an object with view helpers attached to the view.
*
* new EJS({text: "<%= message %>"}).render({
* message: "foo"
* },{helper: function(){ ... }})
*
* @param {Object} object data to be rendered
* @param {Object} [extraHelpers] an object with view helpers
* @return {String} returns the result of the string
*/
render = function( object, extraHelpers ) {
object = object || {};
this._extra_helpers = extraHelpers;
var v = new EJS.Helpers(object, extraHelpers || {});
return this.template.fn.call(object, object, v);
};
/**
* @Static
*/
extend(EJS, {
/**
* Used to convert what's in <%= %> magic tags to a string
* to be inserted in the rendered output.
*
* Typically, it's a string, and the string is just inserted. However,
* if it's a function or an object with a hookup method, it can potentially be
* be ran on the element after it's inserted into the page.
*
* This is a very nice way of adding functionality through the view.
* Usually this is done with [jQuery.EJS.Helpers.prototype.plugin]
* but the following fades in the div element after it has been inserted:
*
* @codestart
* <%= function(el){$(el).fadeIn()} %>
* @codeend
*
* @param {String|Object|Function} input the value in between the
* write magic tags: <%= %>
* @return {String} returns the content to be added to the rendered
* output. The content is different depending on the type:
*
* * string - the original string
* * null or undefined - the empty string ""
* * an object with a hookup method - the attribute "data-view-id='XX'", where XX is a hookup number for jQuery.View
* * a function - the attribute "data-view-id='XX'", where XX is a hookup number for jQuery.View
* * an array - the attribute "data-view-id='XX'", where XX is a hookup number for jQuery.View
*/
text: function( input ) {
// if it's a string, return
if ( typeof input == 'string' ) {
return input;
}
// if has no value
if ( input === null || input === undefined ) {
return '';
}
// if it's an object, and it has a hookup method
var hook = (input.hookup &&
// make a function call the hookup method
function( el, id ) {
input.hookup.call(input, el, id);
}) ||
// or if it's a function, just use the input
(typeof input == 'function' && input) ||
// of it its an array, make a function that calls hookup or the function
// on each item in the array
(isArray(input) &&
function( el, id ) {
for ( var i = 0; i < input.length; i++ ) {
input[i].hookup ? input[i].hookup(el, id) : input[i](el, id);
}
});
// finally, if there is a funciton to hookup on some dom
// pass it to hookup to get the data-view-id back
if ( hook ) {
return "data-view-id='" + $View.hookup(hook) + "'";
}
// finally, if all else false, toString it
return input.toString ? input.toString() : "";
},
/**
* Escapes the text provided as html if it's a string.
* Otherwise, the value is passed to EJS.text(text).
*
* @param {String|Object|Array|Function} text to escape. Otherwise,
* the result of [jQuery.EJS.text] is returned.
* @return {String} the escaped text or likely a $.View data-view-id attribute.
*/
clean: function( text ) {
//return sanatized text
if ( typeof text == 'string' ) {
return escapeHTML(text);
} else if ( typeof text == 'number' ) {
return text;
} else {
return EJS.text(text);
}
},
/**
* @attribute options
* Sets default options for all views.
*
* $.EJS.options.type = '['
*
* Only one option is currently supported: type.
*
* Type is the left hand magic tag.
*/
options: {
type: '[',
ext: '.ejs'
}
});
// ========= SCANNING CODE =========
// Given a scanner, and source content, calls block with each token
// scanner - an object of magicTagName : values
// source - the source you want to scan
// block - function(token, scanner), called with each token
var scan = function( scanner, source, block ) {
// split on /\n/ to have new lines on their own line.
var source_split = rSplit(source, nReg),
i = 0;
for (; i < source_split.length; i++ ) {
scanline(scanner, source_split[i], block);
}
},
scanline = function( scanner, line, block ) {
scanner.lines++;
var line_split = rSplit(line, scanner.splitter),
token;
for ( var i = 0; i < line_split.length; i++ ) {
token = line_split[i];
if ( token !== null ) {
block(token, scanner);
}
}
},
// creates a 'scanner' object. This creates
// values for the left and right magic tags
// it's splitter property is a regexp that splits content
// by all tags
makeScanner = function( left, right ) {
var scanner = {};
extend(scanner, {
left: left + '%',
right: '%' + right,
dLeft: left + '%%',
dRight: '%%' + right,
eeLeft : left + '%==',
eLeft: left + '%=',
cmnt: left + '%#',
cleanLeft: left+"%~",
scan: scan,
lines: 0
});
scanner.splitter = new RegExp("(" + [scanner.dLeft, scanner.dRight, scanner.eeLeft, scanner.eLeft, scanner.cmnt, scanner.left, scanner.right + '\n', scanner.right, '\n'].join(")|(").
replace(/\[/g, "\\[").replace(/\]/g, "\\]") + ")");
return scanner;
},
// compiles a template where
// source - template text
// left - the left magic tag
// name - the name of the template (for debugging)
// returns an object like: {out : "", fn : function(){ ... }} where
// out - the converted JS source of the view
// fn - a function made from the JS source
compile = function( source, left, name ) {
// make everything only use \n
source = source.replace(returnReg, "\n").replace(retReg, "\n");
// if no left is given, assume <
left = left || '[';
// put and insert cmds are used for adding content to the template
// currently they are identical, I am not sure why
var put_cmd = "___v1ew.push(",
insert_cmd = put_cmd,
// the text that starts the view code (or block function)
startTxt = 'var ___v1ew = [];',
// the text that ends the view code (or block function)
finishTxt = "return ___v1ew.join('')",
// initialize a buffer
buff = new EJS.Buffer([startTxt], []),
// content is used as the current 'processing' string
// this is the content between magic tags
content = '',
// adds something to be inserted into the view template
// this comes out looking like __v1ew.push("CONENT")
put = function( content ) {
buff.push(put_cmd, '"', clean(content), '");');
},
// the starting magic tag
startTag = null,
// cleans the running content
empty = function() {
content = ''
},
// what comes after clean or text
doubleParen = "));",
// a stack used to keep track of how we should end a bracket }
// once we have a <%= %> with a leftBracket
// we store how the file should end here (either '))' or ';' )
endStack =[];
// start going token to token
scan(makeScanner(left, left === '[' ? ']' : '>'), source || "", function( token, scanner ) {
// if we don't have a start pair
var bn;
if ( startTag === null ) {
switch ( token ) {
case '\n':
content = content + "\n";
put(content);
buff.cr();
empty();
break;
// set start tag, add previous content (if there is some)
// clean content
case scanner.left:
case scanner.eLeft:
case scanner.eeLeft:
case scanner.cmnt:
// a new line, just add whatever content w/i a clean
// reset everything
startTag = token;
if ( content.length > 0 ) {
put(content);
}
empty();
break;
case scanner.dLeft:
// replace <%% with <%
content += scanner.left;
break;
default:
content += token;
break;
}
}
else {
//we have a start tag
switch ( token ) {
case scanner.right:
// %>
switch ( startTag ) {
case scanner.left:
// <%
// get the number of { minus }
bn = bracketNum(content);
// how are we ending this statement
var last =
// if the stack has value and we are ending a block
endStack.length && bn == -1 ?
// use the last item in the block stack
endStack.pop() :
// or use the default ending
";";
// if we are ending a returning block
// add the finish text which returns the result of the
// block
if(last === doubleParen) {
buff.push(finishTxt)
}
// add the remaining content
buff.push(content, last);
// if we have a block, start counting
if(bn === 1 ){
endStack.push(";")
}
break;
case scanner.eLeft:
// <%= clean content
bn = bracketNum(content);
if( bn ) {
endStack.push(doubleParen)
}
if(quickFunc.test(content)){
var parts = content.match(quickFunc)
content = "function(__){var "+parts[1]+"=$(__);"+parts[2]+"}"
}
buff.push(insert_cmd, $.globalNamespace + ".EJS.clean(", content,bn ? startTxt : doubleParen);
break;
case scanner.eeLeft:
// <%== content
// get the number of { minus }
bn = bracketNum(content);
// if we have more {, it means there is a block
if( bn ){
// when we return to the same # of { vs } end wiht a doubleParen
endStack.push(doubleParen)
}
buff.push(insert_cmd, $.globalNamespace + ".EJS.text(", content,
// if we have a block
bn ?
// start w/ startTxt "var _v1ew = [])"
startTxt :
// if not, add doubleParent to close push and text
doubleParen
);
break;
}
startTag = null;
empty();
break;
case scanner.dRight:
content += scanner.right;
break;
default:
content += token;
break;
}
}
})
if ( content.length > 0 ) {
// Should be content.dump in Ruby
buff.push(put_cmd, '"', clean(content) + '");');
}
var template = buff.close(),
out = {
out: 'try { with(_VIEW) { with (_CONTEXT) {' + template + " "+finishTxt+"}}}catch(e){e.lineNumber=null;throw e;}"
};
//use eval instead of creating a function, b/c it is easier to debug
// myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){' + out.out + '});\r\n//@ sourceURL=' + name + ".js");
// !-- FOUNDRY HACK --! //
// Removed //@ sourceURL as it will break with conditional compilation turned on in IE.
myEval.call(out, 'this.fn = (function(_CONTEXT,_VIEW){ var $ = ' + $.globalNamespace + ';' + out.out + '});');
return out;
};
// A Buffer used to add content to.
// This is useful for performance and simplifying the
// code above.
// We also can use this so we know line numbers when there
// is an error.
// pre_cmd - code that sets up the buffer
// post - code that finalizes the buffer
EJS.Buffer = function( pre_cmd, post ) {
// the current line we are on
this.line = [];
// the combined content added to this buffer
this.script = [];
// content at the end of the buffer
this.post = post;
// add the pre commands to the first line
this.push.apply(this, pre_cmd);
};
EJS.Buffer.prototype = {
// add content to this line
// need to maintain your own semi-colons (for performance)
push: function() {
this.line.push.apply(this.line, arguments);
},
// starts a new line
cr: function() {
this.script.push(this.line.join(''), "\n");
this.line = [];
},
//returns the script too
close: function() {
// if we have ending line content, add it to the script
if ( this.line.length > 0 ) {
this.script.push(this.line.join(''));
this.line = [];
}
// if we have ending content, add it
this.post.length && this.push.apply(this, this.post);
// always end in a ;
this.script.push(";");
return this.script.join("");
}
};
/**
* @class jQuery.EJS.Helpers
* @parent jQuery.EJS
* By adding functions to jQuery.EJS.Helpers.prototype, those functions will be available in the
* views.
*
* The following helper converts a given string to upper case:
*
* $.EJS.Helpers.prototype.toUpper = function(params)
* {
* return params.toUpperCase();
* }
*
* Use it like this in any EJS template:
*
* <%= toUpper('javascriptmvc') %>
*
* To access the current DOM element return a function that takes the element as a parameter:
*
* $.EJS.Helpers.prototype.upperHtml = function(params)
* {
* return function(el) {
* $(el).html(params.toUpperCase());
* }
* }
*
* In your EJS view you can then call the helper on an element tag:
*
* <div <%= upperHtml('javascriptmvc') %>></div>
*
*
* @constructor Creates a view helper. This function
* is called internally. You should never call it.
* @param {Object} data The data passed to the
* view. Helpers have access to it through this._data
*/
EJS.Helpers = function( data, extras ) {
this._data = data;
this._extras = extras;
extend(this, extras);
};
/**
* @prototype
*/
EJS.Helpers.prototype = {
/**
* Hooks up a jQuery plugin on.
* @param {String} name the plugin name
*/
plugin: function( name ) {
var args = $.makeArray(arguments),
widget = args.shift();
return function( el ) {
var jq = $(el);
jq[widget].apply(jq, args);
};
},
/**
* Renders a partial view. This is deprecated in favor of <code>$.View()</code>.
*/
view: function( url, data, helpers ) {
helpers = helpers || this._extras;
data = data || this._data;
return $View(url, data, helpers); //new EJS(options).render(data, helpers);
}
};
// options for steal's build
$View.register({
suffix: "ejs",
//returns a function that renders the view
script: function( id, src ) {
return $.globalNamespace + ".EJS(function(_CONTEXT,_VIEW) { " + new EJS({
text: src,
name: id
}).template.out + " })";
},
renderer: function( id, text ) {
return EJS({
text: text,
name: id
});
}
});
})();(function(){
// Alias helpful methods from jQuery
var isArray = $.isArray,
isObject = function( obj ) {
return typeof obj === 'object' && obj !== null && obj;
},
makeArray = $.makeArray,
each = $.each,
// listens to changes on val and 'bubbles' the event up
// - val the object to listen to changes on
// - prop the property name val is at on
// - parent the parent object of prop
hookup = function( val, prop, parent ) {
// if it's an array make a list, otherwise a val
if (val instanceof $.Observe){
// we have an observe already
// make sure it is not listening to this already
unhookup([val], parent._namespace)
} else if ( isArray(val) ) {
val = new $.Observe.List(val)
} else {
val = new $.Observe(val)
}
// attr (like target, how you (delegate) to get to the target)
// currentAttr (how to get to you)
// delegateAttr (hot to get to the delegated Attr)
//
//
//listen to all changes and trigger upwards
val.bind("change" + parent._namespace, function( ev, attr ) {
// trigger the type on this ...
var args = $.makeArray(arguments),
ev = args.shift();
if(prop === "*"){
args[0] = parent.indexOf(val)+"." + args[0]
} else {
args[0] = prop + "." + args[0]
}
// change the attr
//ev.origTarget = ev.origTarget || ev.target;
// the target should still be the original object ...
$.event.trigger(ev, args, parent)
});
return val;
},
unhookup = function(items, namespace){
var item;
for(var i =0; i < items.length; i++){
item = items[i]
if( item && item.unbind ){
item.unbind("change" + namespace)
}
}
},
// an id to track events for a given observe
id = 0,
collecting = null,
// call to start collecting events (Observe sends all events at once)
collect = function() {
if (!collecting ) {
collecting = [];
return true;
}
},
// creates an event on item, but will not send immediately
// if collecting events
// - item - the item the event should happen on
// - event - the event name ("change")
// - args - an array of arguments
trigger = function( item, event, args ) {
// send no events if initalizing
if (item._init) {
return;
}
if (!collecting ) {
return $.event.trigger(event, args, item, true)
} else {
collecting.push({
t: item,
ev: event,
args: args
})
}
},
// which batch of events this is for, might not want to send multiple
// messages on the same batch. This is mostly for
// event delegation
batchNum = 0,
// sends all pending events
sendCollection = function() {
var len = collecting.length,
items = collecting.slice(0),
cur;
collecting = null;
batchNum ++;
for ( var i = 0; i < len; i++ ) {
cur = items[i];
// batchNum
$.event.trigger({
type: cur.ev,
batchNum : batchNum
}, cur.args, cur.t)
}
},
// a helper used to serialize an Observe or Observe.List where:
// observe - the observable
// how - to serialize with 'attrs' or 'serialize'
// where - to put properties, in a {} or [].
serialize = function( observe, how, where ) {
// go through each property
observe.each(function( name, val ) {
// if the value is an object, and has a attrs or serialize function
where[name] = isObject(val) && typeof val[how] == 'function' ?
// call attrs or serialize to get the original data back
val[how]() :
// otherwise return the value
val
})
return where;
};
/**
* @class jQuery.Observe
* @parent jquerymx.lang
* @test jquery/lang/observe/qunit.html
*
* Observe provides the awesome observable pattern for
* JavaScript Objects and Arrays. It lets you
*
* - Set and remove property or property values on objects and arrays
* - Listen for changes in objects and arrays
* - Work with nested properties
*
* ## Creating an $.Observe
*
* To create an $.Observe, or $.Observe.List, you can simply use
* the `$.O(data)` shortcut like:
*
* var person = $.O({name: 'justin', age: 29}),
* hobbies = $.O(['programming', 'basketball', 'nose picking'])
*
* Depending on the type of data passed to $.O, it will create an instance of either:
*
* - $.Observe, which is used for objects like: `{foo: 'bar'}`, and
* - [jQuery.Observe.List $.Observe.List], which is used for arrays like `['foo','bar']`
*
* $.Observe.List and $.Observe are very similar. In fact,
* $.Observe.List inherits $.Observe and only adds a few extra methods for
* manipulating arrays like [jQuery.Observe.List.prototype.push push]. Go to
* [jQuery.Observe.List $.Observe.List] for more information about $.Observe.List.
*
* You can also create a `new $.Observe` simply by pass it the data you want to observe:
*
* var data = {
* addresses : [
* {
* city: 'Chicago',
* state: 'IL'
* },
* {
* city: 'Boston',
* state : 'MA'
* }
* ],
* name : "Justin Meyer"
* },
* o = new $.Observe(data);
*
* _o_ now represents an observable copy of _data_.
*
* ## Getting and Setting Properties
*
* Use [jQuery.Observe.prototype.attr attr] and [jQuery.Observe.prototype.attr attrs]
* to get and set properties.
*
* For example, you can read the property values of _o_ with
* `observe.attr( name )` like:
*
* // read name
* o.attr('name') //-> Justin Meyer
*
* And set property names of _o_ with
* `observe.attr( name, value )` like:
*
* // update name
* o.attr('name', "Brian Moschel") //-> o
*
* Observe handles nested data. Nested Objects and
* Arrays are converted to $.Observe and
* $.Observe.Lists. This lets you read nested properties
* and use $.Observe methods on them. The following
* updates the second address (Boston) to 'New York':
*
* o.attr('addresses.1').attrs({
* city: 'New York',
* state: 'NY'
* })
*
* `attrs()` can be used to get all properties back from the observe:
*
* o.attrs() // ->
* {
* addresses : [
* {
* city: 'Chicago',
* state: 'IL'
* },
* {
* city: 'New York',
* state : 'MA'
* }
* ],
* name : "Brian Moschel"
* }
*
* ## Listening to property changes
*
* When a property value is changed, it creates events
* that you can listen to. There are two ways to listen
* for events:
*
* - [jQuery.Observe.prototype.bind bind] - listen for any type of change
* - [jQuery.Observe.prototype.delegate delegate] - listen to a specific type of change
*
* With `bind( "change" , handler( ev, attr, how, newVal, oldVal ) )`, you can listen
* to any change that happens within the
* observe. The handler gets called with the property name that was
* changed, how it was changed ['add','remove','set'], the new value
* and the old value.
*
* o.bind('change', function( ev, attr, how, nevVal, oldVal ) {
*
* })
*
* `delegate( attr, event, handler(ev, newVal, oldVal ) )` lets you listen
* to a specific event on a specific attribute.
*
* // listen for name changes
* o.delegate("name","set", function(){
*
* })
*
* Delegate lets you specify multiple attributes and values to match
* for the callback. For example,
*
* r = $.O({type: "video", id : 5})
* r.delegate("type=images id","set", function(){})
*
* This is used heavily by [jQuery.route $.route].
*
* @constructor
*
* @param {Object} obj a JavaScript Object that will be
* converted to an observable
*/
$.Class($.globalNamespace + '.Observe',
/**
* @prototype
*/
{
init: function( obj ) {
// _data is where we keep the properties
this._data = {};
// the namespace this object uses to listen to events
this._namespace = ".observe" + (++id);
// sets all attrs
this._init = true;
this.attrs(obj);
delete this._init;
},
/**
* Get or set an attribute on the observe.
*
* o = new $.Observe({});
*
* // sets a user property
* o.attr('user',{name: 'hank'});
*
* // read the user's name
* o.attr('user.name') //-> 'hank'
*
* If a value is set for the first time, it will trigger
* an `'add'` and `'set'` change event. Once
* the value has been added. Any future value changes will
* trigger only `'set'` events.
*
*
* @param {String} attr the attribute to read or write.
*
* o.attr('name') //-> reads the name
* o.attr('name', 'Justin') //-> writes the name
*
* You can read or write deep property names. For example:
*
* o.attr('person', {name: 'Justin'})
* o.attr('person.name') //-> 'Justin'
*
* @param {Object} [val] if provided, sets the value.
* @return {Object} the observable or the attribute property.
*
* If you are reading, the property value is returned:
*
* o.attr('name') //-> Justin
*
* If you are writing, the observe is returned for chaining:
*
* o.attr('name',"Brian").attr('name') //-> Justin
*/
attr: function( attr, val ) {
if ( val === undefined ) {
// if we are getting a value
return this._get(attr)
} else {
// otherwise we are setting
this._set(attr, val);
return this;
}
},
/**
* Iterates through each attribute, calling handler
* with each attribute name and value.
*
* new Observe({foo: 'bar'})
* .each(function(name, value){
* equals(name, 'foo')
* equals(value,'bar')
* })
*
* @param {function} handler(attrName,value) A function that will get
* called back with the name and value of each attribute on the observe.
*
* Returning `false` breaks the looping. The following will never
* log 3:
*
* new Observe({a : 1, b : 2, c: 3})
* .each(function(name, value){
* console.log(value)
* if(name == 2){
* return false;
* }
* })
*
* @return {jQuery.Observe} the original observable.
*/
each: function() {
return each.apply(null, [this.__get()].concat(makeArray(arguments)))
},
/**
* Removes a property
*
* o = new $.Observe({foo: 'bar'});
* o.removeAttr('foo'); //-> 'bar'
*
* This creates a `'remove'` change event. Learn more about events
* in [jQuery.Observe.prototype.bind bind] and [jQuery.Observe.prototype.delegate delegate].
*
* @param {String} attr the attribute name to remove.
* @return {Object} the value that was removed.
*/
removeAttr: function( attr ) {
// convert the attr into parts (if nested)
var parts = isArray(attr) ? attr : attr.split("."),
// the actual property to remove
prop = parts.shift(),
// the current value
current = this._data[prop];
// if we have more parts, call removeAttr on that part
if ( parts.length ) {
return current.removeAttr(parts)
} else {
// otherwise, delete
delete this._data[prop];
// create the event
trigger(this, "change", [prop, "remove", undefined, current]);
return current;
}
},
// reads a property from the object
_get: function( attr ) {
var parts = isArray(attr) ? attr : (""+attr).split("."),
current = this.__get(parts.shift());
if ( parts.length ) {
return current ? current._get(parts) : undefined
} else {
return current;
}
},
// reads a property directly if an attr is provided, otherwise
// returns the 'real' data object itself
__get: function( attr ) {
return attr ? this._data[attr] : this._data;
},
// sets attr prop as value on this object where
// attr - is a string of properties or an array of property values
// value - the raw value to set
// description - an object with converters / serializers / defaults / getterSetters?
_set: function( attr, value ) {
// convert attr to attr parts (if it isn't already)
var parts = isArray(attr) ? attr : ("" + attr).split("."),
// the immediate prop we are setting
prop = parts.shift(),
// its current value
current = this.__get(prop);
// if we have an object and remaining parts
if ( isObject(current) && parts.length ) {
// that object should set it (this might need to call attr)
current._set(parts, value)
} else if (!parts.length ) {
// otherwise, we are setting it on this object
// todo: check if value is object and transform
// are we changing the value
if ( value !== current ) {
// check if we are adding this for the first time
// if we are, we need to create an 'add' event
var changeType = this.__get().hasOwnProperty(prop) ? "set" : "add";
// set the value on data
this.__set(prop,
// if we are getting an object
isObject(value) ?
// hook it up to send event to us
hookup(value, prop, this) :
// value is normal
value);
// trigger the change event
trigger(this, "change", [prop, changeType, value, current]);
// if we can stop listening to our old value, do it
current && unhookup([current], this._namespace);
}
} else {
throw "jQuery.Observe: set a property on an object that does not exist"
}
},
// directly sets a property on this object
__set: function( prop, val ) {
this._data[prop] = val;
// add property directly for easy writing
// check if its on the prototype so we don't overwrite methods like attrs
if (!(prop in this.constructor.prototype)) {
this[prop] = val
}
},
/**
* Listens to changes on a jQuery.Observe.
*
* When attributes of an observe change, including attributes on nested objects,
* a `'change'` event is triggered on the observe. These events come
* in three flavors:
*
* - `add` - a attribute is added
* - `set` - an existing attribute's value is changed
* - `remove` - an attribute is removed
*
* The change event is fired with:
*
* - the attribute changed
* - how it was changed
* - the newValue of the attribute
* - the oldValue of the attribute
*
* Example:
*
* o = new $.Observe({name : "Payal"});
* o.bind('change', function(ev, attr, how, newVal, oldVal){
* // ev -> {type: 'change'}
* // attr -> "name"
* // how -> "add"
* // newVal-> "Justin"
* // oldVal-> undefined
* })
*
* o.attr('name', 'Justin')
*
* Listening to `change` is only useful for when you want to
* know every change on an Observe. For most applications,
* [jQuery.Observe.prototype.delegate delegate] is
* much more useful as it lets you listen to specific attribute
* changes and sepecific types of changes.
*
*
* @param {String} eventType the event name. Currently,
* only 'change' events are supported. For more fine
* grained control, use [jQuery.Observe.prototype.delegate].
*
* @param {Function} handler(event, attr, how, newVal, oldVal) A
* callback function where
*
* - event - the event
* - attr - the name of the attribute changed
* - how - how the attribute was changed (add, set, remove)
* - newVal - the new value of the attribute
* - oldVal - the old value of the attribute
*
* @return {$.Observe} the observe for chaining.
*/
bind: function( eventType, handler ) {
$.fn.bind.apply($([this]), arguments);
return this;
},
/**
* Unbinds a listener. This uses [http://api.jquery.com/unbind/ jQuery.unbind]
* and works very similar. This means you can
* use namespaces or unbind all event handlers for a given event:
*
* // unbind a specific event handler
* o.unbind('change', handler)
*
* // unbind all change event handlers bound with the
* // foo namespace
* o.unbind('change.foo')
*
* // unbind all change event handlers
* o.unbind('change')
*
* @param {String} eventType - the type of event with
* any optional namespaces. Currently, only `change` events
* are supported with bind.
*
* @param {Function} [handler] - The original handler function passed
* to [jQuery.Observe.prototype.bind bind].
*
* @return {jQuery.Observe} the original observe for chaining.
*/
unbind: function( eventType, handler ) {
$.fn.unbind.apply($([this]), arguments);
return this;
},
/**
* Get the serialized Object form of the observe. Serialized
* data is typically used to send back to a server.
*
* o.serialize() //-> { name: 'Justin' }
*
* Serialize currently returns the same data
* as [jQuery.Observe.prototype.attrs]. However, in future
* versions, serialize will be able to return serialized
* data similar to [jQuery.Model]. The following will work:
*
* new Observe({time: new Date()})
* .serialize() //-> { time: 1319666613663 }
*
* @return {Object} a JavaScript Object that can be
* serialized with `JSON.stringify` or other methods.
*
*/
serialize: function() {
return serialize(this, 'serialize', {});
},
/**
* Set multiple properties on the observable
* @param {Object} props
* @param {Boolean} remove true if you should remove properties that are not in props
*/
attrs: function( props, remove ) {
if ( props === undefined ) {
return serialize(this, 'attrs', {})
}
props = $.extend(true, {}, props);
var prop, collectingStarted = collect();
for ( prop in this._data ) {
var curVal = this._data[prop],
newVal = props[prop];
// if we are merging ...
if ( newVal === undefined ) {
remove && this.removeAttr(prop);
continue;
}
if ( isObject(curVal) && isObject(newVal) ) {
curVal.attrs(newVal, remove)
} else if ( curVal != newVal ) {
this._set(prop, newVal)
} else {
}
delete props[prop];
}
// add remaining props
for ( var prop in props ) {
newVal = props[prop];
this._set(prop, newVal)
}
if ( collectingStarted ) {
sendCollection();
}
}
});
// Helpers for list
/**
* @class jQuery.Observe.List
* @inherits jQuery.Observe
* @parent jQuery.Observe
*
* An observable list. You can listen to when items are push, popped,
* spliced, shifted, and unshifted on this array.
*
*
*/
var list = $.Observe($.globalNamespace + '.Observe.List',
/**
* @prototype
*/
{
init: function( instances, options ) {
this.length = 0;
this._namespace = ".list" + (++id);
this._init = true;
this.bind('change',this.proxy('_changes'));
this.push.apply(this, makeArray(instances || []));
$.extend(this, options);
if(this.comparator){
this.sort()
}
delete this._init;
},
_changes : function(ev, attr, how, newVal, oldVal){
// detects an add, sorts it, re-adds?
//console.log("")
// if we are sorting, and an attribute inside us changed
if(this.comparator && /^\d+./.test(attr) ) {
// get the index
var index = +(/^\d+/.exec(attr)[0]),
// and item
item = this[index],
// and the new item
newIndex = this.sortedIndex(item);
if(newIndex !== index){
// move ...
[].splice.call(this, index, 1);
[].splice.call(this, newIndex, 0, item);
trigger(this, "move", [item, newIndex, index]);
ev.stopImmediatePropagation();
trigger(this,"change", [
attr.replace(/^\d+/,newIndex),
how,
newVal,
oldVal
]);
return;
}
}
// if we add items, we need to handle
// sorting and such
// trigger direct add and remove events ...
if(attr.indexOf('.') === -1){
if( how === 'add' ) {
trigger(this, how, [newVal,+attr]);
} else if( how === 'remove' ) {
trigger(this, how, [oldVal, +attr])
}
}
// issue add, remove, and move events ...
},
sortedIndex : function(item){
var itemCompare = item.attr(this.comparator),
equaled = 0,
i;
for(var i =0; i < this.length; i++){
if(item === this[i]){
equaled = -1;
continue;
}
if(itemCompare <= this[i].attr(this.comparator) ) {
return i+equaled;
}
}
return i+equaled;
},
__get : function(attr){
return attr ? this[attr] : this;
},
__set : function(attr, val){
this[attr] = val;
},
/**
* Returns the serialized form of this list.
*/
serialize: function() {
return serialize(this, 'serialize', []);
},
/**
* Iterates through each item of the list, calling handler
* with each index and value.
*
* new Observe.List(['a'])
* .each(function(index, value){
* equals(index, 1)
* equals(value,'a')
* })
*
* @param {function} handler(index,value) A function that will get
* called back with the index and value of each item on the list.
*
* Returning `false` breaks the looping. The following will never
* log 'c':
*
* new Observe(['a','b','c'])
* .each(function(index, value){
* console.log(value)
* if(index == 1){
* return false;
* }
* })
*
* @return {jQuery.Observe.List} the original observable.
*/
// placeholder for each
/**
* Remove items or add items from a specific point in the list.
*
* ### Example
*
* The following creates a list of numbers and replaces 2 and 3 with
* "a", and "b".
*
* var l = new $.Observe.List([0,1,2,3]);
*
* l.bind('change', function( ev, attr, how, newVals, oldVals, where ) { ... })
*
* l.splice(1,2, "a", "b"); // results in [0,"a","b",3]
*
* This creates 2 change events. The first event is the removal of
* numbers one and two where it's callback values will be:
*
* - attr - "1" - indicates where the remove event took place
* - how - "remove"
* - newVals - undefined
* - oldVals - [1,2] -the array of removed values
* - where - 1 - the location of where these items where removed
*
* The second change event is the addition of the "a", and "b" values where
* the callback values will be:
*
* - attr - "1" - indicates where the add event took place
* - how - "added"
* - newVals - ["a","b"]
* - oldVals - [1, 2] - the array of removed values
* - where - 1 - the location of where these items where added
*
* @param {Number} index where to start removing or adding items
* @param {Object} count the number of items to remove
* @param {Object} [added] an object to add to
*/
splice: function( index, count ) {
var args = makeArray(arguments),
i;
for ( i = 2; i < args.length; i++ ) {
var val = args[i];
if ( isObject(val) ) {
args[i] = hookup(val, "*", this)
}
}
if ( count === undefined ) {
count = args[1] = this.length - index;
}
var removed = [].splice.apply(this, args);
if ( count > 0 ) {
trigger(this, "change", [""+index, "remove", undefined, removed]);
unhookup(removed, this._namespace);
}
if ( args.length > 2 ) {
trigger(this, "change", [""+index, "add", args.slice(2), removed]);
}
return removed;
},
/**
* Updates an array with a new array. It is able to handle
* removes in the middle of the array.
*
* @param {Array} props
* @param {Boolean} remove
*/
attrs: function( props, remove ) {
if ( props === undefined ) {
return serialize(this, 'attrs', []);
}
// copy
props = props.slice(0);
var len = Math.min(props.length, this.length),
collectingStarted = collect();
for ( var prop = 0; prop < len; prop++ ) {
var curVal = this[prop],
newVal = props[prop];
if ( isObject(curVal) && isObject(newVal) ) {
curVal.attrs(newVal, remove)
} else if ( curVal != newVal ) {
this._set(prop, newVal)
} else {
}
}
if ( props.length > this.length ) {
// add in the remaining props
this.push(props.slice(this.length))
} else if ( props.length < this.length && remove ) {
this.splice(props.length)
}
//remove those props didn't get too
if ( collectingStarted ) {
sendCollection()
}
},
sort: function(method, silent){
var comparator = this.comparator,
args = comparator ? [function(a, b){
a = a[comparator]
b = b[comparator]
return a === b ? 0 : (a < b ? -1 : 1);
}] : [],
res = [].sort.apply(this, args);
!silent && trigger(this, "reset");
}
}),
// create push, pop, shift, and unshift
// converts to an array of arguments
getArgs = function( args ) {
if ( args[0] && ($.isArray(args[0])) ) {
return args[0]
}
else {
return makeArray(args)
}
};
// describes the method and where items should be added
each({
/**
* @function push
* Add items to the end of the list.
*
* var l = new $.Observe.List([]);
*
* l.bind('change', function(
* ev, // the change event
* attr, // the attr that was changed, for multiple items, "*" is used
* how, // "add"
* newVals, // an array of new values pushed
* oldVals, // undefined
* where // the location where these items where added
* ) {
*
* })
*
* l.push('0','1','2');
*
* @return {Number} the number of items in the array
*/
push: "length",
/**
* @function unshift
* Add items to the start of the list. This is very similar to
* [jQuery.Observe.prototype.push].
*/
unshift: 0
},
// adds a method where
// - name - method name
// - where - where items in the array should be added
function( name, where ) {
list.prototype[name] = function() {
// get the items being added
var args = getArgs(arguments),
// where we are going to add items
len = where ? this.length : 0;
// go through and convert anything to an observe that needs to be converted
for ( var i = 0; i < args.length; i++ ) {
var val = args[i];
if ( isObject(val) ) {
args[i] = hookup(val, "*", this)
}
}
// if we have a sort item, add that
if( args.length == 1 && this.comparator ) {
// add each item ...
// we could make this check if we are already adding in order
// but that would be confusing ...
var index = this.sortedIndex(args[0]);
this.splice(index, 0, args[0]);
return this.length;
}
// call the original method
var res = [][name].apply(this, args)
// cause the change where the args are:
// len - where the additions happened
// add - items added
// args - the items added
// undefined - the old value
if ( this.comparator && args.length > 1) {
this.sort(null, true);
trigger(this,"reset", [args])
} else {
trigger(this, "change", [""+len, "add", args, undefined])
}
return res;
}
});
each({
/**
* @function pop
*
* Removes an item from the end of the list.
*
* var l = new $.Observe.List([0,1,2]);
*
* l.bind('change', function(
* ev, // the change event
* attr, // the attr that was changed, for multiple items, "*" is used
* how, // "remove"
* newVals, // undefined
* oldVals, // 2
* where // the location where these items where added
* ) {
*
* })
*
* l.pop();
*
* @return {Object} the element at the end of the list
*/
pop: "length",
/**
* @function shift
* Removes an item from the start of the list. This is very similar to
* [jQuery.Observe.prototype.pop].
*
* @return {Object} the element at the start of the list
*/
shift: 0
},
// creates a 'remove' type method
function( name, where ) {
list.prototype[name] = function() {
var args = getArgs(arguments),
len = where && this.length ? this.length - 1 : 0;
var res = [][name].apply(this, args)
// create a change where the args are
// "*" - change on potentially multiple properties
// "remove" - items removed
// undefined - the new values (there are none)
// res - the old, removed values (should these be unbound)
// len - where these items were removed
trigger(this, "change", [""+len, "remove", undefined, [res]])
if ( res && res.unbind ) {
res.unbind("change" + this._namespace)
}
return res;
}
});
list.prototype.
/**
* @function indexOf
* Returns the position of the item in the array. Returns -1 if the
* item is not in the array.
* @param {Object} item
* @return {Number}
*/
indexOf = [].indexOf || function(item){
return $.inArray(item, this)
}
/**
* @class $.O
*/
$.O = function(data, options){
if(isArray(data) || data instanceof $.Observe.List){
return new $.Observe.List(data, options)
} else {
return new $.Observe(data, options)
}
}
})();
});
FD31.plugin("joomla", function($) {
/**
* joomla
* Abstraction layer for Joomla client-side API.
* https://github.com/foundry-modules/joomla
*
* Copyright (c) 2012 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var parser = {
squeezebox: function() {
return (self.isJoomla15) ? window.parent.document.getElementById('sbox-window') : window.parent.SqueezeBox;
}
};
var self = $.Joomla = function(method, args) {
// Overriding function
if ($.isFunction(args)) {
var fn = args;
if (self.isJoomla15) {
window[method] = fn;
} else {
window.Joomla[method] = fn;
};
return;
}
// Calling function
var method = parser[method] || ((self.isJoomla15) ? window[method] : window.Joomla[method]);
if ($.isFunction(method)) {
return method.apply(window, args);
}
};
var version = parseFloat($.joomla.version);
self.isJoomla15 = version <= 1.5;
self.isJoomla25 = version >= 1.6 && version <= 2.5;
self.isJoomla30 = version >= 3.0;
});
FD31.plugin("module", function($) {
/**
* jquery.module.
* An AMD manager built on top of $.Deferred() backbone.
* An alternative take on RequireJS's define().
*
* Part of the jquery.require family.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var Module = function(name) {
var module = this,
ready = $.Callbacks("once memory");
$.extend(this, $.Deferred(), {
// Name of the module
name: name,
// Module status
// pending, ready, executing, resolved, rejected
status: "pending",
// When a module factory is received,
// this event is fired.
ready: function(fn) {
if (fn===true) ready.fire.call(module, $);
if ($.isFunction(fn)) ready.add(fn);
}
});
// Listen to the events of the module
// and update the module status as necessary.
module.then(
function() {
module.exports = this;
module.status = "resolved";
},
function() {
module.status = "rejected";
}
);
// Keep a copy of the original done method.
// This is so that we can track when this done
// method is being called for the first time,
// and perform the necessary actions below.
var done = module.done;
module.done = function() {
// Flag this module as required
// This indicates that we should
// execute the module factory.
module.required = true;
// Execute the module factory
// if this module has received it
// and it hasn't been executed yet.
var factory = module.factory;
if (factory && module.status==="ready") {
factory.call(module, $);
}
// Replace this first-time done method
// with the original done method.
module.done = done;
// Execute the original done method.
return module.done.apply(this, arguments);
}
}
$.module = (function() {
var self = function(name, factory) {
var module;
if (typeof name === "string") {
module = self.get(name);
/** Facade #1. Get module.
*
* $.module('foobar'); // returns module
*
*/
if (factory === undefined) {
return module;
}
/** Facade #2. Factory assignment.
*
* $.module('foobar', function() {
*
* // This is required in every module factory.
* // Resolve module, return exports.
*
* this.resolveWith(exports, [args]);
*
* });
*
*/
if ($.isFunction(factory)) {
// If module is resolved, don't let new factory overwrite it.
if (module.status=="resolved") return module;
module.factory = factory;
module.status = "ready";
// Indicates that the module factory
// for this module has been received.
module.ready("true");
// If the module is required,
// execute the module factory.
if (module.required) {
module.status = "executing";
// Execute factory
factory.call(module, $);
}
return module;
}
}
/** Facade #3. Multiple factory assignments / Predefine modules.
* This is used by Foundry compiler when combining multiple script files into one.
*
* $.module([
*
* // Module task object
* {
* name: "module.name"
* factory: function(){}
* }
*
* // Module which is loading
* // but factory assignment kicks in later
* "module.name"
* ]);
*
*/
// Predefine modules
if ($.isArray(name)) {
var tasks = $.map(name, function(task) {
var module = self.get($.isString(task) ? task : task.name);
if (!module) return;
// If module is pending, set it to ready.
// This trick require calls into thinking that
// the script file of this module has been loaded,
// so it won't go and load the script file again.
if (module.status === "pending") {
module.status = "ready";
}
if ($.isPlainObject(task)) return task;
});
// Run through the list of tasks and assign its factory to the module.
$.each(tasks, function(i, task) {
// Assign factory to module
self(task.name, task.factory);
});
}
}
// $.module static methods
$.extend(self, {
registry: {},
get: function(name) {
if (!name) return;
if ($.isModule(name)) {
name = name.replace("module://", "");
}
return self.registry[name] || self.create(name);
},
create: function(name) {
return self.registry[name] = new Module(name);
},
remove: function(name) {
delete self.registry[name];
}
});
return self;
})();
$.isModule = function(module) {
if ($.isString(module)) {
return !!module.match("module://");
}
return module && module instanceof Module;
}
});
FD31.plugin("script", function($) {
/**
* jquery.script
* Script injection utility built on top $.Deferred() backbone.
* https://github.com/jstonne/jquery.script
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.script = (function(){
var self = function(options) {
if (options===undefined) {
return;
}
if (typeof options==="string") {
options = {
url: options
}
}
var script = new Script(options);
return script;
};
var Script = function(options) {
var script = $.extend(this, options);
script.manager = $.Deferred();
$.extend(script, script.manager.promise());
script.load();
};
var head = document.getElementsByTagName("head")[0],
baseElement = document.getElementsByTagName("base")[0];
$.extend(Script.prototype, {
timeout: 7000,
retry: 3,
retryCount: 1,
type: "text/javascript",
async: false,
charset: "UTF-8",
verbose: false,
insert: function() {
var node = this.node;
head.appendChild(node);
},
remove: function() {
var node = this.node;
// This prevents IE7-8 locking up.
setTimeout(function(){
// Handle memory leak in IE
node.onload = node.onerror = node.onreadystatechange = null;
try {
head.removeChild(node);
} catch(e) {};
}, 1000);
},
load: function() {
var script = this,
node;
script.endTime = undefined;
script.startTime = new Date();
script.node = node = document.createElement('script');
script.insert();
// Create a reference to these proxied functions,
// so that we can detach them from event listeners.
script._ready = $.proxy(script.ready, script);
script._error = $.proxy(script.error, script);
// On IE9, addEventListener() does not necessary fire the onload event after
// the script is loaded, attachEvent() method behaves correctly.
if (node.attachEvent && !$.browser.opera) {
node.attachEvent("onreadystatechange", script._ready);
node.attachEvent("onerror" , script._error); // IE9 only.
} else {
node.addEventListener("load" , script._ready, false);
node.addEventListener("error" , script._error, false);
}
$(node).attr({
type : script.type,
async : script.async,
charset : script.charset,
src : script.url
});
script.monitor();
},
monitor: function() {
var script = this;
if (script.retryCount > script.retry) {
script._error();
return;
}
setTimeout(function() {
if (script.state()!=="resolved") {
if (script.verbose) {
console.warn('$.script: Load timeout. [Retry: ' + script.retryCount + ']', script);
}
script.remove();
script.retryCount++;
script.load();
}
}, script.timeout * script.retryCount);
},
ready: function(event) {
var script = this,
node = script.node;
// if (script.verbose) {
// console.info('$.script: Loaded' + (($.browser.msie) ? ' ' + script.url.replace($.scriptPath, '') + ' ': ''), script);
// }
if (event.type==="load" || /loaded|complete/.test(node.readyState)) {
script.complete.call(script, event);
script.manager.resolve(script);
}
},
error: function(event) {
var script = this;
if (script.verbose) {
console.error('$.script: Unable to load ', script);
}
script.complete.call(script, event);
script.remove();
script.manager.reject(script);
},
complete: function(event) {
var script = this,
node = script.node;
script.endTime = new Date();
if (node.detachEvent && !$.browser.opera) {
node.detachEvent("onreadystatechange", script._ready);
node.detachEvent("onerror" , script._error);
} else {
node.removeEventListener("load" , script._ready, false);
node.removeEventListener("error" , script._error, false);
}
}
});
return self;
})();
});
FD31.plugin("stylesheet", function($) {
/**
* jquery.stylesheet
* Stylesheet injector utility with workarounds
* for IE's 31 stylesheet limitation.
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var head = document.getElementsByTagName('head')[0],
stylesheets = document.styleSheets,
IE_STYLESHEET = document.createStyleSheet,
IE_MAX_STYLESHEET = 31,
IE_MAX_IMPORT = 31,
IE_MAX_RULE = 4095;
$.stylesheet = (function() {
var self = function(url, attr) {
var options = {};
$.extend(
options,
self.defaultOptions,
($.isPlainObject(url)) ?
url :
{
url: url,
attr: attr || {}
}
);
// Create a new stylesheet object
if (options.url===undefined) {
return self.create(options);
}
// Loading an external stylesheet
return self.load(options);
};
$.extend(self, {
defaultOptions: {
type: "text/css",
rel: "stylesheet",
media: "all",
title: "",
// Force link injection, ignores IE workarounds, overrides XHR value.
forceInject: false,
// @TODO: XHR loading.
xhr: false
},
setup: function(options) {
$.extend(self.defaultOptions, options);
},
availability: function() {
// @TODO: Also calculate bleedImports.
var stat = {},
links = $('link[rel*="stylesheet"]')
styles = $('style');
stat.groups = IE_MAX_STYLESHEET - links.length - styles.length;
stat.slots = stat.groups * IE_MAX_IMPORT;
if (self.currentGroup) {
stat.slots += IE_MAX_IMPORT - self.currentGroup.imports.length;
}
return stat;
},
get: function(style) {
var i=0;
for (; i<stylesheets.length; i++) {
var stylesheet = stylesheets[i];
if ((stylesheet.ownerNode || stylesheet.owningElement)==style) {
return stylesheet;
}
}
},
create: function(options) {
var stylesheet,
style,
length = stylesheets.length;
if (IE_STYLESHEET) {
// Unable to create further stylesheets
if (length>=IE_MAX_STYLESHEET) return;
stylesheet = document.createStyleSheet();
style = stylesheet.ownerNode || stylesheet.owningElement;
} else {
style = document.createElement('style');
head.appendChild(style);
}
$.extend(style, {
type : options.type,
title: options.title,
media: options.media,
rel : options.rel
});
// Insert stylesheet content
var content = options.content;
if (content!==undefined) {
if (IE_STYLESHEET) {
stylesheet.cssText = content;
} else {
style.appendChild(document.createTextNode(content));
}
}
return style;
},
nextAvailable: function(alsoCreateIfUnavailable) {
var stylesheet,
length = stylesheets.length;
if (length) {
var i;
for (i=length; i--; i<0) {
stylesheet = stylesheets[i];
// If this is IE and the maximum amount of rules have exceeded,
if (IE_STYLESHEET && ((stylesheet.cssRules || stylesheet.rules).length >= IE_MAX_RULE)) {
// then this stylesheet cannot be used.
stylesheet = undefined;
// try an older stylesheet.
continue;
}
break;
}
}
return stylesheet.ownerNode || stylesheet.owningElement || ((alsoCreateIfUnavailable) ? self() : undefined);
},
load: function(options) {
if ($.browser.msie && !options.forceInject) {
return self._import(options);
} else {
// @TODO: Use onload/onerror events on browsers that support them.
var link =
$('<link>')
.attr({
href: options.url,
type: options.type,
rel: options.rel,
media: options.media
})
.appendTo('head');
return link[0];
}
},
_import: function(options) {
var failed;
if (self.currentGroup===undefined) {
var group;
try {
group = document.createStyleSheet();
// It is only a getter on IE.
// group.type = "text/css";
group.media = "all";
group.title = "jquery_stylesheet";
} catch(e) {
failed = true;
if (options.verbose) {
console.error('There is not enough slots left to create a new stylesheet group.');
}
}
if (failed) return false;
self.currentGroup = group;
}
try {
self.currentGroup.addImport(options.url);
} catch(e) {
failed = true;
if (options.verbose) {
console.info('Import slots exceeded. Creating a new stylesheet group.');
}
}
if (failed) {
self.currentGroup = undefined;
return self._import(options);
}
return true;
}
});
return self;
})();
(function(){
var cssRule = function(selectors, rules, style) {
$.extend(this, {
id : $.uid(),
style : style,
selectors: [],
preRule : "",
rules : {},
legacy : $.IE===8,
important: false
});
// If selector is given, automatically add rule.
// Else assume caller wants a blank rule object.
if (selectors) {
this.set(selectors, rules);
}
}
$.extend(cssRule.prototype, {
set: function(selectors, rules) {
// Normalize selectors into array
if ($.isString(selectors)) {
this.selectors = selectors.split(",");
} else {
this.selectors = selectors;
}
// Normalize rules
if ($.isString(rules)) {
this.preRule = rules + "\n";
this.rules = {};
} else {
this.preRule = "";
this.rules = rules || this.rules;
}
this.update();
return this;
},
cssText: function() {
return this.selectors.join(",") + "{" + this.ruleText() + "}\n";
},
ruleText: function() {
var important = this.important;
return this.preRule +
((this.legacy) ? "-rule-id:" + this.id + ";" : "") +
$.map(this.rules, function(val, prop) {
if ($.isNumeric(val) && !$.cssNumber[prop]) val += "px";
if (important) val += " !important";
return prop + ":" + val;
}).join(";");
},
update: function() {
if (this.legacy) return this.updateLegacy();
// Generate css text
var cssText = this.cssText();
// If new, insert textnode
if (this.textNode===undefined) {
this.textNode = document.createTextNode(cssText);
this.style.appendChild(this.textNode);
// Or update existing textnode.
} else {
this.textNode.nodeValue = cssText;
}
return this;
},
updateLegacy: function() {
this.removeLegacy();
var stylesheet = $.stylesheet.get(this.style),
selectors = this.selectors,
ruleText = this.ruleText(),
i=0;
for (;i<selectors.length;i++) {
stylesheet.addRule(selectors[i], ruleText);
}
return this;
},
remove: function() {
if (this.legacy) return this.removeLegacy();
if (this.textNode!==undefined) {
// Removing text node is so much quicker
// than searching for the rule
this.style.removeChild(this.textNode);
delete this.textNode;
}
return this;
},
removeLegacy: function() {
var stylesheet = $.stylesheet.get(this.style),
rules = stylesheet.rules,
i = 0;
for (;i<rules.length;i++) {
if (rules[i].cssText.match(this.id)!==null) {
stylesheet.removeRule(i);
}
}
return this;
},
css: function(prop, val) {
// Getter
if ($.isString(prop) && val===undefined) {
return this.rules[prop];
}
// Setter
if ($.isPlainObject(prop)) {
$.extend(this.rules, prop);
} else {
this.rules[prop] = val;
}
this.update();
return this;
}
});
var self = $.cssRule = function(selector, rules, style) {
var style = style || self.style || $.stylesheet.nextAvailable(true);
// If no stylesheet available at this point, stop.
if (!style) return;
return new cssRule(selector, rules, style);
};
self.style = $.stylesheet();
})();
(function(){
$.cssUrl = function(url) {
return 'url("' + encodeURI(url) + '")';
}
})();
});
FD31.plugin("language", function($) {
$.sprintf = function() {
// http://kevin.vanzonneveld.net
// + original by: Ash Searle (http://hexmen.com/blog/)
// + namespaced by: Michael White (http://getsprink.com)
// + tweaked by: Jack
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Paulo Freitas
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// * example 1: sprintf("%01.2f", 123.1);
// * returns 1: 123.10
// * example 2: sprintf("[%10s]", 'monkey');
// * returns 2: '[ monkey]'
// * example 3: sprintf("[%'#10s]", 'monkey');
// * returns 3: '[####monkey]'
var regex = /%%|%(\d+\$)?([-+\'#0 ]*)(\*\d+\$|\*|\d+)?(\.(\*\d+\$|\*|\d+))?([scboxXuidfegEG])/g;
var a = arguments,
i = 0,
format = a[i++];
// pad()
var pad = function (str, len, chr, leftJustify) {
if (!chr) {
chr = ' ';
}
var padding = (str.length >= len) ? '' : Array(1 + len - str.length >>> 0).join(chr);
return leftJustify ? str + padding : padding + str;
};
// justify()
var justify = function (value, prefix, leftJustify, minWidth, zeroPad, customPadChar) {
var diff = minWidth - value.length;
if (diff > 0) {
if (leftJustify || !zeroPad) {
value = pad(value, minWidth, customPadChar, leftJustify);
} else {
value = value.slice(0, prefix.length) + pad('', diff, '0', true) + value.slice(prefix.length);
}
}
return value;
};
// formatBaseX()
var formatBaseX = function (value, base, prefix, leftJustify, minWidth, precision, zeroPad) {
// Note: casts negative numbers to positive ones
var number = value >>> 0;
prefix = prefix && number && {
'2': '0b',
'8': '0',
'16': '0x'
}[base] || '';
value = prefix + pad(number.toString(base), precision || 0, '0', false);
return justify(value, prefix, leftJustify, minWidth, zeroPad);
};
// formatString()
var formatString = function (value, leftJustify, minWidth, precision, zeroPad, customPadChar) {
if (precision != null) {
value = value.slice(0, precision);
}
return justify(value, '', leftJustify, minWidth, zeroPad, customPadChar);
};
// doFormat()
var doFormat = function (substring, valueIndex, flags, minWidth, _, precision, type) {
var number;
var prefix;
var method;
var textTransform;
var value;
if (substring == '%%') {
return '%';
}
// parse flags
var leftJustify = false,
positivePrefix = '',
zeroPad = false,
prefixBaseX = false,
customPadChar = ' ';
var flagsl = flags.length;
for (var j = 0; flags && j < flagsl; j++) {
switch (flags.charAt(j)) {
case ' ':
positivePrefix = ' ';
break;
case '+':
positivePrefix = '+';
break;
case '-':
leftJustify = true;
break;
case "'":
customPadChar = flags.charAt(j + 1);
break;
case '0':
zeroPad = true;
break;
case '#':
prefixBaseX = true;
break;
}
}
// parameters may be null, undefined, empty-string or real valued
// we want to ignore null, undefined and empty-string values
if (!minWidth) {
minWidth = 0;
} else if (minWidth == '*') {
minWidth = +a[i++];
} else if (minWidth.charAt(0) == '*') {
minWidth = +a[minWidth.slice(1, -1)];
} else {
minWidth = +minWidth;
}
// Note: undocumented perl feature:
if (minWidth < 0) {
minWidth = -minWidth;
leftJustify = true;
}
if (!isFinite(minWidth)) {
throw new Error('sprintf: (minimum-)width must be finite');
}
if (!precision) {
precision = 'fFeE'.indexOf(type) > -1 ? 6 : (type == 'd') ? 0 : undefined;
} else if (precision == '*') {
precision = +a[i++];
} else if (precision.charAt(0) == '*') {
precision = +a[precision.slice(1, -1)];
} else {
precision = +precision;
}
// grab value using valueIndex if required?
value = valueIndex ? a[valueIndex.slice(0, -1)] : a[i++];
switch (type) {
case 's':
return formatString(String(value), leftJustify, minWidth, precision, zeroPad, customPadChar);
case 'c':
return formatString(String.fromCharCode(+value), leftJustify, minWidth, precision, zeroPad);
case 'b':
return formatBaseX(value, 2, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
case 'o':
return formatBaseX(value, 8, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
case 'x':
return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
case 'X':
return formatBaseX(value, 16, prefixBaseX, leftJustify, minWidth, precision, zeroPad).toUpperCase();
case 'u':
return formatBaseX(value, 10, prefixBaseX, leftJustify, minWidth, precision, zeroPad);
case 'i':
case 'd':
number = (+value) | 0;
prefix = number < 0 ? '-' : positivePrefix;
value = prefix + pad(String(Math.abs(number)), precision, '0', false);
return justify(value, prefix, leftJustify, minWidth, zeroPad);
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
number = +value;
prefix = number < 0 ? '-' : positivePrefix;
method = ['toExponential', 'toFixed', 'toPrecision']['efg'.indexOf(type.toLowerCase())];
textTransform = ['toString', 'toUpperCase']['eEfFgG'.indexOf(type) % 2];
value = prefix + Math.abs(number)[method](precision);
return justify(value, prefix, leftJustify, minWidth, zeroPad)[textTransform]();
default:
return substring;
}
};
return format.replace(regex, doFormat);
}
/**
* jquery.language.
* Client-side access to server-side language.
* https://github.com/foundry-modules/language
*
* Copyright (c) 2011 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var self = $.language = $.JText = function(key) {
if (arguments.length < 2) {
return self._(key);
} else {
return self.sprintf.apply(null, arguments);
}
};
$.extend(self, {
lang: {},
add: function(key, val) {
if ($.isPlainObject(key)) {
$.extend(self.lang, key);
} else {
self.lang[key] = val;
}
},
remove: function(key) {
delete self.lang[key];
},
_: function(key) {
return self.lang[key] || "";
},
sprintf: function() {
var key = arguments[0];
arguments[0] = self.lang[key];
return $.sprintf.apply(null, arguments);
}
});
});
FD31.plugin("template", function($) {
/**
* jquery.template
* Template repository
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.template = (function() {
var defaultTemplate = {
content: "",
type: "ejs"
};
var self = function(name, content) {
if (name===undefined) {
return self.templates;
}
var template;
if (typeof content == "string") {
if ($.isPlainObject(content)) {
template = content;
} else {
template = $.extend({}, defaultTemplate, {name: name, content: content});
}
self.templates[name] = template;
return template;
} else {
template = self.templates[name] || {};
return template.content || "";
}
};
$.extend(self, {
templates: {},
remove: function(name) {
delete self.templates[name];
}
});
return self;
})();
});
FD31.plugin("require", function($) {
/**
* jquery.require.
* A dependency loader built on top of $.Deferred() backbone.
* An alternative take on RequireJS.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require = (function() {
// internal function
var getFolderPath = function(path) {
return $.uri(path).setAnchor('').setQuery('').toPath('../').toString();
};
var self = function(options) {
var batch = new Batch(options);
self.batches[batch.id] = batch;
return batch;
};
// Require methods & properties
$.extend(self, {
defaultOptions: {
// Path selection order:
path: (function() {
var path =
$.path + "/scripts/" ||
// By "require_path" attribute
$('[require-path]').attr('require-path') ||
// By last script tag's "src" attribute
getFolderPath($('script:last').attr('src')) ||
// By window location
getFolderPath(window.location.href);
if (/^(\/|\.)/.test(path)) {
path = $.uri(window.location.href).toPath(path).toString();
}
return path;
})(),
timeout: 10000,
retry: 3,
verbose: ($.environment=="development")
},
setup: function(options) {
$.extend(self.defaultOptions, options);
},
batches: {},
status: function(filter) {
$.each(self.batches, function(i, batch){
var count = {pending: 0, resolved: 0, rejected: 0, ready: 0, total: 0},
messages = [];
// Calculate statistics
$.each(batch.tasks, function(i, task){
state = (task.module && task.module.status=="ready") ? "ready" : task.state();
count[state]++;
count.total++;
messages.push({
state: state,
content: '[' + state + '] ' + task.name
});
});
var batchName = batch.id + ": " + batch.state() + " [" + count.resolved + "/" + count.total + "]";
if (filter && count[filter] < 1) return;
if ($.IE) {
console.log("$.require.batches[\"" + batch.id + "\"]");
$.each(messages, function(i, message){
console.log(message.content);
});
console.log("");
} else {
// Create log group
console.groupCollapsed(batchName);
// Generate list
console.log("$.require.batches[\"" + batch.id + "\"]", batch);
$.each(messages, function(i, message){
var state = message.state,
content = message.content;
if (!filter || state==filter) {
switch (state) {
case 'pending' : console.warn(content); break;
case 'rejected': console.error(content); break;
default : console.info(content); break;
}
}
});
console.groupEnd(batchName);
}
});
return "$.require.status(pending|resolved|rejected|ready);";
},
loaders: {},
addLoader: function(name, factory) {
// Static call, e.g.
// $.require.script.setup({});
self[name] = factory;
// Create proxy functions to require loaders,
// assigning current batch to factory's "this".
Batch.prototype[name] = function() {
var batch = this;
// Reset auto-finalize timer
batch.autoFinalize();
// this == batch
factory.apply(batch, arguments);
// Ensure require calls are chainable
return batch;
};
self.loaders[name] = self[name] = factory;
},
removeLoader: function(name) {
delete Batch.prototype[name];
delete self[name];
}
});
// This serves as batch id counter, it increments
// whenever a new batch instance is created.
var id = 0;
// Batch class.
// When calling $.require(), it is actually
// returning an new instance of this class.
var Batch = function(options) {
var required = $.Callbacks("once memory"),
isRequired = false;
// We are extending the batch instance
// with the following properties.
var batch = $.extend(this, {
// Unique ID for this batch.
id: ++id,
// This array keeps a list of tasks to load.
tasks: [],
// Stores options like load path, timeout and retry count.
options: $.extend({}, self.defaultOptions, options),
// Require chain automatically finalizes itself after
// 300ms if no promise methods were called in the require chain.
// Set false to disable.
autoFinalizeDuration: 300,
// When batch is finalized, further loader calls will be ignored.
finalized: false,
// Determine if the contents of the loaded task is required.
required: function(fn) {
if (fn===true) isRequired=true && required.fire();
if ($.isFunction(fn)) required.add(fn);
return isRequired;
}
});
return batch;
}
$.extend(Batch.prototype, {
addTask: function(task) {
var batch = this;
// Don't add invalid tasks.
// Tasks should be a deferred object.
if (!$.isDeferred(task)) return;
// Don't accept anymore tasks if this batch is finalized.
// Batch is finalized upon calling any of the promises, e.g.
// done, fail, progress, always, then, pipe
if (batch.finalized) return;
// Add this task to the batch's task list
batch.tasks.push(task);
// Decorate task with a reference to the current batch
task.batch = batch;
},
autoFinalize: function() {
var batch = this,
duration = batch.autoFinalizeDuration;
// If autoFinalize is disabled, stop.
if (duration===false) return;
// Clear previous timer
clearTimeout(batch.autoFinalizeTimer);
// Start a new timer
batch.autoFinalizeTimer =
setTimeout(function(){
batch.finalize();
}, duration);
},
finalize: function() {
var batch = this;
// If this batch has been finalized, stop.
if (batch.finalized) return;
// Finalize all tasks so no further
// tasks can be added to this batch.
batch.finalized = true;
// Create batch manager which is a
// master deferred object for all tasks.
var manager = batch.manager = $.when.apply(null, batch.tasks);
// Now that tasks are finalized, we can override
// this batch's pseudo-promise methods with actual
// promise methods from batch manager.
var promise = manager.promise(),
progress = $.Callbacks();
$.extend(batch, promise, {
// Progress & notify method behaves differently.
// We want progress callback to continue executing
// even after after manager has been resolved or rejected.
progress: progress.add,
notify : progress.fire,
// Done method also behaves differently.
// It will trigger an event notifying all tasks that
// there is a demand for the content of the task.
// This is currently used to lazy execute module factories
// to ensure they don't execute until they are asked for.
done: function(){
// Trigger required event
batch.required(true);
// After done has been called once, it will be
// replaced with the actual done method from the
// master deferred object.
batch.done = promise.done;
// And the actual done method gets executed.
return batch.done.apply(batch, arguments);
}
});
// Flag to indicate whether to make
// generate debug messages.
var verbose = batch.options.verbose;
manager
.progress(function(state, task){
if (verbose && state=="rejected") {
console.warn('Require: Task ' + task.name + ' failed to load.', task);
}
})
.fail(function(){
if (verbose) {
console.warn('Require: Batch ' + batch.id + ' failed.', batch);
}
});
// We wrap this in a setTimeout to let existing require chain
// to continue execute. This ensures that progress call in that
// require chain receives the activities of each task below.
setTimeout(function(){
// Always notify whenever there is an activity on every task.
$.each(batch.tasks, function(i, task){
task.then(
function(){ batch.notify("resolved", task) },
function(){ batch.notify("rejected", task) },
function(){ batch.notify("progress", task) }
);
});
}, 1);
},
expand: function(args, opts) {
var args = $.makeArray(args),
options = opts || {},
names = [];
if ($.isPlainObject(args[0])) {
options = $.extend(args[0], opts);
names = args.slice(1);
} else {
names = args;
}
return {
options: options,
names: names
}
}
});
// Masquerade newly created batch instances as a pseudo-promise object
// until one of those promise's method is called. This is to ensure that
// no callbacks are fired too early until all require tasks are finalized.
$.each(["done","fail","progress","always","then"], function(i, method) {
Batch.prototype[method] = function() {
var batch = this;
// Finalize batch
batch.finalize();
// Execute method that was originally called
return batch[method].apply(batch, arguments);
}
});
return self;
})();
/**
* jquery.require.script
* Script loader plugin for $.require.
*
* Part of jquery.require family.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('script', (function() {
// IE & Opera thinks punycoded urls are cross-domain requests,
// and rejects the ajax request because they think they don't have
// the necesary transport to facilitate such requests.
var ajaxHost = $.uri($.indexUrl).host(),
documentHost = $.uri(document.location.href).host();
if (ajaxHost!==documentHost && ajaxHost.match("xn--")) {
$.support.cors = true;
}
var canAsync = document.createElement("script").async === true || "MozAppearance" in document.documentElement.style || window.opera;
var self = function() {
var batch = this,
args = $.makeArray(arguments),
options,
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
options = $.extend(
{},
self.defaultOptions,
batch.options,
options,
{batch: batch}
);
// Create tasks and add it to the batch.
var taskBefore;
$.each(names, function(i, name) {
var task = new self.task(name, options, taskBefore);
batch.addTask(task);
// Serial script loading
if (options.serial && taskBefore!==undefined) {
// Only start current task when the
// task before is resolved/rejected.
taskBefore.always(task.start);
} else {
task.start();
}
taskBefore = task;
});
};
$.extend(self, {
defaultOptions: {
// Overrides require path.
path: '',
extension: (($.mode=='compressed') ? 'min.js' : 'js'),
// Serial script loading. Default: Parallel script loading.
serial: false,
// Asynchronous script execution. Default: Synchronous script execution.
async: false,
// Use XHR to load script. Default: Script injection.
xhr: false
},
setup: function() {
$.extend(self.defaultOptions, options);
},
scripts: {},
task: function(name, options, taskBefore) {
var task = $.extend(this, $.Deferred());
task.name = name;
task.options = options;
task.taskBefore = taskBefore;
// Module assignment or module url override
if ($.isArray(name)) {
task.name = name[0] + "@" + name[1];
task.moduleName = name[0];
var overrideModuleUrl = name[2];
// Module assignment
if (!overrideModuleUrl) {
// Set module flag
task.defineModule = true;
// Raise a warning if the module already exist
if ($.module.registry[task.moduleName]) {
console.warn("$.require.script: " + task.moduleName + ' exists! Using existing module instead.');
}
// Use XHR for module assignments
task.options.xhr = true;
}
// Assign path to be resolved
name = name[1];
task.module = $.module(task.moduleName);
}
// Resolve name to paths
// Absolute path
if ($.isUrl(name)) {
task.url = name;
// Relative path
} else if (/^(\/|\.)/.test(name)) {
task.url = $.uri(task.options.path)
.toPath(name)
.toString();
// Module path
} else {
task.url = $.uri(task.options.path)
.toPath('./' + name + '.' + task.options.extension)
.toString();
task.module = $.module(name);
}
}
});
$.extend(self.task.prototype, {
start: function() {
var task = this,
module = task.module;
// If module has already been loaded,
// we can skip the whole script loading process.
if (module && module.status!=="pending") {
task.waitForModule();
return;
}
// Else load the script that has this module.
task.load();
},
waitForModule: function() {
var task = this,
module = task.module;
// Listen to the events in the module
// without causing the module factory to execute.
module.then(
task.resolve,
task.reject,
task.notify
);
// When there is demand for this module,
// we will call the module's done method.
task.batch.required(function(){
// This will execute the module factory
// in case it wasn't executed before.
module.done(task.resolve);
});
},
load: function() {
var task = this,
taskBefore = task.taskBefore,
options = {};
// Use previously created script instance if exists,
// else create a new one.
task.script = self.scripts[task.url] || (function() {
var script = (task.options.xhr) ?
// Load script via ajax.
$.ajax({
url: task.url,
dataType: "text"
}) :
// Load script using script injection.
$.script({
url: task.url,
type: "text/javascript",
async: task.options.async,
timeout: task.batch.options.timeout,
retry: task.batch.options.retry,
verbose: task.batch.options.verbose
});
return self.scripts[task.url] = script;
})();
// At this point, script may be loaded, BUT may yet
// to be executed under the following conditions:
// - Module loaded via script injection/xhr.
// - Script loaded via via xhr.
task.script
.done(function(data) {
var resolveTask = function() {
// If task loads a module, resolve/reject task only when
// the module is resolved/rejected as the module itself
// may perform additional require tasks.
if (task.module) {
task.waitForModule();
} else {
task.resolve();
}
};
if (task.options.xhr) {
if (task.defineModule) {
// Create our own module factory
task.module = $.module(task.moduleName, function() {
var module = this;
$.globalEval(data);
module.resolveWith(data);
});
};
// For XHR, if scripts needs to be executed synchronously
// a.k.a. ordered script execution, then only eval it when
// the task before it is resolved.
if (!task.options.async || taskBefore) {
taskBefore.done(function() {
$.globalEval(data);
resolveTask();
});
return;
}
};
resolveTask();
})
.fail(function() {
task.reject();
});
}
});
return self;
})()
);
/**
* jquery.require.stylesheet
* Stylesheet loader plugin for $.require.
*
* Part of jquery.require family.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('stylesheet', (function() {
var self = function() {
var batch = this,
args = $.makeArray(arguments),
options,
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
options = $.extend(
{},
self.defaultOptions,
batch.options,
{
path: $.path + '/styles/'
},
options,
{batch: batch}
);
$.each(names, function(i, name) {
var task = new self.task(name, options),
existingTask = self.stylesheets[task.url];
task = existingTask || task;
batch.addTask(task);
if (!existingTask) {
self.stylesheets[task.url] = task;
task.start();
}
});
};
$.extend(self, {
defaultOptions: {
// Overrides require path.
path: '',
extension: (($.mode=='compressed') ? 'min.css' : 'css'),
// @TODO: XHR loading.
// Use XHR to load stylesheet. Default: Link injection. @import() for IE.
xhr: false
},
setup: function() {
$.extend(self.defaultOptions, options);
},
stylesheets: {},
task: function(name, options) {
var task = $.extend(this, $.Deferred());
task.name = name;
task.options = options;
// Absolute path
if ($.isUrl(name)) {
task.url = name;
// Relative path
} else if (/^(\/|\.)/.test(name)) {
task.url = $.uri(task.options.path)
.toPath(name)
.toString();
// Module path
} else {
task.url = $.uri(task.options.path)
.toPath('./' + name + '.' + task.options.extension)
.toString();
}
// Remap task.url to task.options.url
task.options.url = task.url;
},
loaders: {},
loader: function(name) {
// Pre-define loaders
if ($.isArray(name)) {
return $.map(name, function(name){
return self.loader(name);
});
}
// Resolve loaders
if ($.isPlainObject(name)) {
return $.map(name, function(name, options){
return self.loader(name).resolve(options);
});
}
// Get loader or create loaders
var loader = self.loaders[name];
if (!loader) {
loader = self.loaders[name] =
$.Deferred()
.done(function(options){
if ($.isPlainObject(options)) return;
$.stylesheet(options);
});
}
return loader;
}
});
$.extend(self.task.prototype, {
start: function() {
var task = this;
var loader = self.loaders[task.name];
// If this stylesheet hasn't been requested yet
if (!loader) {
// Create a stylesheet loader
loader = self.loader(task.name);
// Insert the stylesheet
if ($.stylesheet(task.options)) {
loader.resolve();
} else {
loader.reject();
}
}
loader.then(task.resolve, task.reject);
}
});
return self;
})()
);
/**
* jquery.require.template
* Template loader plugin for $.require.
*
* Part of jquery.require family.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('template', (function() {
var self = function() {
var batch = this,
args = $.makeArray(arguments),
options,
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
options = $.extend(
{},
self.defaultOptions,
batch.options,
options,
{batch: batch}
);
$.each(names, function(i, name) {
var task = new self.task(name, options);
batch.addTask(task);
task.start();
});
};
$.extend(self, {
defaultOptions: {
// Overrides require path.
path: '',
extension: 'htm'
},
setup: function() {
$.extend(self.defaultOptions, options);
},
task: function(name, options) {
var task = $.extend(this, $.Deferred());
task.name = name;
task.options = options;
// Template definition
if ($.isArray(name)) {
task.name = name[0];
// Assign path to be resolved
name = name[1];
}
// Absolute path
if ($.isUrl(name)) {
task.url = name;
// Relative path
} else if (/^(\/|\.)/.test(name)) {
task.url = $.uri(task.options.path)
.toPath(name)
.toString();
// Template module
} else {
task.url = $.uri(task.options.path)
.toPath('./' + name + '.' + task.options.extension)
.toString();
}
},
loaders: {},
loader: function(name) {
// Pre-define loaders
if ($.isArray(name)) {
return $.map(name, function(name){
return self.loader(name);
});
}
// Resolve loaders
if ($.isPlainObject(name)) {
return $.map(name, function(content, name){
return self.loader(name).resolve(content);
});
}
// Get loader or create loaders
var loader = self.loaders[name];
if (!loader) {
loader = self.loaders[name] =
$.Deferred()
.done(function(content){
$.template(name, content);
});
}
return loader;
}
});
$.extend(self.task.prototype, {
start: function() {
var task = this;
// See if there is an existing loader
var loader = self.loaders[task.name];
if (!loader) {
loader = self.loader(task.name);
loader.xhr =
$.Ajax({
url: task.url,
dataType: "text"
})
.then(loader.resolve, loader.reject)
.then(task.resolve, task.reject);
}
// Keep a reference to the loader in the task
task.loader = loader;
return task;
}
});
return self;
})()
);
/**
* jquery.require.language
* Language loader plugin for $.require.
*
* Part of foundry-module/require family.
* https://github.com/foundry-modules/require
*
* Copyright (c) 2011 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('language', (function() {
var self = function() {
var batch = this,
args = $.makeArray(arguments),
options,
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
options = $.extend(
{},
self.defaultOptions,
batch.options,
options,
{batch: batch}
);
var task = new self.task(names, options);
batch.addTask(task);
task.start();
};
$.extend(self, {
defaultOptions: {
// Overrides require path.
path: ''
},
setup: function() {
$.extend(self.defaultOptions, options);
},
loaders: {},
task: function(names, options) {
var task = $.extend(this, $.Deferred());
task.name = names.join(',');
task.options = options;
task.url = options.path;
task.names = names;
},
loaders: {},
loader: function(name) {
// Pre-define loaders
if ($.isArray(name)) {
return $.map(name, function(name){
return self.loader(name);
});
}
// Resolve loaders
if ($.isPlainObject(name)) {
return $.map(name, function(content, name){
return self.loader(name).resolve(content);
});
}
// Get loader or create loaders
var loader = self.loaders[name];
if (!loader) {
loader = self.loaders[name] =
$.Deferred()
.done(function(string){
$.language.add(name, string);
});
}
return loader;
}
});
$.extend(self.task.prototype, {
start: function() {
var task = this;
var loaders = [];
var names =
$.map(task.names, function(name){
// Get existing loader or predefine loaders
// so that subsequent require calls requesting
// the same language keys won't be loaded again.
var loader = self.loader(name);
// Keep it to our array of loaders
loader.push(loader);
// If the language has resolved or rejected
// remove it from list of language keys to load
if (/resolved|rejected/.test(loader.state())) return null;
return name;
});
// When unable to load language strings,
// reject language loaders.
task.fail(function(){
$.each(names, function(i, name){
self.loader(name).reject();
});
});
// When all language strings has been loaded,
// then we can resolve this task.
$.when.apply(null, loaders)
.then(task.resolve, task.reject);
// If there are no language strings to load,
// then wait for existing loaders to resolve or reject itself.
if (names.length < 1) return task;
task.xhr =
$.Ajax({
url: task.url,
type: "POST",
data: {
keys: names
}
})
.done(function(strings){
// If returned data is a language key-pair object, resolve task.
if ($.isPlainObject(strings)) {
self.loader(strings);
// We don't need to resolve as the $.when above will resolve for us.
} else {
task.reject();
}
})
.fail(function(){
task.reject();
});
return task;
}
});
return self;
})()
);
/**
* jquery.require.library
* Foundry script loader.
*
* Copyright (c) 2011 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('library', function() {
var batch = this,
args = $.makeArray(arguments),
options = {},
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
$.extend(options, {
path: $.scriptPath
});
return batch.script.apply(batch, [options].concat(names));
});
/**
* jquery.require.stylesheet
* Stylesheet loader plugin for $.require.
*
* Part of jquery.require family.
* https://github.com/jstonne/jquery.require
*
* Copyright (c) 2012 Jensen Tonne
* www.jstonne.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
$.require.addLoader('image', (function() {
var self = function() {
var batch = this,
args = $.makeArray(arguments),
options,
names;
// Expand arguments into its actual definition
if ($.isPlainObject(args[0])) {
options = args[0];
names = args.slice(1);
} else {
names = args;
}
options = $.extend(
{},
self.defaultOptions,
batch.options,
options,
{batch: batch}
);
$.each(names, function(i, name) {
var task = new self.task(name, options),
existingTask = self.images[task.url];
task = existingTask || task;
batch.addTask(task);
if (!existingTask) {
self.images[task.url] = task;
task.start();
}
});
};
$.extend(self, {
defaultOptions: {
// Overrides require path.
path: ''
},
setup: function() {
$.extend(self.defaultOptions, options);
},
images: {},
task: function(name, options) {
var task = $.extend(this, $.Deferred());
task.name = name;
task.options = options;
// Absolute path
if ($.isUrl(name)) {
task.url = name;
// Relative path
} else if (/^(\/|\.)/.test(name)) {
task.url = $.uri(task.options.path)
.toPath(name)
.toString();
// Module path
} else {
task.url = $.uri(task.options.path)
.toPath('./' + name)
.toString();
}
// Remap task.url to task.options.url
task.options.url = task.url;
}
});
$.extend(self.task.prototype, {
start: function() {
var task = this;
task.image = $(new Image())
.load(function(){
task.resolve();
})
.error(function(){
task.reject();
})
.attr("src", task.options.url);
}
});
return self;
})()
);
});
FD31.plugin("iframe-transport", function($) {
// This [jQuery](http://jquery.com/) plugin implements an `<iframe>`
// [transport](http://api.jquery.com/extending-ajax/#Transports) so that
// `$.ajax()` calls support the uploading of files using standard HTML file
// input fields. This is done by switching the exchange from `XMLHttpRequest`
// to a hidden `iframe` element containing a form that is submitted.
// The [source for the plugin](http://github.com/cmlenz/jquery-iframe-transport)
// is available on [Github](http://github.com/) and dual licensed under the MIT
// or GPL Version 2 licenses.
// ## Usage
// To use this plugin, you simply add an `iframe` option with the value `true`
// to the Ajax settings an `$.ajax()` call, and specify the file fields to
// include in the submssion using the `files` option, which can be a selector,
// jQuery object, or a list of DOM elements containing one or more
// `<input type="file">` elements:
// $("#myform").submit(function() {
// $.ajax(this.action, {
// files: $(":file", this),
// iframe: true
// }).complete(function(data) {
// console.log(data);
// });
// });
// The plugin will construct hidden `<iframe>` and `<form>` elements, add the
// file field(s) to that form, submit the form, and process the response.
// If you want to include other form fields in the form submission, include
// them in the `data` option, and set the `processData` option to `false`:
// $("#myform").submit(function() {
// $.ajax(this.action, {
// data: $(":text", this).serializeArray(),
// files: $(":file", this),
// iframe: true,
// processData: false
// }).complete(function(data) {
// console.log(data);
// });
// });
// ### Response Data Types
// As the transport does not have access to the HTTP headers of the server
// response, it is not as simple to make use of the automatic content type
// detection provided by jQuery as with regular XHR. If you can't set the
// expected response data type (for example because it may vary depending on
// the outcome of processing by the server), you will need to employ a
// workaround on the server side: Send back an HTML document containing just a
// `<textarea>` element with a `data-type` attribute that specifies the MIME
// type, and put the actual payload in the textarea:
// <textarea data-type="application/json">
// {"ok": true, "message": "Thanks so much"}
// </textarea>
// The iframe transport plugin will detect this and pass the value of the
// `data-type` attribute on to jQuery as if it was the "Content-Type" response
// header, thereby enabling the same kind of conversions that jQuery applies
// to regular responses. For the example above you should get a Javascript
// object as the `data` parameter of the `complete` callback, with the
// properties `ok: true` and `message: "Thanks so much"`.
// ### Handling Server Errors
// Another problem with using an `iframe` for file uploads is that it is
// impossible for the javascript code to determine the HTTP status code of the
// servers response. Effectively, all of the calls you make will look like they
// are getting successful responses, and thus invoke the `done()` or
// `complete()` callbacks. You can only determine communicate problems using
// the content of the response payload. For example, consider using a JSON
// response such as the following to indicate a problem with an uploaded file:
// <textarea data-type="application/json">
// {"ok": false, "message": "Please only upload reasonably sized files."}
// </textarea>
// ### Compatibility
// This plugin has primarily been tested on Safari 5 (or later), Firefox 4 (or
// later), and Internet Explorer (all the way back to version 6). While I
// haven't found any issues with it so far, I'm fairly sure it still doesn't
// work around all the quirks in all different browsers. But the code is still
// pretty simple overall, so you should be able to fix it and contribute a
// patch :)
// ## Annotated Source
// Register a prefilter that checks whether the `iframe` option is set, and
// switches to the "iframe" data type if it is `true`.
$.ajaxPrefilter(function(options, origOptions, jqXHR) {
if (options.iframe) {
return "iframe";
}
});
// Register a transport for the "iframe" data type. It will only activate
// when the "files" option has been set to a non-empty list of enabled file
// inputs.
$.ajaxTransport("iframe", function(options, origOptions, jqXHR) {
var form = null,
iframe = null,
name = "iframe-" + $.now(),
files = $(options.files).filter(":file:enabled"),
hasFiles = files.length > 0,
markers = null;
// This function gets called after a successful submission or an abortion
// and should revert all changes made to the page to enable the
// submission via this transport.
function cleanUp() {
if (hasFiles) {
markers.replaceWith(function(idx) {
return files.get(idx);
});
}
form.remove();
iframe.attr("src", "javascript:false;").remove();
}
// Remove "iframe" from the data types list so that further processing is
// based on the content type returned by the server, without attempting an
// (unsupported) conversion from "iframe" to the actual type.
options.dataTypes.shift();
form = $("<form enctype='multipart/form-data' method='post'></form>").
hide().attr({action: options.url, target: name});
// If there is any additional data specified via the `data` option,
// we add it as hidden fields to the form. This (currently) requires
// the `processData` option to be set to false so that the data doesn't
// get serialized to a string.
if (typeof(options.data) === "string" && options.data.length > 0) {
$.error("data must not be serialized");
}
$.each(options.data || {}, function(name, value) {
if ($.isPlainObject(value)) {
name = value.name;
value = value.value;
}
if (!$.isArray(value)) {
value = [value];
}
$.each(value, function(i, value){
$("<input type='hidden' />").attr({name: name, value: value}).
appendTo(form);
});
});
// Add a hidden `X-Requested-With` field with the value `IFrame` to the
// field, to help server-side code to determine that the upload happened
// through this transport.
$("<input type='hidden' value='IFrame' name='X-Requested-With' />").
appendTo(form);
// Move the file fields into the hidden form, but first remember their
// original locations in the document by replacing them with disabled
// clones. This should also avoid introducing unwanted changes to the
// page layout during submission.
if (hasFiles) {
markers = files.after(function(idx) {
return $(this).clone().prop("disabled", true);
}).next();
files.appendTo(form);
}
return {
// The `send` function is called by jQuery when the request should be
// sent.
send: function(headers, completeCallback) {
iframe = $("<iframe src='javascript:false;' name='" + name +
"' id='" + name + "' style='display:none'></iframe>");
// The first load event gets fired after the iframe has been injected
// into the DOM, and is used to prepare the actual submission.
iframe.bind("load", function() {
// The second load event gets fired when the response to the form
// submission is received. The implementation detects whether the
// actual payload is embedded in a `<textarea>` element, and
// prepares the required conversions to be made in that case.
iframe.unbind("load").bind("load", function() {
var doc = this.contentWindow ? this.contentWindow.document :
(this.contentDocument ? this.contentDocument : this.document),
root = doc.documentElement ? doc.documentElement : doc.body,
textarea = root.getElementsByTagName("textarea")[0],
type = textarea ? textarea.getAttribute("data-type") : null,
status = textarea ? textarea.getAttribute("data-status") : 200,
statusText = textarea ? textarea.getAttribute("data-statusText") : "OK",
content = {
html: root.innerHTML,
text: type ?
textarea.value :
root ? (root.textContent || root.innerText) : null
};
cleanUp();
completeCallback(status, statusText, content, type ?
("Content-Type: " + type) :
null);
});
// Now that the load handler has been set up, submit the form.
form[0].submit();
});
// After everything has been set up correctly, the form and iframe
// get injected into the DOM so that the submission can be
// initiated.
$("body").append(form, iframe);
},
// The `abort` function is called by jQuery when the request should be
// aborted.
abort: function() {
if (iframe !== null) {
iframe.unbind("load").attr("src", "javascript:false;");
cleanUp();
}
}
};
});
});
FD31.plugin("server", function($) {
/*!
* jquery.server.
* Extension of jquery.ajax with ability to parse server commands.
*
* Copyright (c) 2011 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var self = $.server = function(options) {
var request = $.Deferred(),
ajaxOptions = $.extend(true, {}, self.defaultOptions, options, {success: function(){}});
request.xhr = $.Ajax(ajaxOptions)
.done(function(commands){
if (typeof commands==="string") {
try {
commands = $.parseJSON(commands);
} catch(e) {
request.rejectWith(request, ["Unable to parse Ajax commands.", "error"])
}
}
if (!$.isArray(commands)) {
request.rejectWith(request, ["Invalid ajax commands.", "error"]);
} else {
var parse = function(command){
var type = command.type,
parser = self.parsers[type] || options[type];
if ($.isFunction(parser)) {
return parser.apply(request, command.data);
}
}
// Execute all the notifications first
var commands = $.map(commands, function(command) {
if (command.type=="notify") {
parse(command);
} else {
return command;
}
})
$.each(commands, function(i, command) {
parse(command);
});
}
// If server did not resolve this request
if (request.state()==="pending") {
// We'll resolve it ourselves
request.resolveWith(request);
}
})
.fail(function(xhr, status, statusText){
request.rejectWith(request, [statusText, status]);
});
return request;
};
self.defaultOptions = {
type: 'POST',
data: {
tmpl: 'component',
format: 'ajax',
no_html: 1
},
dataType: 'json'
};
self.parsers = {
script: function() {
var data = $.makeArray(arguments);
// For hardcoded javascript codes
if (typeof data[0] == 'string') {
try { eval(data[0]) } catch(err) {};
return;
}
/**
* Execute each method and assign returned object back to the chain.
*
* Foundry().attr('checked', true);
* is equivalent to:
* window['Foundry']('.element')[attr]('checked', true);
*/
var chain = window, chainBroken = false;
$.each(data, function(i, chainer)
{
if (chainer.property==="Foundry") {
chainer.property = $.globalNamespace;
}
if (chainer.method==="Foundry") {
chainer.method = $.globalNamespace;
}
try {
switch(chainer.type)
{
case 'get':
chain = chain[chainer.property];
break;
case 'set':
chain[chainer.property] = chainer.value;
chainBroken=true;
break;
case 'call':
chain = chain[chainer.method].apply(chain, chainer.args);
break;
}
} catch(err) {
chainBroken = true;
}
})
},
resolve: function() {
this.resolveWith(this, arguments);
},
reject: function() {
this.rejectWith(this, arguments);
},
notify: function() {
this.notifyWith(this, arguments);
},
redirect: function(url) {
window.location = url;
}
};
});
FD31.plugin("component", function($) {
/**
* jquery.component.
* Boilerplate for client-side MVC application.
*
* Copyright (c) 2011 Jason Ramos
* www.stackideas.com
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
var Component = $.Component = function(name, options) {
if (arguments.length < 1) {
return Component.registry;
}
if (arguments.length < 2) {
return Component.registry[name];
}
return Component.register(name, options);
}
Component.registry = {};
Component.proxy = function(component, property, value) {
// If it's a method
if ($.isFunction(value)) {
// Change the "this" context to the component itself
component[property] = $.proxy(value, component);
} else {
component[property] = value;
}
}
Component.register = function(name, options) {
// If an abstract component was passed in
var abstractComponent;
// Normalize arguments
if ($.isFunction(name)) {
abstractComponent = name;
name = abstractComponent.className;
options = abstractComponent.options;
}
var self =
// Put it in component registry
Component.registry[name] =
// Set it to the global namespace
window[name] =
// When called as a function, it will return the correct jQuery object.
function(command) {
return ($.isFunction(command)) ? command($) : component;
};
// Extend component with properties in component prototype
$.each(Component.prototype, function(property, value) {
Component.proxy(self, property, value);
});
self.$ = $;
self.options = options;
self.className = name;
self.identifier = name.toLowerCase();
self.componentName = "com_" + self.identifier;
self.prefix = self.identifier + "/";
self.version = options.version;
self.safeVersion = self.version.replace(/\./g,"");
self.environment = options.environment || $.environment;
self.mode = options.mode || $.mode;
self.debug = (self.environment==='development');
self.console = Component.console(self);
self.language = options.language || $.locale.lang || "en";
self.baseUrl = options.baseUrl || $.indexUrl + "?option=" + self.componentName;
self.ajaxUrl = options.ajaxUrl || $.basePath + "/?option=" + self.componentName;
self.scriptPath = options.scriptPath || $.rootPath + "/media/" + self.componentName + "/scripts/";
self.stylePath = options.stylePath || $.rootPath + "/media/" + self.componentName + "/styles/";
self.templatePath = options.templatePath || options.scriptPath;
self.languagePath = options.languagePath || self.ajaxUrl + '&tmpl=component&no_html=1&controller=lang&task=getLanguage';
self.viewPath = options.viewPath || self.ajaxUrl + '&tmpl=component&no_html=1&controller=themes&task=getAjaxTemplate';
self.optimizeResources = true;
self.resourcePath = options.resourcePath || self.ajaxUrl + '&tmpl=component&no_html=1&controller=foundry&task=getResource';
self.resourceInterval = 1200; // Joomla session timestamp is per second, we add another 200ms just to be safe.
self.scriptVersioning = options.scriptVersioning || false;
self.token = (abstractComponent || {}).token;
self.tasks = [];
// Register component to bootleader
FD31.component(name, self);
// If there's no abstract componet prior to this, we're done!
if (!abstractComponent) return;
// If we're on development mode
if (self.debug) {
// Execute queue in abstract component straightaway
abstractComponent.queue.execute();
// If we're on static or optimized mode
} else {
// Get component installers from bootloader and install them
var installer, installers = FD31.installer(name);
while(installer = installers.shift()) {
self.install.apply(self, installer);
}
// Wait until definitions, scripts & resources are installed
$.when(
self.install("definitions"),
self.install("scripts"),
self.install("resources")
).done(function(){
// Then only execute queue in abstract component.
abstractComponent.queue.execute();
});
}
}
Component.extend = function(property, value) {
// For later components
Component.prototype[property] = value;
// For existing components
$.each(Component.registry, function(name, component) {
Component.proxy(component, property, value);
});
}
$.template("component/console",'<div id="[%== component.identifier %]-console" class="foundry-console" style="display: none; z-index: 999999;"><div class="console-header"><div class="console-title">[%= component.className %] [%= component.version %]</div><div class="console-remove-button">x</div></div><div class="console-log-item-group"></div><style type="text/css">.foundry-console{position:fixed;width:50%;height:50%;bottom:0;left:0;background:white;box-shadow: 0 0 5px 0;margin-left: 25px;}.console-log-item-group{width: 100%;height: 100%;overflow-y:scroll;}.console-header{position: absolute;background:red;color:white;font-weight:bold;top:-24px;left: 0;line-height:24px;width:100%}.console-remove-button{text-align:center;cursor: pointer;display:block;width: 24px;float:right}.console-remove-button:hover{color: yellow}.console-title{padding: 0 5px;float:left}.console-log-item{padding: 5px}.console-log-item + .console-log-item{border-top: 1px solid #ccc}</style></div>');
Component.console = function(component) {
return (function(self){
var instance = function(method) {
if (arguments.length < 1) {
return instance.toggle();
}
return instance[method] && instance[method].apply(instance, arguments);
},
element;
instance.selector = "#" + self.identifier + "-console";
instance.init = (function() {
element = $(instance.selector);
if (element.length < 1) {
element = $($.View("component/console", {component: self})).appendTo("body");
element.find(".console-remove-button").click(function(){
element.hide();
});
}
instance.element = element;
return arguments.callee;
})();
instance.methods = {
log: function(message, type, code) {
type = type || "info";
var itemGroup = element.find(".console-log-item-group"),
item =
$(document.createElement("div"))
.addClass("console-log-item type-" + type)
.attr("data-code", code)
.html(message);
itemGroup.append(item);
itemGroup[0].scrollTop = itemGroup[0].scrollHeight;
// Automatically show window on each log
if (self.debug) { element.show(); }
},
toggle: function() {
element.toggle();
},
reset: function() {
element.find(".console-log-item-group").empty();
}
};
$.each(instance.methods, function(method, fn) {
instance[method] = function() {
instance.init(); // Always call init in case of destruction of element
return fn.apply(instance, arguments);
}
});
return instance;
})(component);
}
var doc = $(document),
proto = Component.prototype;
$.extend(proto, {
run: function(command) {
return ($.isFunction(command)) ? command($) : this;
},
ready: (function(){
// Replace itself once document is ready
doc.ready(function(){
proto.ready = proto.run;
});
return function(callback) {
if (!$.isFunction(callback)) return;
// When document is ready
doc.ready(function() {
callback($);
});
}
})(),
install: function(name, factory) {
var self = this,
task = self.tasks[name] || (self.tasks[name] = $.Deferred());
// Getter
if (!factory) return task;
// Setter
var install = function(){
factory($, self);
return task.resolve();
}
// If this is installer contains component definitions,
// install straightaway.
if (name=="definitions") return install();
// Else for component definitiosn to install first,
// then only install this installer.
$.when(self.install("definitions")).done(install);
},
template: function(name) {
var self = this;
// Get all component templates
if (name==undefined) {
return $.grep($.template(), function(template) {
return template.indexOf(self.prefix)==0;
});
}
// Prepend component prefix
arguments[0] = self.prefix + name;
// Getter or setter
return $.template.apply(null, arguments);
},
// Component require extends $.require with the following additional methods:
// - resource()
// - view()
// - language()
//
// It also changes the behaviour of existing methods to load in component-specific behaviour.
require: function(options) {
var self = this,
options = options || {},
require = $.require(options),
_require = {};
// Keep a copy of the original method so the duck punchers below can use it.
$.each(["library", "script", "language", "template", "done"], function(i, method){
_require[method] = require[method];
});
// Resource call should NOT be called directly.
// .resource({type: "view", name: "photo.item", loader: deferredObject})
require.resource = function(resource) {
// If this is not a valid resource object, skip.
if (!$.isPlainObject(resource)) return;
if (!resource.type || !resource.name || !$.isDeferred(resource.loader)) return;
var batch = this;
// Get resource collector
var resourceCollector = self.resourceCollector;
// If we haven't started collecting resources
if (!resourceCollector) {
// Then start collecting resources
resourceCollector = self.resourceCollector = $.Deferred();
$.extend(resourceCollector, {
name: $.uid("ResourceCollector"),
manifest: [],
loaderList: [],
loaders: [],
load: function() {
// End this batch of resource collecting
delete self.resourceCollector;
// If there are not resources to pull,
// just resolve resource collector.
if (resourceCollector.manifest.length < 0) {
resourceCollector.resolve();
return;
}
var retry = 0;
var loadResources = function(){
retry++;
$.Ajax(
{
type: 'POST',
url: self.resourcePath,
dataType: "json",
data: {
resource: resourceCollector.manifest
}
})
.done(function(manifest) {
if (!$.isArray(manifest)) {
resourceCollector.reject("Server did not return a valid resource manifest.");
return;
}
$.each(manifest, function(i, resource) {
var content = resource.content;
resourceCollector.loaders[resource.id]
[content!==undefined ? "resolve" : "reject"]
(content);
});
if (retry > 1 && self.debug) {
console.info("Attempt to try and get resources again was successful!");
}
})
.fail(function(){
if (retry > 2) {
if (self.debug) { console.error("Unable to get resource again. Giving up!"); };
return;
}
if (self.debug) {
console.warn("Unable to get resource. Trying again...");
}
loadResources();
});
}
loadResources();
// Resolve resource collector when all is done
$.when.apply(null, resourceCollector.loaderList)
.done(resourceCollector.resolve)
.fail(resourceCollector.reject);
}
});
setTimeout(resourceCollector.load, self.resourceCollectionInterval);
}
// Create a resource id
var id = resource.id = $.uid("Resource");
// Add to the loader map
// - to be used to resolve the loader with the returned content
resourceCollector.loaders[id] = resource.loader;
// Add to the loader list
// - to be used with $.when()
resourceCollector.loaderList.push(resource.loader);
// Remove the reference to the loader
// - so the loader doesn't get included in the manifest that gets sent to the server
delete resource.loader;
// Then add it to our list of resource manifest
resourceCollector.manifest.push(resource);
// Note: Only resource loaders are batch tasks, not resource collectors.
// var task = resourceCollector;
// batch.addTask(task);
return require;
};
require.view = function() {
var batch = this,
request = batch.expand(arguments, {path: self.viewPath}),
loaders = {},
options = request.options,
names = $.map(request.names, function(name) {
// Get template loader
var absoluteName = self.prefix + name,
loader = $.require.template.loaders[absoluteName];
// If this is being loaded, skip.
if (loader) return;
loader = $.require.template.loader(absoluteName);
loader.name = absoluteName;
// Add template loader as a task of this batch
batch.addTask(loader);
// Load as part of a coalesced ajax call if enabled
if (self.optimizeResources) {
require.resource({
type: "view",
name: name,
loader: loader
});
return;
} else {
loaders[name] = loader;
return name;
}
});
// Load using regular ajax call
// This will always be zero when optimizeResources is enabled.
if (names.length > 0) {
$.Ajax(
{
url: options.path,
dataType: "json",
data: { names: names }
})
.done(function(templates) {
if (!$.isArray(templates)) return;
$.each(templates, function(i, template) {
var content = template.content;
loaders[template.name]
[content!==undefined ? "resolve" : "reject"]
(content);
});
});
}
return require;
};
require.language = function() {
var batch = this,
request = batch.expand(arguments, {path: self.languagePath});
// Load as part of a coalesced ajax call if enabled
if (self.optimizeResources) {
$.each(request.names, function(i, name) {
var loader = $.require.language.loaders[name];
if (loader) return;
loader = $.require.language.loader(name);
loader.name = name;
batch.addTask(loader);
require.resource({
type: "language",
name: name,
loader: loader
});
});
} else {
_require.language.apply(require, [request.options].concat(request.names));
}
return require;
};
require.library = function() {
_require.script.apply(this, arguments);
return require;
};
require.script = function() {
var batch = this,
request = batch.expand(arguments, {path: self.scriptPath})
names = $.map(request.names, function(name) {
// Ignore module definitions
if ($.isArray(name) ||
// and urls
$.isUrl(name) ||
// and relative paths.
/^(\/|\.)/.test(name)) return name;
var moduleName = self.prefix + name,
moduleUrl =
$.uri(request.options.path)
.toPath(
'./' + name + '.' + (request.options.extension || 'js') +
((self.scriptVersioning) ? "?" + "version=" + self.safeVersion : "")
)
.toString();
return [[moduleName, moduleUrl, true]];
});
_require.script.apply(require, [request.options].concat(names));
return require;
};
// Override path
require.template = function() {
var batch = this,
request = batch.expand(arguments, {path: self.templatePath});
_require.template.apply(require, [request.options].concat(
$.map(request.names, function(name) {
return [[self.prefix + name, name]];
})
));
return require;
};
// Only execute require done callback when component is ready
require.done = function(callback) {
return _require.done.call(require, function(){
self.ready(callback);
});
};
return require;
},
module: function(name, factory) {
var self = this;
// TODO: Support for multiple module factory assignment
if ($.isArray(name)) {
return;
}
var fullname = self.prefix + name;
return (factory) ?
// Set module
$.module.apply(null, [fullname, function(){
var module = this;
factory.call(module, $);
}])
:
// Get module
$.module(fullname);
}
});
$.Component.extend("getToken", function(){
var self = this;
// Look for an updated token replaced by Joomla on page load and use
// that token instead. This is for sites where cache is turned on.
return $("span#" + self.identifier + "-token input").attr("name") || self.token;
});
$.Component.extend("ajax", function(namespace, params, callback) {
var self = this;
var options = {
url: self.ajaxUrl,
data: $.extend(
params,
{
option: self.componentName,
namespace: namespace
}
)
};
options = $.extend(true, options, self.options.ajax);
options.data[self.getToken()] = 1;
// This is for server-side function arguments
if (options.data.hasOwnProperty('args')) {
options.data.args = $.toJSON(options.data.args);
}
if ($.isPlainObject(callback)) {
if (callback.type) {
switch (callback.type) {
case 'jsonp':
callback.dataType = 'jsonp';
// This ensure jQuery doesn't use XHR should it detect the ajax url is a local domain.
callback.crossDomain = true;
options.data.transport = 'jsonp';
break;
case 'iframe':
// For use with iframe-transport
callback.iframe = true;
callback.processData = false;
callback.files = options.data.files;
delete options.data.files;
options.data.transport = 'iframe';
break;
}
delete callback.type;
}
$.extend(options, callback);
}
if ($.isFunction(callback)) {
options.success = callback;
}
var ajax = $.server(options);
ajax.progress(function(message, type, code) {
if (type=="debug") {
self.console.log(message, type, code);
}
});
return ajax;
});
$.Component.extend("Controller", function() {
var self = this,
args = $.makeArray(arguments),
name = args[0],
staticProps,
protoFactory;
// Getter
if (args.length==1) {
return $.String.getObject(name);
};
// Setter
if (args.length > 2) {
staticProps = args[1],
protoFactory = args[2]
} else {
staticProps = {},
protoFactory = args[1]
}
// Map component as a static property
// of the controller class
$.extend(staticProps, {
root: self.className + '.Controller',
component: self
});
return $.Controller.apply(this, [name, staticProps, protoFactory]);
});
$.Component.extend("Model", function() {
var self = this,
args = $.makeArray(arguments),
name = self.className + '.Model.' + args[0],
staticProps,
protoFactory;
// Getter
if (args.length==1) {
return $.String.getObject(args[0]);
}
if( args.length==2) {
staticProps = {},
protoFactory = args[1]
}
if( args.length > 2) {
staticProps = args[1],
protoFactory = args[2]
}
// Map component as a static property
// of the model class
$.extend(staticProps, {
component: self
});
return $.Model.apply(this, [name, staticProps, protoFactory]);
});
$.Component.extend("Model.List", function() {
var self = this,
args = $.makeArray(arguments),
name = self.className + '.Model.List.' + args[0],
staticProps,
protoFactory;
// Getter
if (args.length==1) {
return $.String.getObject(args[0]);
}
if( args.length==2) {
staticProps = {},
protoFactory = args[1]
}
if( args.length > 2) {
staticProps = args[1],
protoFactory = args[2]
}
// Map component as a static property
// of the model class
$.extend(staticProps, {
component: self
});
return $.Model.List.apply(this, [name, staticProps, protoFactory]);
});
$.Component.extend("View", function(name) {
var self = this;
// Gett all component views
if (arguments.length < 1) {
return self.template();
}
// Prepend component prefix
arguments[0] = self.prefix + arguments[0];
// Getter or setter
return $.View.apply(this, arguments);
});
// Component should always be the last core plugin to load.
// Execute all pending foundry modules
FD31.module.execute();
// Get all abstract components
$.each(FD31.component(), function(i, abstractComponent){
// If this component is registered, stop.
if (abstractComponent.registered) return;
// Create an instance of the component
$.Component.register(abstractComponent);
});
});