mirror of
https://github.com/snipe/snipe-it.git
synced 2025-03-05 20:52:15 -08:00
59515 lines
1.7 MiB
59515 lines
1.7 MiB
/*!
|
||
* jQuery JavaScript Library v3.5.1
|
||
* https://jquery.com/
|
||
*
|
||
* Includes Sizzle.js
|
||
* https://sizzlejs.com/
|
||
*
|
||
* Copyright JS Foundation and other contributors
|
||
* Released under the MIT license
|
||
* https://jquery.org/license
|
||
*
|
||
* Date: 2020-05-04T22:49Z
|
||
*/
|
||
( function( global, factory ) {
|
||
|
||
"use strict";
|
||
|
||
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 have a `window` with a `document`
|
||
// (such as Node.js), expose a 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 ) {
|
||
|
||
// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
|
||
// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
|
||
// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
|
||
// enough that all such attempts are guarded in a try block.
|
||
"use strict";
|
||
|
||
var arr = [];
|
||
|
||
var getProto = Object.getPrototypeOf;
|
||
|
||
var slice = arr.slice;
|
||
|
||
var flat = arr.flat ? function( array ) {
|
||
return arr.flat.call( array );
|
||
} : function( array ) {
|
||
return arr.concat.apply( [], array );
|
||
};
|
||
|
||
|
||
var push = arr.push;
|
||
|
||
var indexOf = arr.indexOf;
|
||
|
||
var class2type = {};
|
||
|
||
var toString = class2type.toString;
|
||
|
||
var hasOwn = class2type.hasOwnProperty;
|
||
|
||
var fnToString = hasOwn.toString;
|
||
|
||
var ObjectFunctionString = fnToString.call( Object );
|
||
|
||
var support = {};
|
||
|
||
var isFunction = function isFunction( obj ) {
|
||
|
||
// Support: Chrome <=57, Firefox <=52
|
||
// In some browsers, typeof returns "function" for HTML <object> elements
|
||
// (i.e., `typeof document.createElement( "object" ) === "function"`).
|
||
// We don't want to classify *any* DOM node as a function.
|
||
return typeof obj === "function" && typeof obj.nodeType !== "number";
|
||
};
|
||
|
||
|
||
var isWindow = function isWindow( obj ) {
|
||
return obj != null && obj === obj.window;
|
||
};
|
||
|
||
|
||
var document = window.document;
|
||
|
||
|
||
|
||
var preservedScriptAttributes = {
|
||
type: true,
|
||
src: true,
|
||
nonce: true,
|
||
noModule: true
|
||
};
|
||
|
||
function DOMEval( code, node, doc ) {
|
||
doc = doc || document;
|
||
|
||
var i, val,
|
||
script = doc.createElement( "script" );
|
||
|
||
script.text = code;
|
||
if ( node ) {
|
||
for ( i in preservedScriptAttributes ) {
|
||
|
||
// Support: Firefox 64+, Edge 18+
|
||
// Some browsers don't support the "nonce" property on scripts.
|
||
// On the other hand, just using `getAttribute` is not enough as
|
||
// the `nonce` attribute is reset to an empty string whenever it
|
||
// becomes browsing-context connected.
|
||
// See https://github.com/whatwg/html/issues/2369
|
||
// See https://html.spec.whatwg.org/#nonce-attributes
|
||
// The `node.getAttribute` check was added for the sake of
|
||
// `jQuery.globalEval` so that it can fake a nonce-containing node
|
||
// via an object.
|
||
val = node[ i ] || node.getAttribute && node.getAttribute( i );
|
||
if ( val ) {
|
||
script.setAttribute( i, val );
|
||
}
|
||
}
|
||
}
|
||
doc.head.appendChild( script ).parentNode.removeChild( script );
|
||
}
|
||
|
||
|
||
function toType( obj ) {
|
||
if ( obj == null ) {
|
||
return obj + "";
|
||
}
|
||
|
||
// Support: Android <=2.3 only (functionish RegExp)
|
||
return typeof obj === "object" || typeof obj === "function" ?
|
||
class2type[ toString.call( obj ) ] || "object" :
|
||
typeof obj;
|
||
}
|
||
/* global Symbol */
|
||
// Defining this global in .eslintrc.json would create a danger of using the global
|
||
// unguarded in another place, it seems safer to define global only for this module
|
||
|
||
|
||
|
||
var
|
||
version = "3.5.1",
|
||
|
||
// 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 );
|
||
};
|
||
|
||
jQuery.fn = jQuery.prototype = {
|
||
|
||
// The current version of jQuery being used
|
||
jquery: version,
|
||
|
||
constructor: jQuery,
|
||
|
||
// 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 all the elements in a clean array
|
||
if ( num == null ) {
|
||
return slice.call( this );
|
||
}
|
||
|
||
// Return just the one element from the set
|
||
return num < 0 ? this[ num + this.length ] : this[ num ];
|
||
},
|
||
|
||
// 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;
|
||
|
||
// Return the newly-formed element set
|
||
return ret;
|
||
},
|
||
|
||
// Execute a callback for every element in the matched set.
|
||
each: function( callback ) {
|
||
return jQuery.each( this, callback );
|
||
},
|
||
|
||
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 );
|
||
},
|
||
|
||
even: function() {
|
||
return this.pushStack( jQuery.grep( this, function( _elem, i ) {
|
||
return ( i + 1 ) % 2;
|
||
} ) );
|
||
},
|
||
|
||
odd: function() {
|
||
return this.pushStack( jQuery.grep( this, function( _elem, i ) {
|
||
return i % 2;
|
||
} ) );
|
||
},
|
||
|
||
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();
|
||
},
|
||
|
||
// For internal use only.
|
||
// Behaves like an Array's method, not like a jQuery method.
|
||
push: push,
|
||
sort: arr.sort,
|
||
splice: arr.splice
|
||
};
|
||
|
||
jQuery.extend = jQuery.fn.extend = function() {
|
||
var options, name, src, copy, copyIsArray, clone,
|
||
target = arguments[ 0 ] || {},
|
||
i = 1,
|
||
length = arguments.length,
|
||
deep = false;
|
||
|
||
// Handle a deep copy situation
|
||
if ( typeof target === "boolean" ) {
|
||
deep = target;
|
||
|
||
// Skip the boolean and the target
|
||
target = arguments[ i ] || {};
|
||
i++;
|
||
}
|
||
|
||
// Handle case when target is a string or something (possible in deep copy)
|
||
if ( typeof target !== "object" && !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 ) {
|
||
copy = options[ name ];
|
||
|
||
// Prevent Object.prototype pollution
|
||
// Prevent never-ending loop
|
||
if ( name === "__proto__" || target === copy ) {
|
||
continue;
|
||
}
|
||
|
||
// Recurse if we're merging plain objects or arrays
|
||
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
|
||
( copyIsArray = Array.isArray( copy ) ) ) ) {
|
||
src = target[ name ];
|
||
|
||
// Ensure proper type for the source value
|
||
if ( copyIsArray && !Array.isArray( src ) ) {
|
||
clone = [];
|
||
} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
|
||
clone = {};
|
||
} else {
|
||
clone = src;
|
||
}
|
||
copyIsArray = false;
|
||
|
||
// 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() {},
|
||
|
||
isPlainObject: function( obj ) {
|
||
var proto, Ctor;
|
||
|
||
// Detect obvious negatives
|
||
// Use toString instead of jQuery.type to catch host objects
|
||
if ( !obj || toString.call( obj ) !== "[object Object]" ) {
|
||
return false;
|
||
}
|
||
|
||
proto = getProto( obj );
|
||
|
||
// Objects with no prototype (e.g., `Object.create( null )`) are plain
|
||
if ( !proto ) {
|
||
return true;
|
||
}
|
||
|
||
// Objects with prototype are plain iff they were constructed by a global Object function
|
||
Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
|
||
return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
|
||
},
|
||
|
||
isEmptyObject: function( obj ) {
|
||
var name;
|
||
|
||
for ( name in obj ) {
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
|
||
// Evaluates a script in a provided context; falls back to the global one
|
||
// if not specified.
|
||
globalEval: function( code, options, doc ) {
|
||
DOMEval( code, { nonce: options && options.nonce }, doc );
|
||
},
|
||
|
||
each: function( obj, callback ) {
|
||
var length, i = 0;
|
||
|
||
if ( isArrayLike( obj ) ) {
|
||
length = obj.length;
|
||
for ( ; i < length; i++ ) {
|
||
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
for ( i in obj ) {
|
||
if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return obj;
|
||
},
|
||
|
||
// results is for internal usage only
|
||
makeArray: function( arr, results ) {
|
||
var ret = results || [];
|
||
|
||
if ( arr != null ) {
|
||
if ( isArrayLike( Object( arr ) ) ) {
|
||
jQuery.merge( ret,
|
||
typeof arr === "string" ?
|
||
[ arr ] : arr
|
||
);
|
||
} else {
|
||
push.call( ret, arr );
|
||
}
|
||
}
|
||
|
||
return ret;
|
||
},
|
||
|
||
inArray: function( elem, arr, i ) {
|
||
return arr == null ? -1 : indexOf.call( arr, elem, i );
|
||
},
|
||
|
||
// Support: Android <=4.0 only, PhantomJS 1 only
|
||
// push.apply(_, arraylike) throws on ancient WebKit
|
||
merge: function( first, second ) {
|
||
var len = +second.length,
|
||
j = 0,
|
||
i = first.length;
|
||
|
||
for ( ; j < len; j++ ) {
|
||
first[ i++ ] = second[ j ];
|
||
}
|
||
|
||
first.length = i;
|
||
|
||
return first;
|
||
},
|
||
|
||
grep: function( elems, callback, invert ) {
|
||
var callbackInverse,
|
||
matches = [],
|
||
i = 0,
|
||
length = elems.length,
|
||
callbackExpect = !invert;
|
||
|
||
// Go through the array, only saving the items
|
||
// that pass the validator function
|
||
for ( ; i < length; i++ ) {
|
||
callbackInverse = !callback( elems[ i ], i );
|
||
if ( callbackInverse !== callbackExpect ) {
|
||
matches.push( elems[ i ] );
|
||
}
|
||
}
|
||
|
||
return matches;
|
||
},
|
||
|
||
// arg is for internal usage only
|
||
map: function( elems, callback, arg ) {
|
||
var length, value,
|
||
i = 0,
|
||
ret = [];
|
||
|
||
// Go through the array, translating each of the items to their new values
|
||
if ( isArrayLike( elems ) ) {
|
||
length = elems.length;
|
||
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 flat( ret );
|
||
},
|
||
|
||
// A global GUID counter for objects
|
||
guid: 1,
|
||
|
||
// jQuery.support is not used in Core but other projects attach their
|
||
// properties to it so it needs to exist.
|
||
support: support
|
||
} );
|
||
|
||
if ( typeof Symbol === "function" ) {
|
||
jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
|
||
}
|
||
|
||
// Populate the class2type map
|
||
jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
|
||
function( _i, name ) {
|
||
class2type[ "[object " + name + "]" ] = name.toLowerCase();
|
||
} );
|
||
|
||
function isArrayLike( obj ) {
|
||
|
||
// Support: real iOS 8.2 only (not reproducible in simulator)
|
||
// `in` check used to prevent JIT error (gh-2145)
|
||
// hasOwn isn't used here due to false negatives
|
||
// regarding Nodelist length in IE
|
||
var length = !!obj && "length" in obj && obj.length,
|
||
type = toType( obj );
|
||
|
||
if ( isFunction( obj ) || isWindow( obj ) ) {
|
||
return false;
|
||
}
|
||
|
||
return type === "array" || length === 0 ||
|
||
typeof length === "number" && length > 0 && ( length - 1 ) in obj;
|
||
}
|
||
var Sizzle =
|
||
/*!
|
||
* Sizzle CSS Selector Engine v2.3.5
|
||
* https://sizzlejs.com/
|
||
*
|
||
* Copyright JS Foundation and other contributors
|
||
* Released under the MIT license
|
||
* https://js.foundation/
|
||
*
|
||
* Date: 2020-03-14
|
||
*/
|
||
( function( window ) {
|
||
var i,
|
||
support,
|
||
Expr,
|
||
getText,
|
||
isXML,
|
||
tokenize,
|
||
compile,
|
||
select,
|
||
outermostContext,
|
||
sortInput,
|
||
hasDuplicate,
|
||
|
||
// Local document vars
|
||
setDocument,
|
||
document,
|
||
docElem,
|
||
documentIsHTML,
|
||
rbuggyQSA,
|
||
rbuggyMatches,
|
||
matches,
|
||
contains,
|
||
|
||
// Instance-specific data
|
||
expando = "sizzle" + 1 * new Date(),
|
||
preferredDoc = window.document,
|
||
dirruns = 0,
|
||
done = 0,
|
||
classCache = createCache(),
|
||
tokenCache = createCache(),
|
||
compilerCache = createCache(),
|
||
nonnativeSelectorCache = createCache(),
|
||
sortOrder = function( a, b ) {
|
||
if ( a === b ) {
|
||
hasDuplicate = true;
|
||
}
|
||
return 0;
|
||
},
|
||
|
||
// Instance methods
|
||
hasOwn = ( {} ).hasOwnProperty,
|
||
arr = [],
|
||
pop = arr.pop,
|
||
pushNative = arr.push,
|
||
push = arr.push,
|
||
slice = arr.slice,
|
||
|
||
// Use a stripped-down indexOf as it's faster than native
|
||
// https://jsperf.com/thor-indexof-vs-for/5
|
||
indexOf = function( list, elem ) {
|
||
var i = 0,
|
||
len = list.length;
|
||
for ( ; i < len; i++ ) {
|
||
if ( list[ 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
|
||
|
||
// http://www.w3.org/TR/css3-selectors/#whitespace
|
||
whitespace = "[\\x20\\t\\r\\n\\f]",
|
||
|
||
// https://www.w3.org/TR/css-syntax-3/#ident-token-diagram
|
||
identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
|
||
"?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
|
||
|
||
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
|
||
attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
|
||
|
||
// Operator (capture 2)
|
||
"*([*^$|!~]?=)" + whitespace +
|
||
|
||
// "Attribute values must be CSS identifiers [capture 5]
|
||
// or strings [capture 3 or capture 4]"
|
||
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
|
||
whitespace + "*\\]",
|
||
|
||
pseudos = ":(" + identifier + ")(?:\\((" +
|
||
|
||
// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
|
||
// 1. quoted (capture 3; capture 4 or capture 5)
|
||
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
|
||
|
||
// 2. simple (capture 6)
|
||
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
|
||
|
||
// 3. anything else (capture 2)
|
||
".*" +
|
||
")\\)|)",
|
||
|
||
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
|
||
rwhitespace = new RegExp( whitespace + "+", "g" ),
|
||
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
|
||
whitespace + "+$", "g" ),
|
||
|
||
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
|
||
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace +
|
||
"*" ),
|
||
rdescend = new RegExp( whitespace + "|>" ),
|
||
|
||
rpseudo = new RegExp( pseudos ),
|
||
ridentifier = new RegExp( "^" + identifier + "$" ),
|
||
|
||
matchExpr = {
|
||
"ID": new RegExp( "^#(" + identifier + ")" ),
|
||
"CLASS": new RegExp( "^\\.(" + identifier + ")" ),
|
||
"TAG": new RegExp( "^(" + identifier + "|[*])" ),
|
||
"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" )
|
||
},
|
||
|
||
rhtml = /HTML$/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 = /[+~]/,
|
||
|
||
// CSS escapes
|
||
// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
|
||
runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ),
|
||
funescape = function( escape, nonHex ) {
|
||
var high = "0x" + escape.slice( 1 ) - 0x10000;
|
||
|
||
return nonHex ?
|
||
|
||
// Strip the backslash prefix from a non-hex escape sequence
|
||
nonHex :
|
||
|
||
// Replace a hexadecimal escape sequence with the encoded Unicode code point
|
||
// Support: IE <=11+
|
||
// For values outside the Basic Multilingual Plane (BMP), manually construct a
|
||
// surrogate pair
|
||
high < 0 ?
|
||
String.fromCharCode( high + 0x10000 ) :
|
||
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
|
||
},
|
||
|
||
// CSS string/identifier serialization
|
||
// https://drafts.csswg.org/cssom/#common-serializing-idioms
|
||
rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
|
||
fcssescape = function( ch, asCodePoint ) {
|
||
if ( asCodePoint ) {
|
||
|
||
// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
|
||
if ( ch === "\0" ) {
|
||
return "\uFFFD";
|
||
}
|
||
|
||
// Control characters and (dependent upon position) numbers get escaped as code points
|
||
return ch.slice( 0, -1 ) + "\\" +
|
||
ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
|
||
}
|
||
|
||
// Other potentially-special ASCII characters get backslash-escaped
|
||
return "\\" + ch;
|
||
},
|
||
|
||
// Used for iframes
|
||
// See setDocument()
|
||
// Removing the function wrapper causes a "Permission Denied"
|
||
// error in IE
|
||
unloadHandler = function() {
|
||
setDocument();
|
||
},
|
||
|
||
inDisabledFieldset = addCombinator(
|
||
function( elem ) {
|
||
return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
|
||
},
|
||
{ dir: "parentNode", next: "legend" }
|
||
);
|
||
|
||
// Optimize for push.apply( _, NodeList )
|
||
try {
|
||
push.apply(
|
||
( arr = slice.call( preferredDoc.childNodes ) ),
|
||
preferredDoc.childNodes
|
||
);
|
||
|
||
// Support: Android<4.0
|
||
// Detect silently failing push.apply
|
||
// eslint-disable-next-line no-unused-expressions
|
||
arr[ preferredDoc.childNodes.length ].nodeType;
|
||
} catch ( e ) {
|
||
push = { apply: arr.length ?
|
||
|
||
// Leverage slice if possible
|
||
function( target, els ) {
|
||
pushNative.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 m, i, elem, nid, match, groups, newSelector,
|
||
newContext = context && context.ownerDocument,
|
||
|
||
// nodeType defaults to 9, since context defaults to document
|
||
nodeType = context ? context.nodeType : 9;
|
||
|
||
results = results || [];
|
||
|
||
// Return early from calls with invalid selector or context
|
||
if ( typeof selector !== "string" || !selector ||
|
||
nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
|
||
|
||
return results;
|
||
}
|
||
|
||
// Try to shortcut find operations (as opposed to filters) in HTML documents
|
||
if ( !seed ) {
|
||
setDocument( context );
|
||
context = context || document;
|
||
|
||
if ( documentIsHTML ) {
|
||
|
||
// If the selector is sufficiently simple, try using a "get*By*" DOM method
|
||
// (excepting DocumentFragment context, where the methods don't exist)
|
||
if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
|
||
|
||
// ID selector
|
||
if ( ( m = match[ 1 ] ) ) {
|
||
|
||
// Document context
|
||
if ( nodeType === 9 ) {
|
||
if ( ( elem = context.getElementById( m ) ) ) {
|
||
|
||
// Support: IE, Opera, Webkit
|
||
// TODO: identify versions
|
||
// getElementById can match elements by name instead of ID
|
||
if ( elem.id === m ) {
|
||
results.push( elem );
|
||
return results;
|
||
}
|
||
} else {
|
||
return results;
|
||
}
|
||
|
||
// Element context
|
||
} else {
|
||
|
||
// Support: IE, Opera, Webkit
|
||
// TODO: identify versions
|
||
// getElementById can match elements by name instead of ID
|
||
if ( newContext && ( elem = newContext.getElementById( m ) ) &&
|
||
contains( context, elem ) &&
|
||
elem.id === m ) {
|
||
|
||
results.push( elem );
|
||
return results;
|
||
}
|
||
}
|
||
|
||
// Type selector
|
||
} else if ( match[ 2 ] ) {
|
||
push.apply( results, context.getElementsByTagName( selector ) );
|
||
return results;
|
||
|
||
// Class selector
|
||
} else if ( ( m = match[ 3 ] ) && support.getElementsByClassName &&
|
||
context.getElementsByClassName ) {
|
||
|
||
push.apply( results, context.getElementsByClassName( m ) );
|
||
return results;
|
||
}
|
||
}
|
||
|
||
// Take advantage of querySelectorAll
|
||
if ( support.qsa &&
|
||
!nonnativeSelectorCache[ selector + " " ] &&
|
||
( !rbuggyQSA || !rbuggyQSA.test( selector ) ) &&
|
||
|
||
// Support: IE 8 only
|
||
// Exclude object elements
|
||
( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) {
|
||
|
||
newSelector = selector;
|
||
newContext = context;
|
||
|
||
// qSA considers elements outside a scoping root when evaluating child or
|
||
// descendant combinators, which is not what we want.
|
||
// In such cases, we work around the behavior by prefixing every selector in the
|
||
// list with an ID selector referencing the scope context.
|
||
// The technique has to be used as well when a leading combinator is used
|
||
// as such selectors are not recognized by querySelectorAll.
|
||
// Thanks to Andrew Dupont for this technique.
|
||
if ( nodeType === 1 &&
|
||
( rdescend.test( selector ) || rcombinators.test( selector ) ) ) {
|
||
|
||
// Expand context for sibling selectors
|
||
newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
|
||
context;
|
||
|
||
// We can use :scope instead of the ID hack if the browser
|
||
// supports it & if we're not changing the context.
|
||
if ( newContext !== context || !support.scope ) {
|
||
|
||
// Capture the context ID, setting it first if necessary
|
||
if ( ( nid = context.getAttribute( "id" ) ) ) {
|
||
nid = nid.replace( rcssescape, fcssescape );
|
||
} else {
|
||
context.setAttribute( "id", ( nid = expando ) );
|
||
}
|
||
}
|
||
|
||
// Prefix every selector in the list
|
||
groups = tokenize( selector );
|
||
i = groups.length;
|
||
while ( i-- ) {
|
||
groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
|
||
toSelector( groups[ i ] );
|
||
}
|
||
newSelector = groups.join( "," );
|
||
}
|
||
|
||
try {
|
||
push.apply( results,
|
||
newContext.querySelectorAll( newSelector )
|
||
);
|
||
return results;
|
||
} catch ( qsaError ) {
|
||
nonnativeSelectorCache( selector, true );
|
||
} finally {
|
||
if ( nid === expando ) {
|
||
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 element and returns a boolean result
|
||
*/
|
||
function assert( fn ) {
|
||
var el = document.createElement( "fieldset" );
|
||
|
||
try {
|
||
return !!fn( el );
|
||
} catch ( e ) {
|
||
return false;
|
||
} finally {
|
||
|
||
// Remove from its parent by default
|
||
if ( el.parentNode ) {
|
||
el.parentNode.removeChild( el );
|
||
}
|
||
|
||
// release memory in IE
|
||
el = 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 = arr.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 &&
|
||
a.sourceIndex - b.sourceIndex;
|
||
|
||
// 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 :enabled/:disabled
|
||
* @param {Boolean} disabled true for :disabled; false for :enabled
|
||
*/
|
||
function createDisabledPseudo( disabled ) {
|
||
|
||
// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
|
||
return function( elem ) {
|
||
|
||
// Only certain elements can match :enabled or :disabled
|
||
// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
|
||
// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
|
||
if ( "form" in elem ) {
|
||
|
||
// Check for inherited disabledness on relevant non-disabled elements:
|
||
// * listed form-associated elements in a disabled fieldset
|
||
// https://html.spec.whatwg.org/multipage/forms.html#category-listed
|
||
// https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
|
||
// * option elements in a disabled optgroup
|
||
// https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
|
||
// All such elements have a "form" property.
|
||
if ( elem.parentNode && elem.disabled === false ) {
|
||
|
||
// Option elements defer to a parent optgroup if present
|
||
if ( "label" in elem ) {
|
||
if ( "label" in elem.parentNode ) {
|
||
return elem.parentNode.disabled === disabled;
|
||
} else {
|
||
return elem.disabled === disabled;
|
||
}
|
||
}
|
||
|
||
// Support: IE 6 - 11
|
||
// Use the isDisabled shortcut property to check for disabled fieldset ancestors
|
||
return elem.isDisabled === disabled ||
|
||
|
||
// Where there is no isDisabled, check manually
|
||
/* jshint -W018 */
|
||
elem.isDisabled !== !disabled &&
|
||
inDisabledFieldset( elem ) === disabled;
|
||
}
|
||
|
||
return elem.disabled === disabled;
|
||
|
||
// Try to winnow out elements that can't be disabled before trusting the disabled property.
|
||
// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
|
||
// even exist on them, let alone have a boolean value.
|
||
} else if ( "label" in elem ) {
|
||
return elem.disabled === disabled;
|
||
}
|
||
|
||
// Remaining elements are neither :enabled nor :disabled
|
||
return false;
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 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 !== "undefined" && 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 ) {
|
||
var namespace = elem.namespaceURI,
|
||
docElem = ( elem.ownerDocument || elem ).documentElement;
|
||
|
||
// Support: IE <=8
|
||
// Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
|
||
// https://bugs.jquery.com/ticket/4833
|
||
return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
|
||
};
|
||
|
||
/**
|
||
* 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, subWindow,
|
||
doc = node ? node.ownerDocument || node : preferredDoc;
|
||
|
||
// Return early if doc is invalid or already selected
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
|
||
return document;
|
||
}
|
||
|
||
// Update global variables
|
||
document = doc;
|
||
docElem = document.documentElement;
|
||
documentIsHTML = !isXML( document );
|
||
|
||
// Support: IE 9 - 11+, Edge 12 - 18+
|
||
// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( preferredDoc != document &&
|
||
( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
|
||
|
||
// Support: IE 11, Edge
|
||
if ( subWindow.addEventListener ) {
|
||
subWindow.addEventListener( "unload", unloadHandler, false );
|
||
|
||
// Support: IE 9 - 10 only
|
||
} else if ( subWindow.attachEvent ) {
|
||
subWindow.attachEvent( "onunload", unloadHandler );
|
||
}
|
||
}
|
||
|
||
// Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only,
|
||
// Safari 4 - 5 only, Opera <=11.6 - 12.x only
|
||
// IE/Edge & older browsers don't support the :scope pseudo-class.
|
||
// Support: Safari 6.0 only
|
||
// Safari 6.0 supports :scope but it's an alias of :root there.
|
||
support.scope = assert( function( el ) {
|
||
docElem.appendChild( el ).appendChild( document.createElement( "div" ) );
|
||
return typeof el.querySelectorAll !== "undefined" &&
|
||
!el.querySelectorAll( ":scope fieldset div" ).length;
|
||
} );
|
||
|
||
/* Attributes
|
||
---------------------------------------------------------------------- */
|
||
|
||
// Support: IE<8
|
||
// Verify that getAttribute really returns attributes and not properties
|
||
// (excepting IE8 booleans)
|
||
support.attributes = assert( function( el ) {
|
||
el.className = "i";
|
||
return !el.getAttribute( "className" );
|
||
} );
|
||
|
||
/* getElement(s)By*
|
||
---------------------------------------------------------------------- */
|
||
|
||
// Check if getElementsByTagName("*") returns only elements
|
||
support.getElementsByTagName = assert( function( el ) {
|
||
el.appendChild( document.createComment( "" ) );
|
||
return !el.getElementsByTagName( "*" ).length;
|
||
} );
|
||
|
||
// Support: IE<9
|
||
support.getElementsByClassName = rnative.test( document.getElementsByClassName );
|
||
|
||
// Support: IE<10
|
||
// Check if getElementById returns elements by name
|
||
// The broken getElementById methods don't pick up programmatically-set names,
|
||
// so use a roundabout getElementsByName test
|
||
support.getById = assert( function( el ) {
|
||
docElem.appendChild( el ).id = expando;
|
||
return !document.getElementsByName || !document.getElementsByName( expando ).length;
|
||
} );
|
||
|
||
// ID filter and find
|
||
if ( support.getById ) {
|
||
Expr.filter[ "ID" ] = function( id ) {
|
||
var attrId = id.replace( runescape, funescape );
|
||
return function( elem ) {
|
||
return elem.getAttribute( "id" ) === attrId;
|
||
};
|
||
};
|
||
Expr.find[ "ID" ] = function( id, context ) {
|
||
if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
|
||
var elem = context.getElementById( id );
|
||
return elem ? [ elem ] : [];
|
||
}
|
||
};
|
||
} else {
|
||
Expr.filter[ "ID" ] = function( id ) {
|
||
var attrId = id.replace( runescape, funescape );
|
||
return function( elem ) {
|
||
var node = typeof elem.getAttributeNode !== "undefined" &&
|
||
elem.getAttributeNode( "id" );
|
||
return node && node.value === attrId;
|
||
};
|
||
};
|
||
|
||
// Support: IE 6 - 7 only
|
||
// getElementById is not reliable as a find shortcut
|
||
Expr.find[ "ID" ] = function( id, context ) {
|
||
if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
|
||
var node, i, elems,
|
||
elem = context.getElementById( id );
|
||
|
||
if ( elem ) {
|
||
|
||
// Verify the id attribute
|
||
node = elem.getAttributeNode( "id" );
|
||
if ( node && node.value === id ) {
|
||
return [ elem ];
|
||
}
|
||
|
||
// Fall back on getElementsByName
|
||
elems = context.getElementsByName( id );
|
||
i = 0;
|
||
while ( ( elem = elems[ i++ ] ) ) {
|
||
node = elem.getAttributeNode( "id" );
|
||
if ( node && node.value === id ) {
|
||
return [ elem ];
|
||
}
|
||
}
|
||
}
|
||
|
||
return [];
|
||
}
|
||
};
|
||
}
|
||
|
||
// Tag
|
||
Expr.find[ "TAG" ] = support.getElementsByTagName ?
|
||
function( tag, context ) {
|
||
if ( typeof context.getElementsByTagName !== "undefined" ) {
|
||
return context.getElementsByTagName( tag );
|
||
|
||
// DocumentFragment nodes don't have gEBTN
|
||
} else if ( support.qsa ) {
|
||
return context.querySelectorAll( tag );
|
||
}
|
||
} :
|
||
|
||
function( tag, context ) {
|
||
var elem,
|
||
tmp = [],
|
||
i = 0,
|
||
|
||
// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
|
||
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 !== "undefined" && 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 https://bugs.jquery.com/ticket/13378
|
||
rbuggyQSA = [];
|
||
|
||
if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) {
|
||
|
||
// Build QSA regex
|
||
// Regex strategy adopted from Diego Perini
|
||
assert( function( el ) {
|
||
|
||
var input;
|
||
|
||
// 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
|
||
// https://bugs.jquery.com/ticket/12359
|
||
docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
|
||
"<select id='" + expando + "-\r\\' msallowcapture=''>" +
|
||
"<option selected=''></option></select>";
|
||
|
||
// Support: IE8, Opera 11-12.16
|
||
// Nothing should be selected when empty strings follow ^= or $= or *=
|
||
// The test attribute must be unknown in Opera but "safe" for WinRT
|
||
// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
|
||
if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) {
|
||
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
|
||
}
|
||
|
||
// Support: IE8
|
||
// Boolean attributes and "value" are not treated correctly
|
||
if ( !el.querySelectorAll( "[selected]" ).length ) {
|
||
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
|
||
}
|
||
|
||
// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
|
||
if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
|
||
rbuggyQSA.push( "~=" );
|
||
}
|
||
|
||
// Support: IE 11+, Edge 15 - 18+
|
||
// IE 11/Edge don't find elements on a `[name='']` query in some cases.
|
||
// Adding a temporary attribute to the document before the selection works
|
||
// around the issue.
|
||
// Interestingly, IE 10 & older don't seem to have the issue.
|
||
input = document.createElement( "input" );
|
||
input.setAttribute( "name", "" );
|
||
el.appendChild( input );
|
||
if ( !el.querySelectorAll( "[name='']" ).length ) {
|
||
rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
|
||
whitespace + "*(?:''|\"\")" );
|
||
}
|
||
|
||
// 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 ( !el.querySelectorAll( ":checked" ).length ) {
|
||
rbuggyQSA.push( ":checked" );
|
||
}
|
||
|
||
// Support: Safari 8+, iOS 8+
|
||
// https://bugs.webkit.org/show_bug.cgi?id=136851
|
||
// In-page `selector#id sibling-combinator selector` fails
|
||
if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
|
||
rbuggyQSA.push( ".#.+[+~]" );
|
||
}
|
||
|
||
// Support: Firefox <=3.6 - 5 only
|
||
// Old Firefox doesn't throw on a badly-escaped identifier.
|
||
el.querySelectorAll( "\\\f" );
|
||
rbuggyQSA.push( "[\\r\\n\\f]" );
|
||
} );
|
||
|
||
assert( function( el ) {
|
||
el.innerHTML = "<a href='' disabled='disabled'></a>" +
|
||
"<select disabled='disabled'><option/></select>";
|
||
|
||
// Support: Windows 8 Native Apps
|
||
// The type and name attributes are restricted during .innerHTML assignment
|
||
var input = document.createElement( "input" );
|
||
input.setAttribute( "type", "hidden" );
|
||
el.appendChild( input ).setAttribute( "name", "D" );
|
||
|
||
// Support: IE8
|
||
// Enforce case-sensitivity of name attribute
|
||
if ( el.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 ( el.querySelectorAll( ":enabled" ).length !== 2 ) {
|
||
rbuggyQSA.push( ":enabled", ":disabled" );
|
||
}
|
||
|
||
// Support: IE9-11+
|
||
// IE's :disabled selector does not pick up the children of disabled fieldsets
|
||
docElem.appendChild( el ).disabled = true;
|
||
if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
|
||
rbuggyQSA.push( ":enabled", ":disabled" );
|
||
}
|
||
|
||
// Support: Opera 10 - 11 only
|
||
// Opera 10-11 does not throw on post-comma invalid pseudos
|
||
el.querySelectorAll( "*,:x" );
|
||
rbuggyQSA.push( ",.*:" );
|
||
} );
|
||
}
|
||
|
||
if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches ||
|
||
docElem.webkitMatchesSelector ||
|
||
docElem.mozMatchesSelector ||
|
||
docElem.oMatchesSelector ||
|
||
docElem.msMatchesSelector ) ) ) ) {
|
||
|
||
assert( function( el ) {
|
||
|
||
// Check to see if it's possible to do matchesSelector
|
||
// on a disconnected node (IE 9)
|
||
support.disconnectedMatch = matches.call( el, "*" );
|
||
|
||
// This should fail with an exception
|
||
// Gecko does not error, returns false instead
|
||
matches.call( el, "[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 self-exclusive
|
||
// 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
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
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
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( a == document || a.ownerDocument == preferredDoc &&
|
||
contains( preferredDoc, a ) ) {
|
||
return -1;
|
||
}
|
||
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( b == document || b.ownerDocument == preferredDoc &&
|
||
contains( preferredDoc, b ) ) {
|
||
return 1;
|
||
}
|
||
|
||
// Maintain original order
|
||
return sortInput ?
|
||
( indexOf( sortInput, a ) - indexOf( 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 ) {
|
||
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
/* eslint-disable eqeqeq */
|
||
return a == document ? -1 :
|
||
b == document ? 1 :
|
||
/* eslint-enable eqeqeq */
|
||
aup ? -1 :
|
||
bup ? 1 :
|
||
sortInput ?
|
||
( indexOf( sortInput, a ) - indexOf( 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
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
/* eslint-disable eqeqeq */
|
||
ap[ i ] == preferredDoc ? -1 :
|
||
bp[ i ] == preferredDoc ? 1 :
|
||
/* eslint-enable eqeqeq */
|
||
0;
|
||
};
|
||
|
||
return document;
|
||
};
|
||
|
||
Sizzle.matches = function( expr, elements ) {
|
||
return Sizzle( expr, null, null, elements );
|
||
};
|
||
|
||
Sizzle.matchesSelector = function( elem, expr ) {
|
||
setDocument( elem );
|
||
|
||
if ( support.matchesSelector && documentIsHTML &&
|
||
!nonnativeSelectorCache[ expr + " " ] &&
|
||
( !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 ) {
|
||
nonnativeSelectorCache( expr, true );
|
||
}
|
||
}
|
||
|
||
return Sizzle( expr, document, null, [ elem ] ).length > 0;
|
||
};
|
||
|
||
Sizzle.contains = function( context, elem ) {
|
||
|
||
// Set document vars if needed
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( ( context.ownerDocument || context ) != document ) {
|
||
setDocument( context );
|
||
}
|
||
return contains( context, elem );
|
||
};
|
||
|
||
Sizzle.attr = function( elem, name ) {
|
||
|
||
// Set document vars if needed
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
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.escape = function( sel ) {
|
||
return ( sel + "" ).replace( rcssescape, fcssescape );
|
||
};
|
||
|
||
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[ 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[ 6 ] && match[ 2 ];
|
||
|
||
if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) {
|
||
return null;
|
||
}
|
||
|
||
// Accept quoted arguments as-is
|
||
if ( match[ 3 ] ) {
|
||
match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
|
||
|
||
// 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 !== "undefined" &&
|
||
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 += "";
|
||
|
||
/* eslint-disable max-len */
|
||
|
||
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.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
|
||
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
|
||
false;
|
||
/* eslint-enable max-len */
|
||
|
||
};
|
||
},
|
||
|
||
"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, uniqueCache, outerCache, node, nodeIndex, start,
|
||
dir = simple !== forward ? "nextSibling" : "previousSibling",
|
||
parent = elem.parentNode,
|
||
name = ofType && elem.nodeName.toLowerCase(),
|
||
useCache = !xml && !ofType,
|
||
diff = false;
|
||
|
||
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
|
||
|
||
// ...in a gzip-friendly way
|
||
node = parent;
|
||
outerCache = node[ expando ] || ( node[ expando ] = {} );
|
||
|
||
// Support: IE <9 only
|
||
// Defend against cloned attroperties (jQuery gh-1709)
|
||
uniqueCache = outerCache[ node.uniqueID ] ||
|
||
( outerCache[ node.uniqueID ] = {} );
|
||
|
||
cache = uniqueCache[ type ] || [];
|
||
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
|
||
diff = nodeIndex && 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 ) {
|
||
uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
|
||
break;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
// Use previously-cached element index if available
|
||
if ( useCache ) {
|
||
|
||
// ...in a gzip-friendly way
|
||
node = elem;
|
||
outerCache = node[ expando ] || ( node[ expando ] = {} );
|
||
|
||
// Support: IE <9 only
|
||
// Defend against cloned attroperties (jQuery gh-1709)
|
||
uniqueCache = outerCache[ node.uniqueID ] ||
|
||
( outerCache[ node.uniqueID ] = {} );
|
||
|
||
cache = uniqueCache[ type ] || [];
|
||
nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
|
||
diff = nodeIndex;
|
||
}
|
||
|
||
// xml :nth-child(...)
|
||
// or :nth-last-child(...) or :nth(-last)?-of-type(...)
|
||
if ( diff === false ) {
|
||
|
||
// 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 ) {
|
||
outerCache = node[ expando ] ||
|
||
( node[ expando ] = {} );
|
||
|
||
// Support: IE <9 only
|
||
// Defend against cloned attroperties (jQuery gh-1709)
|
||
uniqueCache = outerCache[ node.uniqueID ] ||
|
||
( outerCache[ node.uniqueID ] = {} );
|
||
|
||
uniqueCache[ 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( 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 );
|
||
|
||
// Don't keep the element (issue #299)
|
||
input[ 0 ] = null;
|
||
return !results.pop();
|
||
};
|
||
} ),
|
||
|
||
"has": markFunction( function( selector ) {
|
||
return function( elem ) {
|
||
return Sizzle( selector, elem ).length > 0;
|
||
};
|
||
} ),
|
||
|
||
"contains": markFunction( function( text ) {
|
||
text = text.replace( runescape, funescape );
|
||
return function( elem ) {
|
||
return ( elem.textContent || 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": createDisabledPseudo( false ),
|
||
"disabled": createDisabledPseudo( 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 ) {
|
||
// eslint-disable-next-line no-unused-expressions
|
||
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 > length ?
|
||
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();
|
||
|
||
tokenize = Sizzle.tokenize = function( 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,
|
||
skip = combinator.next,
|
||
key = skip || dir,
|
||
checkNonElements = base && key === "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 );
|
||
}
|
||
}
|
||
return false;
|
||
} :
|
||
|
||
// Check against all ancestor/preceding elements
|
||
function( elem, context, xml ) {
|
||
var oldCache, uniqueCache, outerCache,
|
||
newCache = [ dirruns, doneName ];
|
||
|
||
// We can't set arbitrary data on XML nodes, so they don't benefit from combinator 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 ] = {} );
|
||
|
||
// Support: IE <9 only
|
||
// Defend against cloned attroperties (jQuery gh-1709)
|
||
uniqueCache = outerCache[ elem.uniqueID ] ||
|
||
( outerCache[ elem.uniqueID ] = {} );
|
||
|
||
if ( skip && skip === elem.nodeName.toLowerCase() ) {
|
||
elem = elem[ dir ] || elem;
|
||
} else if ( ( oldCache = uniqueCache[ key ] ) &&
|
||
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
|
||
uniqueCache[ key ] = newCache;
|
||
|
||
// A match means we're done; a fail means we have to keep checking
|
||
if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
}
|
||
|
||
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( 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( checkContext, elem ) > -1;
|
||
}, implicitRelative, true ),
|
||
matchers = [ function( elem, context, xml ) {
|
||
var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
|
||
( checkContext = context ).nodeType ?
|
||
matchContext( elem, context, xml ) :
|
||
matchAnyContext( elem, context, xml ) );
|
||
|
||
// Avoid hanging onto element (issue #299)
|
||
checkContext = null;
|
||
return ret;
|
||
} ];
|
||
|
||
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 ) {
|
||
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
outermostContext = context == document || context || outermost;
|
||
}
|
||
|
||
// Add elements passing elementMatchers directly to results
|
||
// 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;
|
||
|
||
// Support: IE 11+, Edge 17 - 18+
|
||
// IE/Edge sometimes throw a "Permission denied" error when strict-comparing
|
||
// two documents; shallow comparisons work.
|
||
// eslint-disable-next-line eqeqeq
|
||
if ( !context && elem.ownerDocument != document ) {
|
||
setDocument( elem );
|
||
xml = !documentIsHTML;
|
||
}
|
||
while ( ( matcher = elementMatchers[ j++ ] ) ) {
|
||
if ( matcher( elem, context || document, 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 );
|
||
}
|
||
}
|
||
}
|
||
|
||
// `i` is now the count of elements visited above, and adding it to `matchedCount`
|
||
// makes the latter nonnegative.
|
||
matchedCount += i;
|
||
|
||
// Apply set filters to unmatched elements
|
||
// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
|
||
// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
|
||
// no element matchers and no seed.
|
||
// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
|
||
// case, which will result in a "00" `matchedCount` that differs from `i` but is also
|
||
// numerically zero.
|
||
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 only one selector in the list and no seed
|
||
// (the latter of which guarantees us context)
|
||
if ( match.length === 1 ) {
|
||
|
||
// Reduce context if the leading compound selector is an ID
|
||
tokens = match[ 0 ] = match[ 0 ].slice( 0 );
|
||
if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
|
||
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,
|
||
!context || 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-35+
|
||
// 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( el ) {
|
||
|
||
// Should return 1, but returns 4 (following)
|
||
return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
|
||
} );
|
||
|
||
// Support: IE<8
|
||
// Prevent attribute/property "interpolation"
|
||
// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
|
||
if ( !assert( function( el ) {
|
||
el.innerHTML = "<a href='#'></a>";
|
||
return el.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( el ) {
|
||
el.innerHTML = "<input/>";
|
||
el.firstChild.setAttribute( "value", "" );
|
||
return el.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( el ) {
|
||
return el.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;
|
||
|
||
// Deprecated
|
||
jQuery.expr[ ":" ] = jQuery.expr.pseudos;
|
||
jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
|
||
jQuery.text = Sizzle.getText;
|
||
jQuery.isXMLDoc = Sizzle.isXML;
|
||
jQuery.contains = Sizzle.contains;
|
||
jQuery.escapeSelector = Sizzle.escape;
|
||
|
||
|
||
|
||
|
||
var dir = function( elem, dir, until ) {
|
||
var matched = [],
|
||
truncate = until !== undefined;
|
||
|
||
while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
|
||
if ( elem.nodeType === 1 ) {
|
||
if ( truncate && jQuery( elem ).is( until ) ) {
|
||
break;
|
||
}
|
||
matched.push( elem );
|
||
}
|
||
}
|
||
return matched;
|
||
};
|
||
|
||
|
||
var siblings = function( n, elem ) {
|
||
var matched = [];
|
||
|
||
for ( ; n; n = n.nextSibling ) {
|
||
if ( n.nodeType === 1 && n !== elem ) {
|
||
matched.push( n );
|
||
}
|
||
}
|
||
|
||
return matched;
|
||
};
|
||
|
||
|
||
var rneedsContext = jQuery.expr.match.needsContext;
|
||
|
||
|
||
|
||
function nodeName( elem, name ) {
|
||
|
||
return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
|
||
|
||
};
|
||
var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
|
||
|
||
|
||
|
||
// Implement the identical functionality for filter and not
|
||
function winnow( elements, qualifier, not ) {
|
||
if ( isFunction( qualifier ) ) {
|
||
return jQuery.grep( elements, function( elem, i ) {
|
||
return !!qualifier.call( elem, i, elem ) !== not;
|
||
} );
|
||
}
|
||
|
||
// Single element
|
||
if ( qualifier.nodeType ) {
|
||
return jQuery.grep( elements, function( elem ) {
|
||
return ( elem === qualifier ) !== not;
|
||
} );
|
||
}
|
||
|
||
// Arraylike of elements (jQuery, arguments, Array)
|
||
if ( typeof qualifier !== "string" ) {
|
||
return jQuery.grep( elements, function( elem ) {
|
||
return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
|
||
} );
|
||
}
|
||
|
||
// Filtered directly for both simple and complex selectors
|
||
return jQuery.filter( qualifier, elements, not );
|
||
}
|
||
|
||
jQuery.filter = function( expr, elems, not ) {
|
||
var elem = elems[ 0 ];
|
||
|
||
if ( not ) {
|
||
expr = ":not(" + expr + ")";
|
||
}
|
||
|
||
if ( elems.length === 1 && elem.nodeType === 1 ) {
|
||
return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
|
||
}
|
||
|
||
return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
|
||
return elem.nodeType === 1;
|
||
} ) );
|
||
};
|
||
|
||
jQuery.fn.extend( {
|
||
find: function( selector ) {
|
||
var i, ret,
|
||
len = this.length,
|
||
self = this;
|
||
|
||
if ( typeof selector !== "string" ) {
|
||
return this.pushStack( jQuery( selector ).filter( function() {
|
||
for ( i = 0; i < len; i++ ) {
|
||
if ( jQuery.contains( self[ i ], this ) ) {
|
||
return true;
|
||
}
|
||
}
|
||
} ) );
|
||
}
|
||
|
||
ret = this.pushStack( [] );
|
||
|
||
for ( i = 0; i < len; i++ ) {
|
||
jQuery.find( selector, self[ i ], ret );
|
||
}
|
||
|
||
return len > 1 ? jQuery.uniqueSort( ret ) : ret;
|
||
},
|
||
filter: function( selector ) {
|
||
return this.pushStack( winnow( this, selector || [], false ) );
|
||
},
|
||
not: function( selector ) {
|
||
return this.pushStack( winnow( this, selector || [], true ) );
|
||
},
|
||
is: function( selector ) {
|
||
return !!winnow(
|
||
this,
|
||
|
||
// If this is a positional/relative selector, check membership in the returned set
|
||
// so $("p:first").is("p:last") won't return true for a doc with two "p".
|
||
typeof selector === "string" && rneedsContext.test( selector ) ?
|
||
jQuery( selector ) :
|
||
selector || [],
|
||
false
|
||
).length;
|
||
}
|
||
} );
|
||
|
||
|
||
// Initialize a jQuery object
|
||
|
||
|
||
// A central reference to the root jQuery(document)
|
||
var rootjQuery,
|
||
|
||
// A simple way to check for HTML strings
|
||
// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
|
||
// Strict HTML recognition (#11290: must start with <)
|
||
// Shortcut simple #id case for speed
|
||
rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
|
||
|
||
init = jQuery.fn.init = function( selector, context, root ) {
|
||
var match, elem;
|
||
|
||
// HANDLE: $(""), $(null), $(undefined), $(false)
|
||
if ( !selector ) {
|
||
return this;
|
||
}
|
||
|
||
// Method init() accepts an alternate rootjQuery
|
||
// so migrate can support jQuery.sub (gh-2101)
|
||
root = root || rootjQuery;
|
||
|
||
// Handle HTML strings
|
||
if ( typeof selector === "string" ) {
|
||
if ( selector[ 0 ] === "<" &&
|
||
selector[ selector.length - 1 ] === ">" &&
|
||
selector.length >= 3 ) {
|
||
|
||
// Assume that strings that start and end with <> are HTML and skip the regex check
|
||
match = [ null, selector, null ];
|
||
|
||
} else {
|
||
match = rquickExpr.exec( selector );
|
||
}
|
||
|
||
// Match html or make sure no context is specified for #id
|
||
if ( match && ( match[ 1 ] || !context ) ) {
|
||
|
||
// HANDLE: $(html) -> $(array)
|
||
if ( match[ 1 ] ) {
|
||
context = context instanceof jQuery ? context[ 0 ] : context;
|
||
|
||
// Option to run 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 ( 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 ] );
|
||
|
||
if ( elem ) {
|
||
|
||
// Inject the element directly into the jQuery object
|
||
this[ 0 ] = elem;
|
||
this.length = 1;
|
||
}
|
||
return this;
|
||
}
|
||
|
||
// HANDLE: $(expr, $(...))
|
||
} else if ( !context || context.jquery ) {
|
||
return ( context || root ).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[ 0 ] = selector;
|
||
this.length = 1;
|
||
return this;
|
||
|
||
// HANDLE: $(function)
|
||
// Shortcut for document ready
|
||
} else if ( isFunction( selector ) ) {
|
||
return root.ready !== undefined ?
|
||
root.ready( selector ) :
|
||
|
||
// Execute immediately if ready is not present
|
||
selector( jQuery );
|
||
}
|
||
|
||
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.fn.extend( {
|
||
has: function( target ) {
|
||
var targets = jQuery( target, this ),
|
||
l = targets.length;
|
||
|
||
return this.filter( function() {
|
||
var i = 0;
|
||
for ( ; i < l; i++ ) {
|
||
if ( jQuery.contains( this, targets[ i ] ) ) {
|
||
return true;
|
||
}
|
||
}
|
||
} );
|
||
},
|
||
|
||
closest: function( selectors, context ) {
|
||
var cur,
|
||
i = 0,
|
||
l = this.length,
|
||
matched = [],
|
||
targets = typeof selectors !== "string" && jQuery( selectors );
|
||
|
||
// Positional selectors never match, since there's no _selection_ context
|
||
if ( !rneedsContext.test( selectors ) ) {
|
||
for ( ; i < l; i++ ) {
|
||
for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
|
||
|
||
// Always skip document fragments
|
||
if ( cur.nodeType < 11 && ( targets ?
|
||
targets.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.uniqueSort( matched ) : matched );
|
||
},
|
||
|
||
// Determine the position of an element within the set
|
||
index: function( elem ) {
|
||
|
||
// No argument, return index in parent
|
||
if ( !elem ) {
|
||
return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
|
||
}
|
||
|
||
// Index in selector
|
||
if ( typeof elem === "string" ) {
|
||
return indexOf.call( jQuery( elem ), this[ 0 ] );
|
||
}
|
||
|
||
// Locate the position of the desired element
|
||
return indexOf.call( this,
|
||
|
||
// If it receives a jQuery object, the first element is used
|
||
elem.jquery ? elem[ 0 ] : elem
|
||
);
|
||
},
|
||
|
||
add: function( selector, context ) {
|
||
return this.pushStack(
|
||
jQuery.uniqueSort(
|
||
jQuery.merge( this.get(), jQuery( selector, context ) )
|
||
)
|
||
);
|
||
},
|
||
|
||
addBack: function( selector ) {
|
||
return this.add( selector == null ?
|
||
this.prevObject : this.prevObject.filter( selector )
|
||
);
|
||
}
|
||
} );
|
||
|
||
function sibling( cur, dir ) {
|
||
while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
|
||
return cur;
|
||
}
|
||
|
||
jQuery.each( {
|
||
parent: function( elem ) {
|
||
var parent = elem.parentNode;
|
||
return parent && parent.nodeType !== 11 ? parent : null;
|
||
},
|
||
parents: function( elem ) {
|
||
return dir( elem, "parentNode" );
|
||
},
|
||
parentsUntil: function( elem, _i, until ) {
|
||
return dir( elem, "parentNode", until );
|
||
},
|
||
next: function( elem ) {
|
||
return sibling( elem, "nextSibling" );
|
||
},
|
||
prev: function( elem ) {
|
||
return sibling( elem, "previousSibling" );
|
||
},
|
||
nextAll: function( elem ) {
|
||
return dir( elem, "nextSibling" );
|
||
},
|
||
prevAll: function( elem ) {
|
||
return dir( elem, "previousSibling" );
|
||
},
|
||
nextUntil: function( elem, _i, until ) {
|
||
return dir( elem, "nextSibling", until );
|
||
},
|
||
prevUntil: function( elem, _i, until ) {
|
||
return dir( elem, "previousSibling", until );
|
||
},
|
||
siblings: function( elem ) {
|
||
return siblings( ( elem.parentNode || {} ).firstChild, elem );
|
||
},
|
||
children: function( elem ) {
|
||
return siblings( elem.firstChild );
|
||
},
|
||
contents: function( elem ) {
|
||
if ( elem.contentDocument != null &&
|
||
|
||
// Support: IE 11+
|
||
// <object> elements with no `data` attribute has an object
|
||
// `contentDocument` with a `null` prototype.
|
||
getProto( elem.contentDocument ) ) {
|
||
|
||
return elem.contentDocument;
|
||
}
|
||
|
||
// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
|
||
// Treat the template element as a regular one in browsers that
|
||
// don't support it.
|
||
if ( nodeName( elem, "template" ) ) {
|
||
elem = elem.content || elem;
|
||
}
|
||
|
||
return jQuery.merge( [], elem.childNodes );
|
||
}
|
||
}, function( name, fn ) {
|
||
jQuery.fn[ name ] = function( until, selector ) {
|
||
var matched = jQuery.map( this, fn, until );
|
||
|
||
if ( name.slice( -5 ) !== "Until" ) {
|
||
selector = until;
|
||
}
|
||
|
||
if ( selector && typeof selector === "string" ) {
|
||
matched = jQuery.filter( selector, matched );
|
||
}
|
||
|
||
if ( this.length > 1 ) {
|
||
|
||
// Remove duplicates
|
||
if ( !guaranteedUnique[ name ] ) {
|
||
jQuery.uniqueSort( matched );
|
||
}
|
||
|
||
// Reverse order for parents* and prev-derivatives
|
||
if ( rparentsprev.test( name ) ) {
|
||
matched.reverse();
|
||
}
|
||
}
|
||
|
||
return this.pushStack( matched );
|
||
};
|
||
} );
|
||
var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
|
||
|
||
|
||
|
||
// Convert String-formatted options into Object-formatted ones
|
||
function createOptions( options ) {
|
||
var object = {};
|
||
jQuery.each( options.match( rnothtmlwhite ) || [], 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" ?
|
||
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,
|
||
|
||
// Flag to prevent firing
|
||
locked,
|
||
|
||
// Actual callback list
|
||
list = [],
|
||
|
||
// Queue of execution data for repeatable lists
|
||
queue = [],
|
||
|
||
// Index of currently firing callback (modified by add/remove as needed)
|
||
firingIndex = -1,
|
||
|
||
// Fire callbacks
|
||
fire = function() {
|
||
|
||
// Enforce single-firing
|
||
locked = locked || options.once;
|
||
|
||
// Execute callbacks for all pending executions,
|
||
// respecting firingIndex overrides and runtime changes
|
||
fired = firing = true;
|
||
for ( ; queue.length; firingIndex = -1 ) {
|
||
memory = queue.shift();
|
||
while ( ++firingIndex < list.length ) {
|
||
|
||
// Run callback and check for early termination
|
||
if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
|
||
options.stopOnFalse ) {
|
||
|
||
// Jump to end and forget the data so .add doesn't re-fire
|
||
firingIndex = list.length;
|
||
memory = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Forget the data if we're done with it
|
||
if ( !options.memory ) {
|
||
memory = false;
|
||
}
|
||
|
||
firing = false;
|
||
|
||
// Clean up if we're done firing for good
|
||
if ( locked ) {
|
||
|
||
// Keep an empty list if we have data for future add calls
|
||
if ( memory ) {
|
||
list = [];
|
||
|
||
// Otherwise, this object is spent
|
||
} else {
|
||
list = "";
|
||
}
|
||
}
|
||
},
|
||
|
||
// Actual Callbacks object
|
||
self = {
|
||
|
||
// Add a callback or a collection of callbacks to the list
|
||
add: function() {
|
||
if ( list ) {
|
||
|
||
// If we have memory from a past run, we should fire after adding
|
||
if ( memory && !firing ) {
|
||
firingIndex = list.length - 1;
|
||
queue.push( memory );
|
||
}
|
||
|
||
( function add( args ) {
|
||
jQuery.each( args, function( _, arg ) {
|
||
if ( isFunction( arg ) ) {
|
||
if ( !options.unique || !self.has( arg ) ) {
|
||
list.push( arg );
|
||
}
|
||
} else if ( arg && arg.length && toType( arg ) !== "string" ) {
|
||
|
||
// Inspect recursively
|
||
add( arg );
|
||
}
|
||
} );
|
||
} )( arguments );
|
||
|
||
if ( memory && !firing ) {
|
||
fire();
|
||
}
|
||
}
|
||
return this;
|
||
},
|
||
|
||
// Remove a callback from the list
|
||
remove: function() {
|
||
jQuery.each( arguments, function( _, arg ) {
|
||
var index;
|
||
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
|
||
list.splice( index, 1 );
|
||
|
||
// Handle firing indexes
|
||
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.length > 0;
|
||
},
|
||
|
||
// Remove all callbacks from the list
|
||
empty: function() {
|
||
if ( list ) {
|
||
list = [];
|
||
}
|
||
return this;
|
||
},
|
||
|
||
// Disable .fire and .add
|
||
// Abort any current/pending executions
|
||
// Clear all callbacks and values
|
||
disable: function() {
|
||
locked = queue = [];
|
||
list = memory = "";
|
||
return this;
|
||
},
|
||
disabled: function() {
|
||
return !list;
|
||
},
|
||
|
||
// Disable .fire
|
||
// Also disable .add unless we have memory (since it would have no effect)
|
||
// Abort any pending executions
|
||
lock: function() {
|
||
locked = queue = [];
|
||
if ( !memory && !firing ) {
|
||
list = memory = "";
|
||
}
|
||
return this;
|
||
},
|
||
locked: function() {
|
||
return !!locked;
|
||
},
|
||
|
||
// Call all callbacks with the given context and arguments
|
||
fireWith: function( context, args ) {
|
||
if ( !locked ) {
|
||
args = args || [];
|
||
args = [ context, args.slice ? args.slice() : args ];
|
||
queue.push( args );
|
||
if ( !firing ) {
|
||
fire();
|
||
}
|
||
}
|
||
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;
|
||
};
|
||
|
||
|
||
function Identity( v ) {
|
||
return v;
|
||
}
|
||
function Thrower( ex ) {
|
||
throw ex;
|
||
}
|
||
|
||
function adoptValue( value, resolve, reject, noValue ) {
|
||
var method;
|
||
|
||
try {
|
||
|
||
// Check for promise aspect first to privilege synchronous behavior
|
||
if ( value && isFunction( ( method = value.promise ) ) ) {
|
||
method.call( value ).done( resolve ).fail( reject );
|
||
|
||
// Other thenables
|
||
} else if ( value && isFunction( ( method = value.then ) ) ) {
|
||
method.call( value, resolve, reject );
|
||
|
||
// Other non-thenables
|
||
} else {
|
||
|
||
// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
|
||
// * false: [ value ].slice( 0 ) => resolve( value )
|
||
// * true: [ value ].slice( 1 ) => resolve()
|
||
resolve.apply( undefined, [ value ].slice( noValue ) );
|
||
}
|
||
|
||
// For Promises/A+, convert exceptions into rejections
|
||
// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
|
||
// Deferred#then to conditionally suppress rejection.
|
||
} catch ( value ) {
|
||
|
||
// Support: Android 4.0 only
|
||
// Strict mode functions invoked without .call/.apply get global-object context
|
||
reject.apply( undefined, [ value ] );
|
||
}
|
||
}
|
||
|
||
jQuery.extend( {
|
||
|
||
Deferred: function( func ) {
|
||
var tuples = [
|
||
|
||
// action, add listener, callbacks,
|
||
// ... .then handlers, argument index, [final state]
|
||
[ "notify", "progress", jQuery.Callbacks( "memory" ),
|
||
jQuery.Callbacks( "memory" ), 2 ],
|
||
[ "resolve", "done", jQuery.Callbacks( "once memory" ),
|
||
jQuery.Callbacks( "once memory" ), 0, "resolved" ],
|
||
[ "reject", "fail", jQuery.Callbacks( "once memory" ),
|
||
jQuery.Callbacks( "once memory" ), 1, "rejected" ]
|
||
],
|
||
state = "pending",
|
||
promise = {
|
||
state: function() {
|
||
return state;
|
||
},
|
||
always: function() {
|
||
deferred.done( arguments ).fail( arguments );
|
||
return this;
|
||
},
|
||
"catch": function( fn ) {
|
||
return promise.then( null, fn );
|
||
},
|
||
|
||
// Keep pipe for back-compat
|
||
pipe: function( /* fnDone, fnFail, fnProgress */ ) {
|
||
var fns = arguments;
|
||
|
||
return jQuery.Deferred( function( newDefer ) {
|
||
jQuery.each( tuples, function( _i, tuple ) {
|
||
|
||
// Map tuples (progress, done, fail) to arguments (done, fail, progress)
|
||
var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
|
||
|
||
// deferred.progress(function() { bind to newDefer or newDefer.notify })
|
||
// deferred.done(function() { bind to newDefer or newDefer.resolve })
|
||
// deferred.fail(function() { bind to newDefer or newDefer.reject })
|
||
deferred[ tuple[ 1 ] ]( function() {
|
||
var returned = fn && fn.apply( this, arguments );
|
||
if ( returned && isFunction( returned.promise ) ) {
|
||
returned.promise()
|
||
.progress( newDefer.notify )
|
||
.done( newDefer.resolve )
|
||
.fail( newDefer.reject );
|
||
} else {
|
||
newDefer[ tuple[ 0 ] + "With" ](
|
||
this,
|
||
fn ? [ returned ] : arguments
|
||
);
|
||
}
|
||
} );
|
||
} );
|
||
fns = null;
|
||
} ).promise();
|
||
},
|
||
then: function( onFulfilled, onRejected, onProgress ) {
|
||
var maxDepth = 0;
|
||
function resolve( depth, deferred, handler, special ) {
|
||
return function() {
|
||
var that = this,
|
||
args = arguments,
|
||
mightThrow = function() {
|
||
var returned, then;
|
||
|
||
// Support: Promises/A+ section 2.3.3.3.3
|
||
// https://promisesaplus.com/#point-59
|
||
// Ignore double-resolution attempts
|
||
if ( depth < maxDepth ) {
|
||
return;
|
||
}
|
||
|
||
returned = handler.apply( that, args );
|
||
|
||
// Support: Promises/A+ section 2.3.1
|
||
// https://promisesaplus.com/#point-48
|
||
if ( returned === deferred.promise() ) {
|
||
throw new TypeError( "Thenable self-resolution" );
|
||
}
|
||
|
||
// Support: Promises/A+ sections 2.3.3.1, 3.5
|
||
// https://promisesaplus.com/#point-54
|
||
// https://promisesaplus.com/#point-75
|
||
// Retrieve `then` only once
|
||
then = returned &&
|
||
|
||
// Support: Promises/A+ section 2.3.4
|
||
// https://promisesaplus.com/#point-64
|
||
// Only check objects and functions for thenability
|
||
( typeof returned === "object" ||
|
||
typeof returned === "function" ) &&
|
||
returned.then;
|
||
|
||
// Handle a returned thenable
|
||
if ( isFunction( then ) ) {
|
||
|
||
// Special processors (notify) just wait for resolution
|
||
if ( special ) {
|
||
then.call(
|
||
returned,
|
||
resolve( maxDepth, deferred, Identity, special ),
|
||
resolve( maxDepth, deferred, Thrower, special )
|
||
);
|
||
|
||
// Normal processors (resolve) also hook into progress
|
||
} else {
|
||
|
||
// ...and disregard older resolution values
|
||
maxDepth++;
|
||
|
||
then.call(
|
||
returned,
|
||
resolve( maxDepth, deferred, Identity, special ),
|
||
resolve( maxDepth, deferred, Thrower, special ),
|
||
resolve( maxDepth, deferred, Identity,
|
||
deferred.notifyWith )
|
||
);
|
||
}
|
||
|
||
// Handle all other returned values
|
||
} else {
|
||
|
||
// Only substitute handlers pass on context
|
||
// and multiple values (non-spec behavior)
|
||
if ( handler !== Identity ) {
|
||
that = undefined;
|
||
args = [ returned ];
|
||
}
|
||
|
||
// Process the value(s)
|
||
// Default process is resolve
|
||
( special || deferred.resolveWith )( that, args );
|
||
}
|
||
},
|
||
|
||
// Only normal processors (resolve) catch and reject exceptions
|
||
process = special ?
|
||
mightThrow :
|
||
function() {
|
||
try {
|
||
mightThrow();
|
||
} catch ( e ) {
|
||
|
||
if ( jQuery.Deferred.exceptionHook ) {
|
||
jQuery.Deferred.exceptionHook( e,
|
||
process.stackTrace );
|
||
}
|
||
|
||
// Support: Promises/A+ section 2.3.3.3.4.1
|
||
// https://promisesaplus.com/#point-61
|
||
// Ignore post-resolution exceptions
|
||
if ( depth + 1 >= maxDepth ) {
|
||
|
||
// Only substitute handlers pass on context
|
||
// and multiple values (non-spec behavior)
|
||
if ( handler !== Thrower ) {
|
||
that = undefined;
|
||
args = [ e ];
|
||
}
|
||
|
||
deferred.rejectWith( that, args );
|
||
}
|
||
}
|
||
};
|
||
|
||
// Support: Promises/A+ section 2.3.3.3.1
|
||
// https://promisesaplus.com/#point-57
|
||
// Re-resolve promises immediately to dodge false rejection from
|
||
// subsequent errors
|
||
if ( depth ) {
|
||
process();
|
||
} else {
|
||
|
||
// Call an optional hook to record the stack, in case of exception
|
||
// since it's otherwise lost when execution goes async
|
||
if ( jQuery.Deferred.getStackHook ) {
|
||
process.stackTrace = jQuery.Deferred.getStackHook();
|
||
}
|
||
window.setTimeout( process );
|
||
}
|
||
};
|
||
}
|
||
|
||
return jQuery.Deferred( function( newDefer ) {
|
||
|
||
// progress_handlers.add( ... )
|
||
tuples[ 0 ][ 3 ].add(
|
||
resolve(
|
||
0,
|
||
newDefer,
|
||
isFunction( onProgress ) ?
|
||
onProgress :
|
||
Identity,
|
||
newDefer.notifyWith
|
||
)
|
||
);
|
||
|
||
// fulfilled_handlers.add( ... )
|
||
tuples[ 1 ][ 3 ].add(
|
||
resolve(
|
||
0,
|
||
newDefer,
|
||
isFunction( onFulfilled ) ?
|
||
onFulfilled :
|
||
Identity
|
||
)
|
||
);
|
||
|
||
// rejected_handlers.add( ... )
|
||
tuples[ 2 ][ 3 ].add(
|
||
resolve(
|
||
0,
|
||
newDefer,
|
||
isFunction( onRejected ) ?
|
||
onRejected :
|
||
Thrower
|
||
)
|
||
);
|
||
} ).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 = {};
|
||
|
||
// Add list-specific methods
|
||
jQuery.each( tuples, function( i, tuple ) {
|
||
var list = tuple[ 2 ],
|
||
stateString = tuple[ 5 ];
|
||
|
||
// promise.progress = list.add
|
||
// promise.done = list.add
|
||
// promise.fail = list.add
|
||
promise[ tuple[ 1 ] ] = list.add;
|
||
|
||
// Handle state
|
||
if ( stateString ) {
|
||
list.add(
|
||
function() {
|
||
|
||
// state = "resolved" (i.e., fulfilled)
|
||
// state = "rejected"
|
||
state = stateString;
|
||
},
|
||
|
||
// rejected_callbacks.disable
|
||
// fulfilled_callbacks.disable
|
||
tuples[ 3 - i ][ 2 ].disable,
|
||
|
||
// rejected_handlers.disable
|
||
// fulfilled_handlers.disable
|
||
tuples[ 3 - i ][ 3 ].disable,
|
||
|
||
// progress_callbacks.lock
|
||
tuples[ 0 ][ 2 ].lock,
|
||
|
||
// progress_handlers.lock
|
||
tuples[ 0 ][ 3 ].lock
|
||
);
|
||
}
|
||
|
||
// progress_handlers.fire
|
||
// fulfilled_handlers.fire
|
||
// rejected_handlers.fire
|
||
list.add( tuple[ 3 ].fire );
|
||
|
||
// deferred.notify = function() { deferred.notifyWith(...) }
|
||
// deferred.resolve = function() { deferred.resolveWith(...) }
|
||
// deferred.reject = function() { deferred.rejectWith(...) }
|
||
deferred[ tuple[ 0 ] ] = function() {
|
||
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
|
||
return this;
|
||
};
|
||
|
||
// deferred.notifyWith = list.fireWith
|
||
// deferred.resolveWith = list.fireWith
|
||
// deferred.rejectWith = list.fireWith
|
||
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( singleValue ) {
|
||
var
|
||
|
||
// count of uncompleted subordinates
|
||
remaining = arguments.length,
|
||
|
||
// count of unprocessed arguments
|
||
i = remaining,
|
||
|
||
// subordinate fulfillment data
|
||
resolveContexts = Array( i ),
|
||
resolveValues = slice.call( arguments ),
|
||
|
||
// the master Deferred
|
||
master = jQuery.Deferred(),
|
||
|
||
// subordinate callback factory
|
||
updateFunc = function( i ) {
|
||
return function( value ) {
|
||
resolveContexts[ i ] = this;
|
||
resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
|
||
if ( !( --remaining ) ) {
|
||
master.resolveWith( resolveContexts, resolveValues );
|
||
}
|
||
};
|
||
};
|
||
|
||
// Single- and empty arguments are adopted like Promise.resolve
|
||
if ( remaining <= 1 ) {
|
||
adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
|
||
!remaining );
|
||
|
||
// Use .then() to unwrap secondary thenables (cf. gh-3000)
|
||
if ( master.state() === "pending" ||
|
||
isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
|
||
|
||
return master.then();
|
||
}
|
||
}
|
||
|
||
// Multiple arguments are aggregated like Promise.all array elements
|
||
while ( i-- ) {
|
||
adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
|
||
}
|
||
|
||
return master.promise();
|
||
}
|
||
} );
|
||
|
||
|
||
// These usually indicate a programmer mistake during development,
|
||
// warn about them ASAP rather than swallowing them by default.
|
||
var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
|
||
|
||
jQuery.Deferred.exceptionHook = function( error, stack ) {
|
||
|
||
// Support: IE 8 - 9 only
|
||
// Console exists when dev tools are open, which can happen at any time
|
||
if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
|
||
window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
|
||
}
|
||
};
|
||
|
||
|
||
|
||
|
||
jQuery.readyException = function( error ) {
|
||
window.setTimeout( function() {
|
||
throw error;
|
||
} );
|
||
};
|
||
|
||
|
||
|
||
|
||
// The deferred used on DOM ready
|
||
var readyList = jQuery.Deferred();
|
||
|
||
jQuery.fn.ready = function( fn ) {
|
||
|
||
readyList
|
||
.then( fn )
|
||
|
||
// Wrap jQuery.readyException in a function so that the lookup
|
||
// happens at the time of error handling instead of callback
|
||
// registration.
|
||
.catch( function( error ) {
|
||
jQuery.readyException( error );
|
||
} );
|
||
|
||
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,
|
||
|
||
// Handle when the DOM is ready
|
||
ready: function( wait ) {
|
||
|
||
// Abort if there are pending holds or we're already ready
|
||
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
|
||
return;
|
||
}
|
||
|
||
// Remember that the DOM is ready
|
||
jQuery.isReady = true;
|
||
|
||
// If a normal DOM Ready event fired, decrement, and wait if need be
|
||
if ( wait !== true && --jQuery.readyWait > 0 ) {
|
||
return;
|
||
}
|
||
|
||
// If there are functions bound, to execute
|
||
readyList.resolveWith( document, [ jQuery ] );
|
||
}
|
||
} );
|
||
|
||
jQuery.ready.then = readyList.then;
|
||
|
||
// The ready event handler and self cleanup method
|
||
function completed() {
|
||
document.removeEventListener( "DOMContentLoaded", completed );
|
||
window.removeEventListener( "load", completed );
|
||
jQuery.ready();
|
||
}
|
||
|
||
// Catch cases where $(document).ready() is called
|
||
// after the browser event has already occurred.
|
||
// Support: IE <=9 - 10 only
|
||
// Older IE sometimes signals "interactive" too soon
|
||
if ( document.readyState === "complete" ||
|
||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
|
||
|
||
// Handle it asynchronously to allow scripts the opportunity to delay ready
|
||
window.setTimeout( jQuery.ready );
|
||
|
||
} else {
|
||
|
||
// Use the handy event callback
|
||
document.addEventListener( "DOMContentLoaded", completed );
|
||
|
||
// A fallback to window.onload, that will always work
|
||
window.addEventListener( "load", completed );
|
||
}
|
||
|
||
|
||
|
||
|
||
// Multifunctional method to get and set values of a collection
|
||
// The value/s can optionally be executed if it's a function
|
||
var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
|
||
var i = 0,
|
||
len = elems.length,
|
||
bulk = key == null;
|
||
|
||
// Sets many values
|
||
if ( toType( key ) === "object" ) {
|
||
chainable = true;
|
||
for ( i in key ) {
|
||
access( elems, fn, i, key[ i ], true, emptyGet, raw );
|
||
}
|
||
|
||
// Sets one value
|
||
} else if ( value !== undefined ) {
|
||
chainable = true;
|
||
|
||
if ( !isFunction( value ) ) {
|
||
raw = true;
|
||
}
|
||
|
||
if ( bulk ) {
|
||
|
||
// Bulk operations run against the entire set
|
||
if ( raw ) {
|
||
fn.call( elems, value );
|
||
fn = null;
|
||
|
||
// ...except when executing function values
|
||
} else {
|
||
bulk = fn;
|
||
fn = function( elem, _key, value ) {
|
||
return bulk.call( jQuery( elem ), value );
|
||
};
|
||
}
|
||
}
|
||
|
||
if ( fn ) {
|
||
for ( ; i < len; i++ ) {
|
||
fn(
|
||
elems[ i ], key, raw ?
|
||
value :
|
||
value.call( elems[ i ], i, fn( elems[ i ], key ) )
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( chainable ) {
|
||
return elems;
|
||
}
|
||
|
||
// Gets
|
||
if ( bulk ) {
|
||
return fn.call( elems );
|
||
}
|
||
|
||
return len ? fn( elems[ 0 ], key ) : emptyGet;
|
||
};
|
||
|
||
|
||
// Matches dashed string for camelizing
|
||
var rmsPrefix = /^-ms-/,
|
||
rdashAlpha = /-([a-z])/g;
|
||
|
||
// Used by camelCase as callback to replace()
|
||
function fcamelCase( _all, letter ) {
|
||
return letter.toUpperCase();
|
||
}
|
||
|
||
// Convert dashed to camelCase; used by the css and data modules
|
||
// Support: IE <=9 - 11, Edge 12 - 15
|
||
// Microsoft forgot to hump their vendor prefix (#9572)
|
||
function camelCase( string ) {
|
||
return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
|
||
}
|
||
var acceptData = function( owner ) {
|
||
|
||
// Accepts only:
|
||
// - Node
|
||
// - Node.ELEMENT_NODE
|
||
// - Node.DOCUMENT_NODE
|
||
// - Object
|
||
// - Any
|
||
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
|
||
};
|
||
|
||
|
||
|
||
|
||
function Data() {
|
||
this.expando = jQuery.expando + Data.uid++;
|
||
}
|
||
|
||
Data.uid = 1;
|
||
|
||
Data.prototype = {
|
||
|
||
cache: function( owner ) {
|
||
|
||
// Check if the owner object already has a cache
|
||
var value = owner[ this.expando ];
|
||
|
||
// If not, create one
|
||
if ( !value ) {
|
||
value = {};
|
||
|
||
// We can accept data for non-element nodes in modern browsers,
|
||
// but we should not, see #8335.
|
||
// Always return an empty object.
|
||
if ( acceptData( owner ) ) {
|
||
|
||
// If it is a node unlikely to be stringify-ed or looped over
|
||
// use plain assignment
|
||
if ( owner.nodeType ) {
|
||
owner[ this.expando ] = value;
|
||
|
||
// Otherwise secure it in a non-enumerable property
|
||
// configurable must be true to allow the property to be
|
||
// deleted when data is removed
|
||
} else {
|
||
Object.defineProperty( owner, this.expando, {
|
||
value: value,
|
||
configurable: true
|
||
} );
|
||
}
|
||
}
|
||
}
|
||
|
||
return value;
|
||
},
|
||
set: function( owner, data, value ) {
|
||
var prop,
|
||
cache = this.cache( owner );
|
||
|
||
// Handle: [ owner, key, value ] args
|
||
// Always use camelCase key (gh-2257)
|
||
if ( typeof data === "string" ) {
|
||
cache[ camelCase( data ) ] = value;
|
||
|
||
// Handle: [ owner, { properties } ] args
|
||
} else {
|
||
|
||
// Copy the properties one-by-one to the cache object
|
||
for ( prop in data ) {
|
||
cache[ camelCase( prop ) ] = data[ prop ];
|
||
}
|
||
}
|
||
return cache;
|
||
},
|
||
get: function( owner, key ) {
|
||
return key === undefined ?
|
||
this.cache( owner ) :
|
||
|
||
// Always use camelCase key (gh-2257)
|
||
owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
|
||
},
|
||
access: function( owner, key, value ) {
|
||
|
||
// In cases where either:
|
||
//
|
||
// 1. No key was specified
|
||
// 2. A string key was specified, but no value provided
|
||
//
|
||
// Take the "read" path and allow the get method to determine
|
||
// which value to return, respectively either:
|
||
//
|
||
// 1. The entire cache object
|
||
// 2. The data stored at the key
|
||
//
|
||
if ( key === undefined ||
|
||
( ( key && typeof key === "string" ) && value === undefined ) ) {
|
||
|
||
return this.get( owner, key );
|
||
}
|
||
|
||
// When the key is not a string, or both a key and value
|
||
// are specified, set or extend (existing objects) with either:
|
||
//
|
||
// 1. An object of properties
|
||
// 2. A key and value
|
||
//
|
||
this.set( owner, key, value );
|
||
|
||
// Since the "set" path can have two possible entry points
|
||
// return the expected data based on which path was taken[*]
|
||
return value !== undefined ? value : key;
|
||
},
|
||
remove: function( owner, key ) {
|
||
var i,
|
||
cache = owner[ this.expando ];
|
||
|
||
if ( cache === undefined ) {
|
||
return;
|
||
}
|
||
|
||
if ( key !== undefined ) {
|
||
|
||
// Support array or space separated string of keys
|
||
if ( Array.isArray( key ) ) {
|
||
|
||
// If key is an array of keys...
|
||
// We always set camelCase keys, so remove that.
|
||
key = key.map( camelCase );
|
||
} else {
|
||
key = camelCase( key );
|
||
|
||
// If a key with the spaces exists, use it.
|
||
// Otherwise, create an array by matching non-whitespace
|
||
key = key in cache ?
|
||
[ key ] :
|
||
( key.match( rnothtmlwhite ) || [] );
|
||
}
|
||
|
||
i = key.length;
|
||
|
||
while ( i-- ) {
|
||
delete cache[ key[ i ] ];
|
||
}
|
||
}
|
||
|
||
// Remove the expando if there's no more data
|
||
if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
|
||
|
||
// Support: Chrome <=35 - 45
|
||
// Webkit & Blink performance suffers when deleting properties
|
||
// from DOM nodes, so set to undefined instead
|
||
// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
|
||
if ( owner.nodeType ) {
|
||
owner[ this.expando ] = undefined;
|
||
} else {
|
||
delete owner[ this.expando ];
|
||
}
|
||
}
|
||
},
|
||
hasData: function( owner ) {
|
||
var cache = owner[ this.expando ];
|
||
return cache !== undefined && !jQuery.isEmptyObject( cache );
|
||
}
|
||
};
|
||
var dataPriv = new Data();
|
||
|
||
var dataUser = new Data();
|
||
|
||
|
||
|
||
// Implementation Summary
|
||
//
|
||
// 1. Enforce API surface and semantic compatibility with 1.9.x branch
|
||
// 2. Improve the module's maintainability by reducing the storage
|
||
// paths to a single mechanism.
|
||
// 3. Use the same single mechanism to support "private" and "user" data.
|
||
// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
|
||
// 5. Avoid exposing implementation details on user objects (eg. expando properties)
|
||
// 6. Provide a clear path for implementation upgrade to WeakMap in 2014
|
||
|
||
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
|
||
rmultiDash = /[A-Z]/g;
|
||
|
||
function getData( data ) {
|
||
if ( data === "true" ) {
|
||
return true;
|
||
}
|
||
|
||
if ( data === "false" ) {
|
||
return false;
|
||
}
|
||
|
||
if ( data === "null" ) {
|
||
return null;
|
||
}
|
||
|
||
// Only convert to a number if it doesn't change the string
|
||
if ( data === +data + "" ) {
|
||
return +data;
|
||
}
|
||
|
||
if ( rbrace.test( data ) ) {
|
||
return JSON.parse( data );
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
function dataAttr( elem, key, data ) {
|
||
var name;
|
||
|
||
// If nothing was found internally, try to fetch any
|
||
// data from the HTML5 data-* attribute
|
||
if ( data === undefined && elem.nodeType === 1 ) {
|
||
name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
|
||
data = elem.getAttribute( name );
|
||
|
||
if ( typeof data === "string" ) {
|
||
try {
|
||
data = getData( data );
|
||
} catch ( e ) {}
|
||
|
||
// Make sure we set the data so it isn't changed later
|
||
dataUser.set( elem, key, data );
|
||
} else {
|
||
data = undefined;
|
||
}
|
||
}
|
||
return data;
|
||
}
|
||
|
||
jQuery.extend( {
|
||
hasData: function( elem ) {
|
||
return dataUser.hasData( elem ) || dataPriv.hasData( elem );
|
||
},
|
||
|
||
data: function( elem, name, data ) {
|
||
return dataUser.access( elem, name, data );
|
||
},
|
||
|
||
removeData: function( elem, name ) {
|
||
dataUser.remove( elem, name );
|
||
},
|
||
|
||
// TODO: Now that all calls to _data and _removeData have been replaced
|
||
// with direct calls to dataPriv methods, these can be deprecated.
|
||
_data: function( elem, name, data ) {
|
||
return dataPriv.access( elem, name, data );
|
||
},
|
||
|
||
_removeData: function( elem, name ) {
|
||
dataPriv.remove( elem, name );
|
||
}
|
||
} );
|
||
|
||
jQuery.fn.extend( {
|
||
data: function( key, value ) {
|
||
var i, name, data,
|
||
elem = this[ 0 ],
|
||
attrs = elem && elem.attributes;
|
||
|
||
// Gets all values
|
||
if ( key === undefined ) {
|
||
if ( this.length ) {
|
||
data = dataUser.get( elem );
|
||
|
||
if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
|
||
i = attrs.length;
|
||
while ( i-- ) {
|
||
|
||
// Support: IE 11 only
|
||
// The attrs elements can be null (#14894)
|
||
if ( attrs[ i ] ) {
|
||
name = attrs[ i ].name;
|
||
if ( name.indexOf( "data-" ) === 0 ) {
|
||
name = camelCase( name.slice( 5 ) );
|
||
dataAttr( elem, name, data[ name ] );
|
||
}
|
||
}
|
||
}
|
||
dataPriv.set( elem, "hasDataAttrs", true );
|
||
}
|
||
}
|
||
|
||
return data;
|
||
}
|
||
|
||
// Sets multiple values
|
||
if ( typeof key === "object" ) {
|
||
return this.each( function() {
|
||
dataUser.set( this, key );
|
||
} );
|
||
}
|
||
|
||
return access( this, function( value ) {
|
||
var data;
|
||
|
||
// The calling jQuery object (element matches) is not empty
|
||
// (and therefore has an element appears at this[ 0 ]) and the
|
||
// `value` parameter was not undefined. An empty jQuery object
|
||
// will result in `undefined` for elem = this[ 0 ] which will
|
||
// throw an exception if an attempt to read a data cache is made.
|
||
if ( elem && value === undefined ) {
|
||
|
||
// Attempt to get data from the cache
|
||
// The key will always be camelCased in Data
|
||
data = dataUser.get( elem, key );
|
||
if ( data !== undefined ) {
|
||
return data;
|
||
}
|
||
|
||
// Attempt to "discover" the data in
|
||
// HTML5 custom data-* attrs
|
||
data = dataAttr( elem, key );
|
||
if ( data !== undefined ) {
|
||
return data;
|
||
}
|
||
|
||
// We tried really hard, but the data doesn't exist.
|
||
return;
|
||
}
|
||
|
||
// Set the data...
|
||
this.each( function() {
|
||
|
||
// We always store the camelCased key
|
||
dataUser.set( this, key, value );
|
||
} );
|
||
}, null, value, arguments.length > 1, null, true );
|
||
},
|
||
|
||
removeData: function( key ) {
|
||
return this.each( function() {
|
||
dataUser.remove( this, key );
|
||
} );
|
||
}
|
||
} );
|
||
|
||
|
||
jQuery.extend( {
|
||
queue: function( elem, type, data ) {
|
||
var queue;
|
||
|
||
if ( elem ) {
|
||
type = ( type || "fx" ) + "queue";
|
||
queue = dataPriv.get( elem, type );
|
||
|
||
// Speed up dequeue by getting out quickly if this is just a lookup
|
||
if ( data ) {
|
||
if ( !queue || Array.isArray( data ) ) {
|
||
queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
|
||
} else {
|
||
queue.push( data );
|
||
}
|
||
}
|
||
return queue || [];
|
||
}
|
||
},
|
||
|
||
dequeue: function( elem, type ) {
|
||
type = type || "fx";
|
||
|
||
var queue = jQuery.queue( elem, type ),
|
||
startLength = queue.length,
|
||
fn = queue.shift(),
|
||
hooks = jQuery._queueHooks( elem, type ),
|
||
next = function() {
|
||
jQuery.dequeue( elem, type );
|
||
};
|
||
|
||
// If the fx queue is dequeued, always remove the progress sentinel
|
||
if ( fn === "inprogress" ) {
|
||
fn = queue.shift();
|
||
startLength--;
|
||
}
|
||
|
||
if ( fn ) {
|
||
|
||
// Add a progress sentinel to prevent the fx queue from being
|
||
// automatically dequeued
|
||
if ( type === "fx" ) {
|
||
queue.unshift( "inprogress" );
|
||
}
|
||
|
||
// Clear up the last queue stop function
|
||
delete hooks.stop;
|
||
fn.call( elem, next, hooks );
|
||
}
|
||
|
||
if ( !startLength && hooks ) {
|
||
hooks.empty.fire();
|
||
}
|
||
},
|
||
|
||
// Not public - generate a queueHooks object, or return the current one
|
||
_queueHooks: function( elem, type ) {
|
||
var key = type + "queueHooks";
|
||
return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
|
||
empty: jQuery.Callbacks( "once memory" ).add( function() {
|
||
dataPriv.remove( elem, [ type + "queue", key ] );
|
||
} )
|
||
} );
|
||
}
|
||
} );
|
||
|
||
jQuery.fn.extend( {
|
||
queue: function( type, data ) {
|
||
var setter = 2;
|
||
|
||
if ( typeof type !== "string" ) {
|
||
data = type;
|
||
type = "fx";
|
||
setter--;
|
||
}
|
||
|
||
if ( arguments.length < setter ) {
|
||
return jQuery.queue( this[ 0 ], type );
|
||
}
|
||
|
||
return data === undefined ?
|
||
this :
|
||
this.each( function() {
|
||
var queue = jQuery.queue( this, type, data );
|
||
|
||
// Ensure a hooks for this queue
|
||
jQuery._queueHooks( this, type );
|
||
|
||
if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
|
||
jQuery.dequeue( this, type );
|
||
}
|
||
} );
|
||
},
|
||
dequeue: function( type ) {
|
||
return this.each( function() {
|
||
jQuery.dequeue( this, type );
|
||
} );
|
||
},
|
||
clearQueue: function( type ) {
|
||
return this.queue( type || "fx", [] );
|
||
},
|
||
|
||
// Get a promise resolved when queues of a certain type
|
||
// are emptied (fx is the type by default)
|
||
promise: function( type, obj ) {
|
||
var tmp,
|
||
count = 1,
|
||
defer = jQuery.Deferred(),
|
||
elements = this,
|
||
i = this.length,
|
||
resolve = function() {
|
||
if ( !( --count ) ) {
|
||
defer.resolveWith( elements, [ elements ] );
|
||
}
|
||
};
|
||
|
||
if ( typeof type !== "string" ) {
|
||
obj = type;
|
||
type = undefined;
|
||
}
|
||
type = type || "fx";
|
||
|
||
while ( i-- ) {
|
||
tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
|
||
if ( tmp && tmp.empty ) {
|
||
count++;
|
||
tmp.empty.add( resolve );
|
||
}
|
||
}
|
||
resolve();
|
||
return defer.promise( obj );
|
||
}
|
||
} );
|
||
var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
|
||
|
||
var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
|
||
|
||
|
||
var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
|
||
|
||
var documentElement = document.documentElement;
|
||
|
||
|
||
|
||
var isAttached = function( elem ) {
|
||
return jQuery.contains( elem.ownerDocument, elem );
|
||
},
|
||
composed = { composed: true };
|
||
|
||
// Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
|
||
// Check attachment across shadow DOM boundaries when possible (gh-3504)
|
||
// Support: iOS 10.0-10.2 only
|
||
// Early iOS 10 versions support `attachShadow` but not `getRootNode`,
|
||
// leading to errors. We need to check for `getRootNode`.
|
||
if ( documentElement.getRootNode ) {
|
||
isAttached = function( elem ) {
|
||
return jQuery.contains( elem.ownerDocument, elem ) ||
|
||
elem.getRootNode( composed ) === elem.ownerDocument;
|
||
};
|
||
}
|
||
var isHiddenWithinTree = function( elem, el ) {
|
||
|
||
// isHiddenWithinTree might be called from jQuery#filter function;
|
||
// in that case, element will be second argument
|
||
elem = el || elem;
|
||
|
||
// Inline style trumps all
|
||
return elem.style.display === "none" ||
|
||
elem.style.display === "" &&
|
||
|
||
// Otherwise, check computed style
|
||
// Support: Firefox <=43 - 45
|
||
// Disconnected elements can have computed display: none, so first confirm that elem is
|
||
// in the document.
|
||
isAttached( elem ) &&
|
||
|
||
jQuery.css( elem, "display" ) === "none";
|
||
};
|
||
|
||
|
||
|
||
function adjustCSS( elem, prop, valueParts, tween ) {
|
||
var adjusted, scale,
|
||
maxIterations = 20,
|
||
currentValue = tween ?
|
||
function() {
|
||
return tween.cur();
|
||
} :
|
||
function() {
|
||
return jQuery.css( elem, prop, "" );
|
||
},
|
||
initial = currentValue(),
|
||
unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
|
||
|
||
// Starting value computation is required for potential unit mismatches
|
||
initialInUnit = elem.nodeType &&
|
||
( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
|
||
rcssNum.exec( jQuery.css( elem, prop ) );
|
||
|
||
if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
|
||
|
||
// Support: Firefox <=54
|
||
// Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
|
||
initial = initial / 2;
|
||
|
||
// Trust units reported by jQuery.css
|
||
unit = unit || initialInUnit[ 3 ];
|
||
|
||
// Iteratively approximate from a nonzero starting point
|
||
initialInUnit = +initial || 1;
|
||
|
||
while ( maxIterations-- ) {
|
||
|
||
// Evaluate and update our best guess (doubling guesses that zero out).
|
||
// Finish if the scale equals or crosses 1 (making the old*new product non-positive).
|
||
jQuery.style( elem, prop, initialInUnit + unit );
|
||
if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
|
||
maxIterations = 0;
|
||
}
|
||
initialInUnit = initialInUnit / scale;
|
||
|
||
}
|
||
|
||
initialInUnit = initialInUnit * 2;
|
||
jQuery.style( elem, prop, initialInUnit + unit );
|
||
|
||
// Make sure we update the tween properties later on
|
||
valueParts = valueParts || [];
|
||
}
|
||
|
||
if ( valueParts ) {
|
||
initialInUnit = +initialInUnit || +initial || 0;
|
||
|
||
// Apply relative offset (+=/-=) if specified
|
||
adjusted = valueParts[ 1 ] ?
|
||
initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
|
||
+valueParts[ 2 ];
|
||
if ( tween ) {
|
||
tween.unit = unit;
|
||
tween.start = initialInUnit;
|
||
tween.end = adjusted;
|
||
}
|
||
}
|
||
return adjusted;
|
||
}
|
||
|
||
|
||
var defaultDisplayMap = {};
|
||
|
||
function getDefaultDisplay( elem ) {
|
||
var temp,
|
||
doc = elem.ownerDocument,
|
||
nodeName = elem.nodeName,
|
||
display = defaultDisplayMap[ nodeName ];
|
||
|
||
if ( display ) {
|
||
return display;
|
||
}
|
||
|
||
temp = doc.body.appendChild( doc.createElement( nodeName ) );
|
||
display = jQuery.css( temp, "display" );
|
||
|
||
temp.parentNode.removeChild( temp );
|
||
|
||
if ( display === "none" ) {
|
||
display = "block";
|
||
}
|
||
defaultDisplayMap[ nodeName ] = display;
|
||
|
||
return display;
|
||
}
|
||
|
||
function showHide( elements, show ) {
|
||
var display, elem,
|
||
values = [],
|
||
index = 0,
|
||
length = elements.length;
|
||
|
||
// Determine new display value for elements that need to change
|
||
for ( ; index < length; index++ ) {
|
||
elem = elements[ index ];
|
||
if ( !elem.style ) {
|
||
continue;
|
||
}
|
||
|
||
display = elem.style.display;
|
||
if ( show ) {
|
||
|
||
// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
|
||
// check is required in this first loop unless we have a nonempty display value (either
|
||
// inline or about-to-be-restored)
|
||
if ( display === "none" ) {
|
||
values[ index ] = dataPriv.get( elem, "display" ) || null;
|
||
if ( !values[ index ] ) {
|
||
elem.style.display = "";
|
||
}
|
||
}
|
||
if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
|
||
values[ index ] = getDefaultDisplay( elem );
|
||
}
|
||
} else {
|
||
if ( display !== "none" ) {
|
||
values[ index ] = "none";
|
||
|
||
// Remember what we're overwriting
|
||
dataPriv.set( elem, "display", display );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Set the display of the elements in a second loop to avoid constant reflow
|
||
for ( index = 0; index < length; index++ ) {
|
||
if ( values[ index ] != null ) {
|
||
elements[ index ].style.display = values[ index ];
|
||
}
|
||
}
|
||
|
||
return elements;
|
||
}
|
||
|
||
jQuery.fn.extend( {
|
||
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 ( isHiddenWithinTree( this ) ) {
|
||
jQuery( this ).show();
|
||
} else {
|
||
jQuery( this ).hide();
|
||
}
|
||
} );
|
||
}
|
||
} );
|
||
var rcheckableType = ( /^(?:checkbox|radio)$/i );
|
||
|
||
var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
|
||
|
||
var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
|
||
|
||
|
||
|
||
( function() {
|
||
var fragment = document.createDocumentFragment(),
|
||
div = fragment.appendChild( document.createElement( "div" ) ),
|
||
input = document.createElement( "input" );
|
||
|
||
// Support: Android 4.0 - 4.3 only
|
||
// Check state lost if the name is set (#11217)
|
||
// Support: Windows Web Apps (WWA)
|
||
// `name` and `type` must use .setAttribute for WWA (#14901)
|
||
input.setAttribute( "type", "radio" );
|
||
input.setAttribute( "checked", "checked" );
|
||
input.setAttribute( "name", "t" );
|
||
|
||
div.appendChild( input );
|
||
|
||
// Support: Android <=4.1 only
|
||
// Older WebKit doesn't clone checked state correctly in fragments
|
||
support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
|
||
|
||
// Support: IE <=11 only
|
||
// Make sure textarea (and checkbox) defaultValue is properly cloned
|
||
div.innerHTML = "<textarea>x</textarea>";
|
||
support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
|
||
|
||
// Support: IE <=9 only
|
||
// IE <=9 replaces <option> tags with their contents when inserted outside of
|
||
// the select element.
|
||
div.innerHTML = "<option></option>";
|
||
support.option = !!div.lastChild;
|
||
} )();
|
||
|
||
|
||
// We have to close these tags to support XHTML (#13200)
|
||
var wrapMap = {
|
||
|
||
// XHTML parsers do not magically insert elements in the
|
||
// same way that tag soup parsers do. So we cannot shorten
|
||
// this by omitting <tbody> or other required elements.
|
||
thead: [ 1, "<table>", "</table>" ],
|
||
col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
|
||
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
|
||
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
|
||
|
||
_default: [ 0, "", "" ]
|
||
};
|
||
|
||
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
|
||
wrapMap.th = wrapMap.td;
|
||
|
||
// Support: IE <=9 only
|
||
if ( !support.option ) {
|
||
wrapMap.optgroup = wrapMap.option = [ 1, "<select multiple='multiple'>", "</select>" ];
|
||
}
|
||
|
||
|
||
function getAll( context, tag ) {
|
||
|
||
// Support: IE <=9 - 11 only
|
||
// Use typeof to avoid zero-argument method invocation on host objects (#15151)
|
||
var ret;
|
||
|
||
if ( typeof context.getElementsByTagName !== "undefined" ) {
|
||
ret = context.getElementsByTagName( tag || "*" );
|
||
|
||
} else if ( typeof context.querySelectorAll !== "undefined" ) {
|
||
ret = context.querySelectorAll( tag || "*" );
|
||
|
||
} else {
|
||
ret = [];
|
||
}
|
||
|
||
if ( tag === undefined || tag && nodeName( context, tag ) ) {
|
||
return jQuery.merge( [ context ], ret );
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
|
||
// Mark scripts as having already been evaluated
|
||
function setGlobalEval( elems, refElements ) {
|
||
var i = 0,
|
||
l = elems.length;
|
||
|
||
for ( ; i < l; i++ ) {
|
||
dataPriv.set(
|
||
elems[ i ],
|
||
"globalEval",
|
||
!refElements || dataPriv.get( refElements[ i ], "globalEval" )
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
var rhtml = /<|&#?\w+;/;
|
||
|
||
function buildFragment( elems, context, scripts, selection, ignored ) {
|
||
var elem, tmp, tag, wrap, attached, j,
|
||
fragment = context.createDocumentFragment(),
|
||
nodes = [],
|
||
i = 0,
|
||
l = elems.length;
|
||
|
||
for ( ; i < l; i++ ) {
|
||
elem = elems[ i ];
|
||
|
||
if ( elem || elem === 0 ) {
|
||
|
||
// Add nodes directly
|
||
if ( toType( elem ) === "object" ) {
|
||
|
||
// Support: Android <=4.0 only, PhantomJS 1 only
|
||
// push.apply(_, arraylike) throws on ancient WebKit
|
||
jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
|
||
|
||
// Convert non-html into a text node
|
||
} else if ( !rhtml.test( elem ) ) {
|
||
nodes.push( context.createTextNode( elem ) );
|
||
|
||
// Convert html into DOM nodes
|
||
} else {
|
||
tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
|
||
|
||
// Deserialize a standard representation
|
||
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
|
||
wrap = wrapMap[ tag ] || wrapMap._default;
|
||
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
|
||
|
||
// Descend through wrappers to the right content
|
||
j = wrap[ 0 ];
|
||
while ( j-- ) {
|
||
tmp = tmp.lastChild;
|
||
}
|
||
|
||
// Support: Android <=4.0 only, PhantomJS 1 only
|
||
// push.apply(_, arraylike) throws on ancient WebKit
|
||
jQuery.merge( nodes, tmp.childNodes );
|
||
|
||
// Remember the top-level container
|
||
tmp = fragment.firstChild;
|
||
|
||
// Ensure the created nodes are orphaned (#12392)
|
||
tmp.textContent = "";
|
||
}
|
||
}
|
||
}
|
||
|
||
// Remove wrapper from fragment
|
||
fragment.textContent = "";
|
||
|
||
i = 0;
|
||
while ( ( elem = nodes[ i++ ] ) ) {
|
||
|
||
// Skip elements already in the context collection (trac-4087)
|
||
if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
|
||
if ( ignored ) {
|
||
ignored.push( elem );
|
||
}
|
||
continue;
|
||
}
|
||
|
||
attached = isAttached( elem );
|
||
|
||
// Append to fragment
|
||
tmp = getAll( fragment.appendChild( elem ), "script" );
|
||
|
||
// Preserve script evaluation history
|
||
if ( attached ) {
|
||
setGlobalEval( tmp );
|
||
}
|
||
|
||
// Capture executables
|
||
if ( scripts ) {
|
||
j = 0;
|
||
while ( ( elem = tmp[ j++ ] ) ) {
|
||
if ( rscriptType.test( elem.type || "" ) ) {
|
||
scripts.push( elem );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return fragment;
|
||
}
|
||
|
||
|
||
var
|
||
rkeyEvent = /^key/,
|
||
rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
|
||
rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
|
||
|
||
function returnTrue() {
|
||
return true;
|
||
}
|
||
|
||
function returnFalse() {
|
||
return false;
|
||
}
|
||
|
||
// Support: IE <=9 - 11+
|
||
// focus() and blur() are asynchronous, except when they are no-op.
|
||
// So expect focus to be synchronous when the element is already active,
|
||
// and blur to be synchronous when the element is not already active.
|
||
// (focus and blur are always synchronous in other supported browsers,
|
||
// this just defines when we can count on it).
|
||
function expectSync( elem, type ) {
|
||
return ( elem === safeActiveElement() ) === ( type === "focus" );
|
||
}
|
||
|
||
// Support: IE <=9 only
|
||
// Accessing document.activeElement can throw unexpectedly
|
||
// https://bugs.jquery.com/ticket/13393
|
||
function safeActiveElement() {
|
||
try {
|
||
return document.activeElement;
|
||
} catch ( err ) { }
|
||
}
|
||
|
||
function on( elem, types, selector, data, fn, one ) {
|
||
var origFn, type;
|
||
|
||
// Types can be a map of types/handlers
|
||
if ( typeof types === "object" ) {
|
||
|
||
// ( types-Object, selector, data )
|
||
if ( typeof selector !== "string" ) {
|
||
|
||
// ( types-Object, data )
|
||
data = data || selector;
|
||
selector = undefined;
|
||
}
|
||
for ( type in types ) {
|
||
on( elem, type, selector, data, types[ type ], one );
|
||
}
|
||
return elem;
|
||
}
|
||
|
||
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 elem;
|
||
}
|
||
|
||
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 elem.each( function() {
|
||
jQuery.event.add( this, types, fn, data, selector );
|
||
} );
|
||
}
|
||
|
||
/*
|
||
* Helper functions for managing events -- not part of the public interface.
|
||
* Props to Dean Edwards' addEvent library for many of the ideas.
|
||
*/
|
||
jQuery.event = {
|
||
|
||
global: {},
|
||
|
||
add: function( elem, types, handler, data, selector ) {
|
||
|
||
var handleObjIn, eventHandle, tmp,
|
||
events, t, handleObj,
|
||
special, handlers, type, namespaces, origType,
|
||
elemData = dataPriv.get( elem );
|
||
|
||
// Only attach events to objects that accept data
|
||
if ( !acceptData( elem ) ) {
|
||
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;
|
||
}
|
||
|
||
// Ensure that invalid selectors throw exceptions at attach time
|
||
// Evaluate against documentElement in case elem is a non-element node (e.g., document)
|
||
if ( selector ) {
|
||
jQuery.find.matchesSelector( documentElement, 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 = Object.create( null );
|
||
}
|
||
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 !== "undefined" && jQuery.event.triggered !== e.type ?
|
||
jQuery.event.dispatch.apply( elem, arguments ) : undefined;
|
||
};
|
||
}
|
||
|
||
// Handle multiple events separated by a space
|
||
types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
|
||
t = types.length;
|
||
while ( t-- ) {
|
||
tmp = rtypenamespace.exec( types[ t ] ) || [];
|
||
type = origType = tmp[ 1 ];
|
||
namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
|
||
|
||
// There *must* be a type, no attaching namespace-only handlers
|
||
if ( !type ) {
|
||
continue;
|
||
}
|
||
|
||
// If event changes its type, use the special event handlers for the changed type
|
||
special = jQuery.event.special[ type ] || {};
|
||
|
||
// If selector defined, determine special event api type, otherwise given type
|
||
type = ( selector ? special.delegateType : special.bindType ) || type;
|
||
|
||
// Update special based on newly reset type
|
||
special = jQuery.event.special[ type ] || {};
|
||
|
||
// handleObj is passed to all event handlers
|
||
handleObj = jQuery.extend( {
|
||
type: type,
|
||
origType: origType,
|
||
data: data,
|
||
handler: handler,
|
||
guid: handler.guid,
|
||
selector: selector,
|
||
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
|
||
namespace: namespaces.join( "." )
|
||
}, handleObjIn );
|
||
|
||
// Init the event handler queue if we're the first
|
||
if ( !( handlers = events[ type ] ) ) {
|
||
handlers = events[ type ] = [];
|
||
handlers.delegateCount = 0;
|
||
|
||
// Only use addEventListener if the special events handler returns false
|
||
if ( !special.setup ||
|
||
special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
|
||
|
||
if ( elem.addEventListener ) {
|
||
elem.addEventListener( type, eventHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( special.add ) {
|
||
special.add.call( elem, handleObj );
|
||
|
||
if ( !handleObj.handler.guid ) {
|
||
handleObj.handler.guid = handler.guid;
|
||
}
|
||
}
|
||
|
||
// Add to the element's handler list, delegates in front
|
||
if ( selector ) {
|
||
handlers.splice( handlers.delegateCount++, 0, handleObj );
|
||
} else {
|
||
handlers.push( handleObj );
|
||
}
|
||
|
||
// Keep track of which events have ever been used, for event optimization
|
||
jQuery.event.global[ type ] = true;
|
||
}
|
||
|
||
},
|
||
|
||
// Detach an event or set of events from an element
|
||
remove: function( elem, types, handler, selector, mappedTypes ) {
|
||
|
||
var j, origCount, tmp,
|
||
events, t, handleObj,
|
||
special, handlers, type, namespaces, origType,
|
||
elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
|
||
|
||
if ( !elemData || !( events = elemData.events ) ) {
|
||
return;
|
||
}
|
||
|
||
// Once for each type.namespace in types; type may be omitted
|
||
types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
|
||
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 data and the expando if it's no longer used
|
||
if ( jQuery.isEmptyObject( events ) ) {
|
||
dataPriv.remove( elem, "handle events" );
|
||
}
|
||
},
|
||
|
||
dispatch: function( nativeEvent ) {
|
||
|
||
var i, j, ret, matched, handleObj, handlerQueue,
|
||
args = new Array( arguments.length ),
|
||
|
||
// Make a writable jQuery.Event from the native event object
|
||
event = jQuery.event.fix( nativeEvent ),
|
||
|
||
handlers = (
|
||
dataPriv.get( this, "events" ) || Object.create( null )
|
||
)[ event.type ] || [],
|
||
special = jQuery.event.special[ event.type ] || {};
|
||
|
||
// Use the fix-ed jQuery.Event rather than the (read-only) native event
|
||
args[ 0 ] = event;
|
||
|
||
for ( i = 1; i < arguments.length; i++ ) {
|
||
args[ i ] = arguments[ i ];
|
||
}
|
||
|
||
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() ) {
|
||
|
||
// If the event is namespaced, then each handler is only invoked if it is
|
||
// specially universal or its namespaces are a superset of the event's.
|
||
if ( !event.rnamespace || handleObj.namespace === false ||
|
||
event.rnamespace.test( handleObj.namespace ) ) {
|
||
|
||
event.handleObj = handleObj;
|
||
event.data = handleObj.data;
|
||
|
||
ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
|
||
handleObj.handler ).apply( matched.elem, args );
|
||
|
||
if ( ret !== undefined ) {
|
||
if ( ( event.result = ret ) === false ) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Call the postDispatch hook for the mapped type
|
||
if ( special.postDispatch ) {
|
||
special.postDispatch.call( this, event );
|
||
}
|
||
|
||
return event.result;
|
||
},
|
||
|
||
handlers: function( event, handlers ) {
|
||
var i, handleObj, sel, matchedHandlers, matchedSelectors,
|
||
handlerQueue = [],
|
||
delegateCount = handlers.delegateCount,
|
||
cur = event.target;
|
||
|
||
// Find delegate handlers
|
||
if ( delegateCount &&
|
||
|
||
// Support: IE <=9
|
||
// Black-hole SVG <use> instance trees (trac-13180)
|
||
cur.nodeType &&
|
||
|
||
// Support: Firefox <=42
|
||
// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
|
||
// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
|
||
// Support: IE 11 only
|
||
// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
|
||
!( event.type === "click" && event.button >= 1 ) ) {
|
||
|
||
for ( ; cur !== this; cur = cur.parentNode || this ) {
|
||
|
||
// Don't check non-elements (#13208)
|
||
// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
|
||
if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
|
||
matchedHandlers = [];
|
||
matchedSelectors = {};
|
||
for ( i = 0; i < delegateCount; i++ ) {
|
||
handleObj = handlers[ i ];
|
||
|
||
// Don't conflict with Object.prototype properties (#13203)
|
||
sel = handleObj.selector + " ";
|
||
|
||
if ( matchedSelectors[ sel ] === undefined ) {
|
||
matchedSelectors[ sel ] = handleObj.needsContext ?
|
||
jQuery( sel, this ).index( cur ) > -1 :
|
||
jQuery.find( sel, this, null, [ cur ] ).length;
|
||
}
|
||
if ( matchedSelectors[ sel ] ) {
|
||
matchedHandlers.push( handleObj );
|
||
}
|
||
}
|
||
if ( matchedHandlers.length ) {
|
||
handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Add the remaining (directly-bound) handlers
|
||
cur = this;
|
||
if ( delegateCount < handlers.length ) {
|
||
handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
|
||
}
|
||
|
||
return handlerQueue;
|
||
},
|
||
|
||
addProp: function( name, hook ) {
|
||
Object.defineProperty( jQuery.Event.prototype, name, {
|
||
enumerable: true,
|
||
configurable: true,
|
||
|
||
get: isFunction( hook ) ?
|
||
function() {
|
||
if ( this.originalEvent ) {
|
||
return hook( this.originalEvent );
|
||
}
|
||
} :
|
||
function() {
|
||
if ( this.originalEvent ) {
|
||
return this.originalEvent[ name ];
|
||
}
|
||
},
|
||
|
||
set: function( value ) {
|
||
Object.defineProperty( this, name, {
|
||
enumerable: true,
|
||
configurable: true,
|
||
writable: true,
|
||
value: value
|
||
} );
|
||
}
|
||
} );
|
||
},
|
||
|
||
fix: function( originalEvent ) {
|
||
return originalEvent[ jQuery.expando ] ?
|
||
originalEvent :
|
||
new jQuery.Event( originalEvent );
|
||
},
|
||
|
||
special: {
|
||
load: {
|
||
|
||
// Prevent triggered image.load events from bubbling to window.load
|
||
noBubble: true
|
||
},
|
||
click: {
|
||
|
||
// Utilize native event to ensure correct state for checkable inputs
|
||
setup: function( data ) {
|
||
|
||
// For mutual compressibility with _default, replace `this` access with a local var.
|
||
// `|| data` is dead code meant only to preserve the variable through minification.
|
||
var el = this || data;
|
||
|
||
// Claim the first handler
|
||
if ( rcheckableType.test( el.type ) &&
|
||
el.click && nodeName( el, "input" ) ) {
|
||
|
||
// dataPriv.set( el, "click", ... )
|
||
leverageNative( el, "click", returnTrue );
|
||
}
|
||
|
||
// Return false to allow normal processing in the caller
|
||
return false;
|
||
},
|
||
trigger: function( data ) {
|
||
|
||
// For mutual compressibility with _default, replace `this` access with a local var.
|
||
// `|| data` is dead code meant only to preserve the variable through minification.
|
||
var el = this || data;
|
||
|
||
// Force setup before triggering a click
|
||
if ( rcheckableType.test( el.type ) &&
|
||
el.click && nodeName( el, "input" ) ) {
|
||
|
||
leverageNative( el, "click" );
|
||
}
|
||
|
||
// Return non-false to allow normal event-path propagation
|
||
return true;
|
||
},
|
||
|
||
// For cross-browser consistency, suppress native .click() on links
|
||
// Also prevent it if we're currently inside a leveraged native-event stack
|
||
_default: function( event ) {
|
||
var target = event.target;
|
||
return rcheckableType.test( target.type ) &&
|
||
target.click && nodeName( target, "input" ) &&
|
||
dataPriv.get( target, "click" ) ||
|
||
nodeName( target, "a" );
|
||
}
|
||
},
|
||
|
||
beforeunload: {
|
||
postDispatch: function( event ) {
|
||
|
||
// Support: Firefox 20+
|
||
// Firefox doesn't alert if the returnValue field is not set.
|
||
if ( event.result !== undefined && event.originalEvent ) {
|
||
event.originalEvent.returnValue = event.result;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// Ensure the presence of an event listener that handles manually-triggered
|
||
// synthetic events by interrupting progress until reinvoked in response to
|
||
// *native* events that it fires directly, ensuring that state changes have
|
||
// already occurred before other listeners are invoked.
|
||
function leverageNative( el, type, expectSync ) {
|
||
|
||
// Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
|
||
if ( !expectSync ) {
|
||
if ( dataPriv.get( el, type ) === undefined ) {
|
||
jQuery.event.add( el, type, returnTrue );
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Register the controller as a special universal handler for all event namespaces
|
||
dataPriv.set( el, type, false );
|
||
jQuery.event.add( el, type, {
|
||
namespace: false,
|
||
handler: function( event ) {
|
||
var notAsync, result,
|
||
saved = dataPriv.get( this, type );
|
||
|
||
if ( ( event.isTrigger & 1 ) && this[ type ] ) {
|
||
|
||
// Interrupt processing of the outer synthetic .trigger()ed event
|
||
// Saved data should be false in such cases, but might be a leftover capture object
|
||
// from an async native handler (gh-4350)
|
||
if ( !saved.length ) {
|
||
|
||
// Store arguments for use when handling the inner native event
|
||
// There will always be at least one argument (an event object), so this array
|
||
// will not be confused with a leftover capture object.
|
||
saved = slice.call( arguments );
|
||
dataPriv.set( this, type, saved );
|
||
|
||
// Trigger the native event and capture its result
|
||
// Support: IE <=9 - 11+
|
||
// focus() and blur() are asynchronous
|
||
notAsync = expectSync( this, type );
|
||
this[ type ]();
|
||
result = dataPriv.get( this, type );
|
||
if ( saved !== result || notAsync ) {
|
||
dataPriv.set( this, type, false );
|
||
} else {
|
||
result = {};
|
||
}
|
||
if ( saved !== result ) {
|
||
|
||
// Cancel the outer synthetic event
|
||
event.stopImmediatePropagation();
|
||
event.preventDefault();
|
||
return result.value;
|
||
}
|
||
|
||
// If this is an inner synthetic event for an event with a bubbling surrogate
|
||
// (focus or blur), assume that the surrogate already propagated from triggering the
|
||
// native event and prevent that from happening again here.
|
||
// This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
|
||
// bubbling surrogate propagates *after* the non-bubbling base), but that seems
|
||
// less bad than duplication.
|
||
} else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
|
||
event.stopPropagation();
|
||
}
|
||
|
||
// If this is a native event triggered above, everything is now in order
|
||
// Fire an inner synthetic event with the original arguments
|
||
} else if ( saved.length ) {
|
||
|
||
// ...and capture the result
|
||
dataPriv.set( this, type, {
|
||
value: jQuery.event.trigger(
|
||
|
||
// Support: IE <=9 - 11+
|
||
// Extend with the prototype to reset the above stopImmediatePropagation()
|
||
jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
|
||
saved.slice( 1 ),
|
||
this
|
||
)
|
||
} );
|
||
|
||
// Abort handling of the native event
|
||
event.stopImmediatePropagation();
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
|
||
jQuery.removeEvent = function( elem, type, handle ) {
|
||
|
||
// This "if" is needed for plain objects
|
||
if ( elem.removeEventListener ) {
|
||
elem.removeEventListener( type, 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: Android <=2.3 only
|
||
src.returnValue === false ?
|
||
returnTrue :
|
||
returnFalse;
|
||
|
||
// Create target properties
|
||
// Support: Safari <=6 - 7 only
|
||
// Target should not be a text node (#504, #13143)
|
||
this.target = ( src.target && src.target.nodeType === 3 ) ?
|
||
src.target.parentNode :
|
||
src.target;
|
||
|
||
this.currentTarget = src.currentTarget;
|
||
this.relatedTarget = src.relatedTarget;
|
||
|
||
// 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 || Date.now();
|
||
|
||
// Mark it as fixed
|
||
this[ jQuery.expando ] = true;
|
||
};
|
||
|
||
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
|
||
// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
||
jQuery.Event.prototype = {
|
||
constructor: jQuery.Event,
|
||
isDefaultPrevented: returnFalse,
|
||
isPropagationStopped: returnFalse,
|
||
isImmediatePropagationStopped: returnFalse,
|
||
isSimulated: false,
|
||
|
||
preventDefault: function() {
|
||
var e = this.originalEvent;
|
||
|
||
this.isDefaultPrevented = returnTrue;
|
||
|
||
if ( e && !this.isSimulated ) {
|
||
e.preventDefault();
|
||
}
|
||
},
|
||
stopPropagation: function() {
|
||
var e = this.originalEvent;
|
||
|
||
this.isPropagationStopped = returnTrue;
|
||
|
||
if ( e && !this.isSimulated ) {
|
||
e.stopPropagation();
|
||
}
|
||
},
|
||
stopImmediatePropagation: function() {
|
||
var e = this.originalEvent;
|
||
|
||
this.isImmediatePropagationStopped = returnTrue;
|
||
|
||
if ( e && !this.isSimulated ) {
|
||
e.stopImmediatePropagation();
|
||
}
|
||
|
||
this.stopPropagation();
|
||
}
|
||
};
|
||
|
||
// Includes all common event props including KeyEvent and MouseEvent specific props
|
||
jQuery.each( {
|
||
altKey: true,
|
||
bubbles: true,
|
||
cancelable: true,
|
||
changedTouches: true,
|
||
ctrlKey: true,
|
||
detail: true,
|
||
eventPhase: true,
|
||
metaKey: true,
|
||
pageX: true,
|
||
pageY: true,
|
||
shiftKey: true,
|
||
view: true,
|
||
"char": true,
|
||
code: true,
|
||
charCode: true,
|
||
key: true,
|
||
keyCode: true,
|
||
button: true,
|
||
buttons: true,
|
||
clientX: true,
|
||
clientY: true,
|
||
offsetX: true,
|
||
offsetY: true,
|
||
pointerId: true,
|
||
pointerType: true,
|
||
screenX: true,
|
||
screenY: true,
|
||
targetTouches: true,
|
||
toElement: true,
|
||
touches: true,
|
||
|
||
which: function( event ) {
|
||
var button = event.button;
|
||
|
||
// Add which for key events
|
||
if ( event.which == null && rkeyEvent.test( event.type ) ) {
|
||
return event.charCode != null ? event.charCode : event.keyCode;
|
||
}
|
||
|
||
// Add which for click: 1 === left; 2 === middle; 3 === right
|
||
if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
|
||
if ( button & 1 ) {
|
||
return 1;
|
||
}
|
||
|
||
if ( button & 2 ) {
|
||
return 3;
|
||
}
|
||
|
||
if ( button & 4 ) {
|
||
return 2;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
return event.which;
|
||
}
|
||
}, jQuery.event.addProp );
|
||
|
||
jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
|
||
jQuery.event.special[ type ] = {
|
||
|
||
// Utilize native event if possible so blur/focus sequence is correct
|
||
setup: function() {
|
||
|
||
// Claim the first handler
|
||
// dataPriv.set( this, "focus", ... )
|
||
// dataPriv.set( this, "blur", ... )
|
||
leverageNative( this, type, expectSync );
|
||
|
||
// Return false to allow normal processing in the caller
|
||
return false;
|
||
},
|
||
trigger: function() {
|
||
|
||
// Force setup before trigger
|
||
leverageNative( this, type );
|
||
|
||
// Return non-false to allow normal event-path propagation
|
||
return true;
|
||
},
|
||
|
||
delegateType: delegateType
|
||
};
|
||
} );
|
||
|
||
// Create mouseenter/leave events using mouseover/out and event-time checks
|
||
// so that event delegation works in jQuery.
|
||
// Do the same for pointerenter/pointerleave and pointerover/pointerout
|
||
//
|
||
// Support: Safari 7 only
|
||
// Safari sends mouseenter too often; see:
|
||
// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
|
||
// for the description of the bug (it existed in older Chrome versions as well).
|
||
jQuery.each( {
|
||
mouseenter: "mouseover",
|
||
mouseleave: "mouseout",
|
||
pointerenter: "pointerover",
|
||
pointerleave: "pointerout"
|
||
}, 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 mouseenter/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;
|
||
}
|
||
};
|
||
} );
|
||
|
||
jQuery.fn.extend( {
|
||
|
||
on: function( types, selector, data, fn ) {
|
||
return on( this, types, selector, data, fn );
|
||
},
|
||
one: function( types, selector, data, fn ) {
|
||
return on( this, 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 );
|
||
} );
|
||
}
|
||
} );
|
||
|
||
|
||
var
|
||
|
||
// Support: IE <=10 - 11, Edge 12 - 13 only
|
||
// In IE/Edge using regex groups here causes severe slowdowns.
|
||
// See https://connect.microsoft.com/IE/feedback/details/1736512/
|
||
rnoInnerhtml = /<script|<style|<link/i,
|
||
|
||
// checked="checked" or checked
|
||
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
|
||
rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
|
||
|
||
// Prefer a tbody over its parent table for containing new rows
|
||
function manipulationTarget( elem, content ) {
|
||
if ( nodeName( elem, "table" ) &&
|
||
nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
|
||
|
||
return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
|
||
}
|
||
|
||
return elem;
|
||
}
|
||
|
||
// Replace/restore the type attribute of script elements for safe DOM manipulation
|
||
function disableScript( elem ) {
|
||
elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
|
||
return elem;
|
||
}
|
||
function restoreScript( elem ) {
|
||
if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
|
||
elem.type = elem.type.slice( 5 );
|
||
} else {
|
||
elem.removeAttribute( "type" );
|
||
}
|
||
|
||
return elem;
|
||
}
|
||
|
||
function cloneCopyEvent( src, dest ) {
|
||
var i, l, type, pdataOld, udataOld, udataCur, events;
|
||
|
||
if ( dest.nodeType !== 1 ) {
|
||
return;
|
||
}
|
||
|
||
// 1. Copy private data: events, handlers, etc.
|
||
if ( dataPriv.hasData( src ) ) {
|
||
pdataOld = dataPriv.get( src );
|
||
events = pdataOld.events;
|
||
|
||
if ( events ) {
|
||
dataPriv.remove( dest, "handle events" );
|
||
|
||
for ( type in events ) {
|
||
for ( i = 0, l = events[ type ].length; i < l; i++ ) {
|
||
jQuery.event.add( dest, type, events[ type ][ i ] );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. Copy user data
|
||
if ( dataUser.hasData( src ) ) {
|
||
udataOld = dataUser.access( src );
|
||
udataCur = jQuery.extend( {}, udataOld );
|
||
|
||
dataUser.set( dest, udataCur );
|
||
}
|
||
}
|
||
|
||
// Fix IE bugs, see support tests
|
||
function fixInput( src, dest ) {
|
||
var nodeName = dest.nodeName.toLowerCase();
|
||
|
||
// Fails to persist the checked state of a cloned checkbox or radio button.
|
||
if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
|
||
dest.checked = src.checked;
|
||
|
||
// Fails to return the selected option to the default selected state when cloning options
|
||
} else if ( nodeName === "input" || nodeName === "textarea" ) {
|
||
dest.defaultValue = src.defaultValue;
|
||
}
|
||
}
|
||
|
||
function domManip( collection, args, callback, ignored ) {
|
||
|
||
// Flatten any nested arrays
|
||
args = flat( args );
|
||
|
||
var fragment, first, scripts, hasScripts, node, doc,
|
||
i = 0,
|
||
l = collection.length,
|
||
iNoClone = l - 1,
|
||
value = args[ 0 ],
|
||
valueIsFunction = isFunction( value );
|
||
|
||
// We can't cloneNode fragments that contain checked, in WebKit
|
||
if ( valueIsFunction ||
|
||
( l > 1 && typeof value === "string" &&
|
||
!support.checkClone && rchecked.test( value ) ) ) {
|
||
return collection.each( function( index ) {
|
||
var self = collection.eq( index );
|
||
if ( valueIsFunction ) {
|
||
args[ 0 ] = value.call( this, index, self.html() );
|
||
}
|
||
domManip( self, args, callback, ignored );
|
||
} );
|
||
}
|
||
|
||
if ( l ) {
|
||
fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
|
||
first = fragment.firstChild;
|
||
|
||
if ( fragment.childNodes.length === 1 ) {
|
||
fragment = first;
|
||
}
|
||
|
||
// Require either new content or an interest in ignored elements to invoke the callback
|
||
if ( first || ignored ) {
|
||
scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
|
||
hasScripts = scripts.length;
|
||
|
||
// Use the original fragment for the last item
|
||
// instead of the first because it can end up
|
||
// being emptied incorrectly in certain situations (#8070).
|
||
for ( ; i < l; i++ ) {
|
||
node = fragment;
|
||
|
||
if ( i !== iNoClone ) {
|
||
node = jQuery.clone( node, true, true );
|
||
|
||
// Keep references to cloned scripts for later restoration
|
||
if ( hasScripts ) {
|
||
|
||
// Support: Android <=4.0 only, PhantomJS 1 only
|
||
// push.apply(_, arraylike) throws on ancient WebKit
|
||
jQuery.merge( scripts, getAll( node, "script" ) );
|
||
}
|
||
}
|
||
|
||
callback.call( collection[ 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 || "" ) &&
|
||
!dataPriv.access( node, "globalEval" ) &&
|
||
jQuery.contains( doc, node ) ) {
|
||
|
||
if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) {
|
||
|
||
// Optional AJAX dependency, but won't run scripts if not present
|
||
if ( jQuery._evalUrl && !node.noModule ) {
|
||
jQuery._evalUrl( node.src, {
|
||
nonce: node.nonce || node.getAttribute( "nonce" )
|
||
}, doc );
|
||
}
|
||
} else {
|
||
DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return collection;
|
||
}
|
||
|
||
function remove( elem, selector, keepData ) {
|
||
var node,
|
||
nodes = selector ? jQuery.filter( selector, elem ) : elem,
|
||
i = 0;
|
||
|
||
for ( ; ( node = nodes[ i ] ) != null; i++ ) {
|
||
if ( !keepData && node.nodeType === 1 ) {
|
||
jQuery.cleanData( getAll( node ) );
|
||
}
|
||
|
||
if ( node.parentNode ) {
|
||
if ( keepData && isAttached( node ) ) {
|
||
setGlobalEval( getAll( node, "script" ) );
|
||
}
|
||
node.parentNode.removeChild( node );
|
||
}
|
||
}
|
||
|
||
return elem;
|
||
}
|
||
|
||
jQuery.extend( {
|
||
htmlPrefilter: function( html ) {
|
||
return html;
|
||
},
|
||
|
||
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
|
||
var i, l, srcElements, destElements,
|
||
clone = elem.cloneNode( true ),
|
||
inPage = isAttached( elem );
|
||
|
||
// Fix IE cloning issues
|
||
if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
|
||
!jQuery.isXMLDoc( elem ) ) {
|
||
|
||
// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
|
||
destElements = getAll( clone );
|
||
srcElements = getAll( elem );
|
||
|
||
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
||
fixInput( srcElements[ i ], destElements[ i ] );
|
||
}
|
||
}
|
||
|
||
// Copy the events from the original to the clone
|
||
if ( dataAndEvents ) {
|
||
if ( deepDataAndEvents ) {
|
||
srcElements = srcElements || getAll( elem );
|
||
destElements = destElements || getAll( clone );
|
||
|
||
for ( i = 0, l = srcElements.length; i < l; i++ ) {
|
||
cloneCopyEvent( srcElements[ i ], destElements[ i ] );
|
||
}
|
||
} else {
|
||
cloneCopyEvent( elem, clone );
|
||
}
|
||
}
|
||
|
||
// Preserve script evaluation history
|
||
destElements = getAll( clone, "script" );
|
||
if ( destElements.length > 0 ) {
|
||
setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
|
||
}
|
||
|
||
// Return the cloned set
|
||
return clone;
|
||
},
|
||
|
||
cleanData: function( elems ) {
|
||
var data, elem, type,
|
||
special = jQuery.event.special,
|
||
i = 0;
|
||
|
||
for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
|
||
if ( acceptData( elem ) ) {
|
||
if ( ( data = elem[ dataPriv.expando ] ) ) {
|
||
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 );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Support: Chrome <=35 - 45+
|
||
// Assign undefined instead of using delete, see Data#remove
|
||
elem[ dataPriv.expando ] = undefined;
|
||
}
|
||
if ( elem[ dataUser.expando ] ) {
|
||
|
||
// Support: Chrome <=35 - 45+
|
||
// Assign undefined instead of using delete, see Data#remove
|
||
elem[ dataUser.expando ] = undefined;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
|
||
jQuery.fn.extend( {
|
||
detach: function( selector ) {
|
||
return remove( this, selector, true );
|
||
},
|
||
|
||
remove: function( selector ) {
|
||
return remove( this, selector );
|
||
},
|
||
|
||
text: function( value ) {
|
||
return access( this, function( value ) {
|
||
return value === undefined ?
|
||
jQuery.text( this ) :
|
||
this.empty().each( function() {
|
||
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
||
this.textContent = value;
|
||
}
|
||
} );
|
||
}, null, value, arguments.length );
|
||
},
|
||
|
||
append: function() {
|
||
return domManip( this, arguments, function( elem ) {
|
||
if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
|
||
var target = manipulationTarget( this, elem );
|
||
target.appendChild( elem );
|
||
}
|
||
} );
|
||
},
|
||
|
||
prepend: function() {
|
||
return domManip( this, 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 domManip( this, arguments, function( elem ) {
|
||
if ( this.parentNode ) {
|
||
this.parentNode.insertBefore( elem, this );
|
||
}
|
||
} );
|
||
},
|
||
|
||
after: function() {
|
||
return domManip( this, arguments, function( elem ) {
|
||
if ( this.parentNode ) {
|
||
this.parentNode.insertBefore( elem, this.nextSibling );
|
||
}
|
||
} );
|
||
},
|
||
|
||
empty: function() {
|
||
var elem,
|
||
i = 0;
|
||
|
||
for ( ; ( elem = this[ i ] ) != null; i++ ) {
|
||
if ( elem.nodeType === 1 ) {
|
||
|
||
// Prevent memory leaks
|
||
jQuery.cleanData( getAll( elem, false ) );
|
||
|
||
// Remove any remaining nodes
|
||
elem.textContent = "";
|
||
}
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
clone: function( dataAndEvents, deepDataAndEvents ) {
|
||
dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
|
||
deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
|
||
|
||
return this.map( function() {
|
||
return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
|
||
} );
|
||
},
|
||
|
||
html: function( value ) {
|
||
return access( this, function( value ) {
|
||
var elem = this[ 0 ] || {},
|
||
i = 0,
|
||
l = this.length;
|
||
|
||
if ( value === undefined && elem.nodeType === 1 ) {
|
||
return elem.innerHTML;
|
||
}
|
||
|
||
// See if we can take a shortcut and just use innerHTML
|
||
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
|
||
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
|
||
|
||
value = jQuery.htmlPrefilter( value );
|
||
|
||
try {
|
||
for ( ; i < l; i++ ) {
|
||
elem = this[ i ] || {};
|
||
|
||
// Remove element nodes and prevent memory leaks
|
||
if ( elem.nodeType === 1 ) {
|
||
jQuery.cleanData( getAll( elem, false ) );
|
||
elem.innerHTML = value;
|
||
}
|
||
}
|
||
|
||
elem = 0;
|
||
|
||
// If using innerHTML throws an exception, use the fallback method
|
||
} catch ( e ) {}
|
||
}
|
||
|
||
if ( elem ) {
|
||
this.empty().append( value );
|
||
}
|
||
}, null, value, arguments.length );
|
||
},
|
||
|
||
replaceWith: function() {
|
||
var ignored = [];
|
||
|
||
// Make the changes, replacing each non-ignored context element with the new content
|
||
return domManip( this, arguments, function( elem ) {
|
||
var parent = this.parentNode;
|
||
|
||
if ( jQuery.inArray( this, ignored ) < 0 ) {
|
||
jQuery.cleanData( getAll( this ) );
|
||
if ( parent ) {
|
||
parent.replaceChild( elem, this );
|
||
}
|
||
}
|
||
|
||
// Force callback invocation
|
||
}, ignored );
|
||
}
|
||
} );
|
||
|
||
jQuery.each( {
|
||
appendTo: "append",
|
||
prependTo: "prepend",
|
||
insertBefore: "before",
|
||
insertAfter: "after",
|
||
replaceAll: "replaceWith"
|
||
}, function( name, original ) {
|
||
jQuery.fn[ name ] = function( selector ) {
|
||
var elems,
|
||
ret = [],
|
||
insert = jQuery( selector ),
|
||
last = insert.length - 1,
|
||
i = 0;
|
||
|
||
for ( ; i <= last; i++ ) {
|
||
elems = i === last ? this : this.clone( true );
|
||
jQuery( insert[ i ] )[ original ]( elems );
|
||
|
||
// Support: Android <=4.0 only, PhantomJS 1 only
|
||
// .get() because push.apply(_, arraylike) throws on ancient WebKit
|
||
push.apply( ret, elems.get() );
|
||
}
|
||
|
||
return this.pushStack( ret );
|
||
};
|
||
} );
|
||
var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
|
||
|
||
var getStyles = function( elem ) {
|
||
|
||
// Support: IE <=11 only, Firefox <=30 (#15098, #14150)
|
||
// IE throws on elements created in popups
|
||
// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
|
||
var view = elem.ownerDocument.defaultView;
|
||
|
||
if ( !view || !view.opener ) {
|
||
view = window;
|
||
}
|
||
|
||
return view.getComputedStyle( elem );
|
||
};
|
||
|
||
var swap = function( elem, options, callback ) {
|
||
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.call( elem );
|
||
|
||
// Revert the old values
|
||
for ( name in options ) {
|
||
elem.style[ name ] = old[ name ];
|
||
}
|
||
|
||
return ret;
|
||
};
|
||
|
||
|
||
var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
|
||
|
||
|
||
|
||
( function() {
|
||
|
||
// Executing both pixelPosition & boxSizingReliable tests require only one layout
|
||
// so they're executed at the same time to save the second computation.
|
||
function computeStyleTests() {
|
||
|
||
// This is a singleton, we need to execute it only once
|
||
if ( !div ) {
|
||
return;
|
||
}
|
||
|
||
container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
|
||
"margin-top:1px;padding:0;border:0";
|
||
div.style.cssText =
|
||
"position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
|
||
"margin:auto;border:1px;padding:1px;" +
|
||
"width:60%;top:1%";
|
||
documentElement.appendChild( container ).appendChild( div );
|
||
|
||
var divStyle = window.getComputedStyle( div );
|
||
pixelPositionVal = divStyle.top !== "1%";
|
||
|
||
// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
|
||
reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
|
||
|
||
// Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
|
||
// Some styles come back with percentage values, even though they shouldn't
|
||
div.style.right = "60%";
|
||
pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
|
||
|
||
// Support: IE 9 - 11 only
|
||
// Detect misreporting of content dimensions for box-sizing:border-box elements
|
||
boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
|
||
|
||
// Support: IE 9 only
|
||
// Detect overflow:scroll screwiness (gh-3699)
|
||
// Support: Chrome <=64
|
||
// Don't get tricked when zoom affects offsetWidth (gh-4029)
|
||
div.style.position = "absolute";
|
||
scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;
|
||
|
||
documentElement.removeChild( container );
|
||
|
||
// Nullify the div so it wouldn't be stored in the memory and
|
||
// it will also be a sign that checks already performed
|
||
div = null;
|
||
}
|
||
|
||
function roundPixelMeasures( measure ) {
|
||
return Math.round( parseFloat( measure ) );
|
||
}
|
||
|
||
var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
|
||
reliableTrDimensionsVal, reliableMarginLeftVal,
|
||
container = document.createElement( "div" ),
|
||
div = document.createElement( "div" );
|
||
|
||
// Finish early in limited (non-browser) environments
|
||
if ( !div.style ) {
|
||
return;
|
||
}
|
||
|
||
// Support: IE <=9 - 11 only
|
||
// Style of cloned element affects source element cloned (#8908)
|
||
div.style.backgroundClip = "content-box";
|
||
div.cloneNode( true ).style.backgroundClip = "";
|
||
support.clearCloneStyle = div.style.backgroundClip === "content-box";
|
||
|
||
jQuery.extend( support, {
|
||
boxSizingReliable: function() {
|
||
computeStyleTests();
|
||
return boxSizingReliableVal;
|
||
},
|
||
pixelBoxStyles: function() {
|
||
computeStyleTests();
|
||
return pixelBoxStylesVal;
|
||
},
|
||
pixelPosition: function() {
|
||
computeStyleTests();
|
||
return pixelPositionVal;
|
||
},
|
||
reliableMarginLeft: function() {
|
||
computeStyleTests();
|
||
return reliableMarginLeftVal;
|
||
},
|
||
scrollboxSize: function() {
|
||
computeStyleTests();
|
||
return scrollboxSizeVal;
|
||
},
|
||
|
||
// Support: IE 9 - 11+, Edge 15 - 18+
|
||
// IE/Edge misreport `getComputedStyle` of table rows with width/height
|
||
// set in CSS while `offset*` properties report correct values.
|
||
// Behavior in IE 9 is more subtle than in newer versions & it passes
|
||
// some versions of this test; make sure not to make it pass there!
|
||
reliableTrDimensions: function() {
|
||
var table, tr, trChild, trStyle;
|
||
if ( reliableTrDimensionsVal == null ) {
|
||
table = document.createElement( "table" );
|
||
tr = document.createElement( "tr" );
|
||
trChild = document.createElement( "div" );
|
||
|
||
table.style.cssText = "position:absolute;left:-11111px";
|
||
tr.style.height = "1px";
|
||
trChild.style.height = "9px";
|
||
|
||
documentElement
|
||
.appendChild( table )
|
||
.appendChild( tr )
|
||
.appendChild( trChild );
|
||
|
||
trStyle = window.getComputedStyle( tr );
|
||
reliableTrDimensionsVal = parseInt( trStyle.height ) > 3;
|
||
|
||
documentElement.removeChild( table );
|
||
}
|
||
return reliableTrDimensionsVal;
|
||
}
|
||
} );
|
||
} )();
|
||
|
||
|
||
function curCSS( elem, name, computed ) {
|
||
var width, minWidth, maxWidth, ret,
|
||
|
||
// Support: Firefox 51+
|
||
// Retrieving style before computed somehow
|
||
// fixes an issue with getting wrong values
|
||
// on detached elements
|
||
style = elem.style;
|
||
|
||
computed = computed || getStyles( elem );
|
||
|
||
// getPropertyValue is needed for:
|
||
// .css('filter') (IE 9 only, #12537)
|
||
// .css('--customProperty) (#3144)
|
||
if ( computed ) {
|
||
ret = computed.getPropertyValue( name ) || computed[ name ];
|
||
|
||
if ( ret === "" && !isAttached( elem ) ) {
|
||
ret = jQuery.style( elem, name );
|
||
}
|
||
|
||
// A tribute to the "awesome hack by Dean Edwards"
|
||
// Android Browser returns percentage for some values,
|
||
// but width seems to be reliably pixels.
|
||
// This is against the CSSOM draft spec:
|
||
// https://drafts.csswg.org/cssom/#resolved-values
|
||
if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
|
||
|
||
// Remember the original values
|
||
width = style.width;
|
||
minWidth = style.minWidth;
|
||
maxWidth = style.maxWidth;
|
||
|
||
// Put in the new values to get a computed value out
|
||
style.minWidth = style.maxWidth = style.width = ret;
|
||
ret = computed.width;
|
||
|
||
// Revert the changed values
|
||
style.width = width;
|
||
style.minWidth = minWidth;
|
||
style.maxWidth = maxWidth;
|
||
}
|
||
}
|
||
|
||
return ret !== undefined ?
|
||
|
||
// Support: IE <=9 - 11 only
|
||
// IE returns zIndex value as an integer.
|
||
ret + "" :
|
||
ret;
|
||
}
|
||
|
||
|
||
function addGetHookIf( conditionFn, hookFn ) {
|
||
|
||
// Define the hook, we'll check on the first run if it's really needed.
|
||
return {
|
||
get: function() {
|
||
if ( conditionFn() ) {
|
||
|
||
// Hook not needed (or it's not possible to use it due
|
||
// to missing dependency), remove it.
|
||
delete this.get;
|
||
return;
|
||
}
|
||
|
||
// Hook needed; redefine it so that the support test is not executed again.
|
||
return ( this.get = hookFn ).apply( this, arguments );
|
||
}
|
||
};
|
||
}
|
||
|
||
|
||
var cssPrefixes = [ "Webkit", "Moz", "ms" ],
|
||
emptyStyle = document.createElement( "div" ).style,
|
||
vendorProps = {};
|
||
|
||
// Return a vendor-prefixed property or undefined
|
||
function vendorPropName( name ) {
|
||
|
||
// Check for vendor prefixed names
|
||
var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
|
||
i = cssPrefixes.length;
|
||
|
||
while ( i-- ) {
|
||
name = cssPrefixes[ i ] + capName;
|
||
if ( name in emptyStyle ) {
|
||
return name;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
|
||
function finalPropName( name ) {
|
||
var final = jQuery.cssProps[ name ] || vendorProps[ name ];
|
||
|
||
if ( final ) {
|
||
return final;
|
||
}
|
||
if ( name in emptyStyle ) {
|
||
return name;
|
||
}
|
||
return vendorProps[ name ] = vendorPropName( name ) || name;
|
||
}
|
||
|
||
|
||
var
|
||
|
||
// Swappable if display is none or starts with table
|
||
// except "table", "table-cell", or "table-caption"
|
||
// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
|
||
rdisplayswap = /^(none|table(?!-c[ea]).+)/,
|
||
rcustomProp = /^--/,
|
||
cssShow = { position: "absolute", visibility: "hidden", display: "block" },
|
||
cssNormalTransform = {
|
||
letterSpacing: "0",
|
||
fontWeight: "400"
|
||
};
|
||
|
||
function setPositiveNumber( _elem, value, subtract ) {
|
||
|
||
// Any relative (+/-) values have already been
|
||
// normalized at this point
|
||
var matches = rcssNum.exec( value );
|
||
return matches ?
|
||
|
||
// Guard against undefined "subtract", e.g., when used as in cssHooks
|
||
Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
|
||
value;
|
||
}
|
||
|
||
function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
|
||
var i = dimension === "width" ? 1 : 0,
|
||
extra = 0,
|
||
delta = 0;
|
||
|
||
// Adjustment may not be necessary
|
||
if ( box === ( isBorderBox ? "border" : "content" ) ) {
|
||
return 0;
|
||
}
|
||
|
||
for ( ; i < 4; i += 2 ) {
|
||
|
||
// Both box models exclude margin
|
||
if ( box === "margin" ) {
|
||
delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
|
||
}
|
||
|
||
// If we get here with a content-box, we're seeking "padding" or "border" or "margin"
|
||
if ( !isBorderBox ) {
|
||
|
||
// Add padding
|
||
delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
||
|
||
// For "border" or "margin", add border
|
||
if ( box !== "padding" ) {
|
||
delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
||
|
||
// But still keep track of it otherwise
|
||
} else {
|
||
extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
||
}
|
||
|
||
// If we get here with a border-box (content + padding + border), we're seeking "content" or
|
||
// "padding" or "margin"
|
||
} else {
|
||
|
||
// For "content", subtract padding
|
||
if ( box === "content" ) {
|
||
delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
|
||
}
|
||
|
||
// For "content" or "padding", subtract border
|
||
if ( box !== "margin" ) {
|
||
delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Account for positive content-box scroll gutter when requested by providing computedVal
|
||
if ( !isBorderBox && computedVal >= 0 ) {
|
||
|
||
// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
|
||
// Assuming integer scroll gutter, subtract the rest and round down
|
||
delta += Math.max( 0, Math.ceil(
|
||
elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
|
||
computedVal -
|
||
delta -
|
||
extra -
|
||
0.5
|
||
|
||
// If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
|
||
// Use an explicit zero to avoid NaN (gh-3964)
|
||
) ) || 0;
|
||
}
|
||
|
||
return delta;
|
||
}
|
||
|
||
function getWidthOrHeight( elem, dimension, extra ) {
|
||
|
||
// Start with computed style
|
||
var styles = getStyles( elem ),
|
||
|
||
// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
|
||
// Fake content-box until we know it's needed to know the true value.
|
||
boxSizingNeeded = !support.boxSizingReliable() || extra,
|
||
isBorderBox = boxSizingNeeded &&
|
||
jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
|
||
valueIsBorderBox = isBorderBox,
|
||
|
||
val = curCSS( elem, dimension, styles ),
|
||
offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
|
||
|
||
// Support: Firefox <=54
|
||
// Return a confounding non-pixel value or feign ignorance, as appropriate.
|
||
if ( rnumnonpx.test( val ) ) {
|
||
if ( !extra ) {
|
||
return val;
|
||
}
|
||
val = "auto";
|
||
}
|
||
|
||
|
||
// Support: IE 9 - 11 only
|
||
// Use offsetWidth/offsetHeight for when box sizing is unreliable.
|
||
// In those cases, the computed value can be trusted to be border-box.
|
||
if ( ( !support.boxSizingReliable() && isBorderBox ||
|
||
|
||
// Support: IE 10 - 11+, Edge 15 - 18+
|
||
// IE/Edge misreport `getComputedStyle` of table rows with width/height
|
||
// set in CSS while `offset*` properties report correct values.
|
||
// Interestingly, in some cases IE 9 doesn't suffer from this issue.
|
||
!support.reliableTrDimensions() && nodeName( elem, "tr" ) ||
|
||
|
||
// Fall back to offsetWidth/offsetHeight when value is "auto"
|
||
// This happens for inline elements with no explicit setting (gh-3571)
|
||
val === "auto" ||
|
||
|
||
// Support: Android <=4.1 - 4.3 only
|
||
// Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
|
||
!parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
|
||
|
||
// Make sure the element is visible & connected
|
||
elem.getClientRects().length ) {
|
||
|
||
isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
|
||
|
||
// Where available, offsetWidth/offsetHeight approximate border box dimensions.
|
||
// Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
|
||
// retrieved value as a content box dimension.
|
||
valueIsBorderBox = offsetProp in elem;
|
||
if ( valueIsBorderBox ) {
|
||
val = elem[ offsetProp ];
|
||
}
|
||
}
|
||
|
||
// Normalize "" and auto
|
||
val = parseFloat( val ) || 0;
|
||
|
||
// Adjust for the element's box model
|
||
return ( val +
|
||
boxModelAdjustment(
|
||
elem,
|
||
dimension,
|
||
extra || ( isBorderBox ? "border" : "content" ),
|
||
valueIsBorderBox,
|
||
styles,
|
||
|
||
// Provide the current computed size to request scroll gutter calculation (gh-3589)
|
||
val
|
||
)
|
||
) + "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: {
|
||
"animationIterationCount": true,
|
||
"columnCount": true,
|
||
"fillOpacity": true,
|
||
"flexGrow": true,
|
||
"flexShrink": true,
|
||
"fontWeight": true,
|
||
"gridArea": true,
|
||
"gridColumn": true,
|
||
"gridColumnEnd": true,
|
||
"gridColumnStart": true,
|
||
"gridRow": true,
|
||
"gridRowEnd": true,
|
||
"gridRowStart": 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: {},
|
||
|
||
// 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 = camelCase( name ),
|
||
isCustomProp = rcustomProp.test( name ),
|
||
style = elem.style;
|
||
|
||
// Make sure that we're working with the right name. We don't
|
||
// want to query the value if it is a CSS custom property
|
||
// since they are user-defined.
|
||
if ( !isCustomProp ) {
|
||
name = finalPropName( origName );
|
||
}
|
||
|
||
// Gets hook for the prefixed version, then unprefixed version
|
||
hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
|
||
|
||
// Check if we're setting a value
|
||
if ( value !== undefined ) {
|
||
type = typeof value;
|
||
|
||
// Convert "+=" or "-=" to relative numbers (#7345)
|
||
if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
|
||
value = adjustCSS( elem, name, ret );
|
||
|
||
// Fixes bug #9237
|
||
type = "number";
|
||
}
|
||
|
||
// Make sure that null and NaN values aren't set (#7116)
|
||
if ( value == null || value !== value ) {
|
||
return;
|
||
}
|
||
|
||
// If a number was passed in, add the unit (except for certain CSS properties)
|
||
// The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
|
||
// "px" to a few hardcoded values.
|
||
if ( type === "number" && !isCustomProp ) {
|
||
value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
|
||
}
|
||
|
||
// background-* props affect original clone's values
|
||
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 ) {
|
||
|
||
if ( isCustomProp ) {
|
||
style.setProperty( name, value );
|
||
} else {
|
||
style[ name ] = value;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
// If a hook was provided get the non-computed value from there
|
||
if ( hooks && "get" in hooks &&
|
||
( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
|
||
|
||
return ret;
|
||
}
|
||
|
||
// Otherwise just get the value from the style object
|
||
return style[ name ];
|
||
}
|
||
},
|
||
|
||
css: function( elem, name, extra, styles ) {
|
||
var val, num, hooks,
|
||
origName = camelCase( name ),
|
||
isCustomProp = rcustomProp.test( name );
|
||
|
||
// Make sure that we're working with the right name. We don't
|
||
// want to modify the value if it is a CSS custom property
|
||
// since they are user-defined.
|
||
if ( !isCustomProp ) {
|
||
name = finalPropName( origName );
|
||
}
|
||
|
||
// Try prefixed name followed by the unprefixed name
|
||
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 ];
|
||
}
|
||
|
||
// Make numeric if forced or a qualifier was provided and val looks numeric
|
||
if ( extra === "" || extra ) {
|
||
num = parseFloat( val );
|
||
return extra === true || isFinite( num ) ? num || 0 : val;
|
||
}
|
||
|
||
return val;
|
||
}
|
||
} );
|
||
|
||
jQuery.each( [ "height", "width" ], function( _i, dimension ) {
|
||
jQuery.cssHooks[ dimension ] = {
|
||
get: function( elem, computed, extra ) {
|
||
if ( computed ) {
|
||
|
||
// Certain elements can have dimension info if we invisibly show them
|
||
// but it must have a current display style that would benefit
|
||
return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
|
||
|
||
// Support: Safari 8+
|
||
// Table columns in Safari have non-zero offsetWidth & zero
|
||
// getBoundingClientRect().width unless display is changed.
|
||
// Support: IE <=11 only
|
||
// Running getBoundingClientRect on a disconnected node
|
||
// in IE throws an error.
|
||
( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
|
||
swap( elem, cssShow, function() {
|
||
return getWidthOrHeight( elem, dimension, extra );
|
||
} ) :
|
||
getWidthOrHeight( elem, dimension, extra );
|
||
}
|
||
},
|
||
|
||
set: function( elem, value, extra ) {
|
||
var matches,
|
||
styles = getStyles( elem ),
|
||
|
||
// Only read styles.position if the test has a chance to fail
|
||
// to avoid forcing a reflow.
|
||
scrollboxSizeBuggy = !support.scrollboxSize() &&
|
||
styles.position === "absolute",
|
||
|
||
// To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
|
||
boxSizingNeeded = scrollboxSizeBuggy || extra,
|
||
isBorderBox = boxSizingNeeded &&
|
||
jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
|
||
subtract = extra ?
|
||
boxModelAdjustment(
|
||
elem,
|
||
dimension,
|
||
extra,
|
||
isBorderBox,
|
||
styles
|
||
) :
|
||
0;
|
||
|
||
// Account for unreliable border-box dimensions by comparing offset* to computed and
|
||
// faking a content-box to get border and padding (gh-3699)
|
||
if ( isBorderBox && scrollboxSizeBuggy ) {
|
||
subtract -= Math.ceil(
|
||
elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
|
||
parseFloat( styles[ dimension ] ) -
|
||
boxModelAdjustment( elem, dimension, "border", false, styles ) -
|
||
0.5
|
||
);
|
||
}
|
||
|
||
// Convert to pixels if value adjustment is needed
|
||
if ( subtract && ( matches = rcssNum.exec( value ) ) &&
|
||
( matches[ 3 ] || "px" ) !== "px" ) {
|
||
|
||
elem.style[ dimension ] = value;
|
||
value = jQuery.css( elem, dimension );
|
||
}
|
||
|
||
return setPositiveNumber( elem, value, subtract );
|
||
}
|
||
};
|
||
} );
|
||
|
||
jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
|
||
function( elem, computed ) {
|
||
if ( computed ) {
|
||
return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
|
||
elem.getBoundingClientRect().left -
|
||
swap( elem, { marginLeft: 0 }, function() {
|
||
return elem.getBoundingClientRect().left;
|
||
} )
|
||
) + "px";
|
||
}
|
||
}
|
||
);
|
||
|
||
// 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 ( prefix !== "margin" ) {
|
||
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 ( Array.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 );
|
||
}
|
||
} );
|
||
|
||
|
||
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 || jQuery.easing._default;
|
||
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;
|
||
|
||
// Use a property on the element directly when it is not a DOM element,
|
||
// or when there is no matching style property that exists.
|
||
if ( tween.elem.nodeType !== 1 ||
|
||
tween.elem[ tween.prop ] != null && 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.
|
||
// 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 available and use plain properties where available.
|
||
if ( jQuery.fx.step[ tween.prop ] ) {
|
||
jQuery.fx.step[ tween.prop ]( tween );
|
||
} else if ( tween.elem.nodeType === 1 && (
|
||
jQuery.cssHooks[ tween.prop ] ||
|
||
tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
|
||
jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
|
||
} else {
|
||
tween.elem[ tween.prop ] = tween.now;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
// Support: IE <=9 only
|
||
// 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;
|
||
},
|
||
_default: "swing"
|
||
};
|
||
|
||
jQuery.fx = Tween.prototype.init;
|
||
|
||
// Back compat <1.8 extension point
|
||
jQuery.fx.step = {};
|
||
|
||
|
||
|
||
|
||
var
|
||
fxNow, inProgress,
|
||
rfxtypes = /^(?:toggle|show|hide)$/,
|
||
rrun = /queueHooks$/;
|
||
|
||
function schedule() {
|
||
if ( inProgress ) {
|
||
if ( document.hidden === false && window.requestAnimationFrame ) {
|
||
window.requestAnimationFrame( schedule );
|
||
} else {
|
||
window.setTimeout( schedule, jQuery.fx.interval );
|
||
}
|
||
|
||
jQuery.fx.tick();
|
||
}
|
||
}
|
||
|
||
// Animations created synchronously will run synchronously
|
||
function createFxNow() {
|
||
window.setTimeout( function() {
|
||
fxNow = undefined;
|
||
} );
|
||
return ( fxNow = Date.now() );
|
||
}
|
||
|
||
// Generate parameters to create a standard animation
|
||
function genFx( type, includeWidth ) {
|
||
var which,
|
||
i = 0,
|
||
attrs = { height: type };
|
||
|
||
// If we include width, step value is 1 to do all cssExpand values,
|
||
// otherwise 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 = ( Animation.tweeners[ prop ] || [] ).concat( Animation.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 ) {
|
||
var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
|
||
isBox = "width" in props || "height" in props,
|
||
anim = this,
|
||
orig = {},
|
||
style = elem.style,
|
||
hidden = elem.nodeType && isHiddenWithinTree( elem ),
|
||
dataShow = dataPriv.get( elem, "fxshow" );
|
||
|
||
// Queue-skipping animations hijack the fx hooks
|
||
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() {
|
||
|
||
// Ensure the complete handler is called before this completes
|
||
anim.always( function() {
|
||
hooks.unqueued--;
|
||
if ( !jQuery.queue( elem, "fx" ).length ) {
|
||
hooks.empty.fire();
|
||
}
|
||
} );
|
||
} );
|
||
}
|
||
|
||
// Detect show/hide animations
|
||
for ( prop in props ) {
|
||
value = props[ prop ];
|
||
if ( rfxtypes.test( value ) ) {
|
||
delete props[ prop ];
|
||
toggle = toggle || value === "toggle";
|
||
if ( value === ( hidden ? "hide" : "show" ) ) {
|
||
|
||
// Pretend to be hidden if this is a "show" and
|
||
// there is still data from a stopped show/hide
|
||
if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
|
||
hidden = true;
|
||
|
||
// Ignore all other no-op show/hide data
|
||
} else {
|
||
continue;
|
||
}
|
||
}
|
||
orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
|
||
}
|
||
}
|
||
|
||
// Bail out if this is a no-op like .hide().hide()
|
||
propTween = !jQuery.isEmptyObject( props );
|
||
if ( !propTween && jQuery.isEmptyObject( orig ) ) {
|
||
return;
|
||
}
|
||
|
||
// Restrict "overflow" and "display" styles during box animations
|
||
if ( isBox && elem.nodeType === 1 ) {
|
||
|
||
// Support: IE <=9 - 11, Edge 12 - 15
|
||
// Record all 3 overflow attributes because IE does not infer the shorthand
|
||
// from identically-valued overflowX and overflowY and Edge just mirrors
|
||
// the overflowX value there.
|
||
opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
|
||
|
||
// Identify a display type, preferring old show/hide data over the CSS cascade
|
||
restoreDisplay = dataShow && dataShow.display;
|
||
if ( restoreDisplay == null ) {
|
||
restoreDisplay = dataPriv.get( elem, "display" );
|
||
}
|
||
display = jQuery.css( elem, "display" );
|
||
if ( display === "none" ) {
|
||
if ( restoreDisplay ) {
|
||
display = restoreDisplay;
|
||
} else {
|
||
|
||
// Get nonempty value(s) by temporarily forcing visibility
|
||
showHide( [ elem ], true );
|
||
restoreDisplay = elem.style.display || restoreDisplay;
|
||
display = jQuery.css( elem, "display" );
|
||
showHide( [ elem ] );
|
||
}
|
||
}
|
||
|
||
// Animate inline elements as inline-block
|
||
if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
|
||
if ( jQuery.css( elem, "float" ) === "none" ) {
|
||
|
||
// Restore the original display value at the end of pure show/hide animations
|
||
if ( !propTween ) {
|
||
anim.done( function() {
|
||
style.display = restoreDisplay;
|
||
} );
|
||
if ( restoreDisplay == null ) {
|
||
display = style.display;
|
||
restoreDisplay = display === "none" ? "" : display;
|
||
}
|
||
}
|
||
style.display = "inline-block";
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( opts.overflow ) {
|
||
style.overflow = "hidden";
|
||
anim.always( function() {
|
||
style.overflow = opts.overflow[ 0 ];
|
||
style.overflowX = opts.overflow[ 1 ];
|
||
style.overflowY = opts.overflow[ 2 ];
|
||
} );
|
||
}
|
||
|
||
// Implement show/hide animations
|
||
propTween = false;
|
||
for ( prop in orig ) {
|
||
|
||
// General show/hide setup for this element animation
|
||
if ( !propTween ) {
|
||
if ( dataShow ) {
|
||
if ( "hidden" in dataShow ) {
|
||
hidden = dataShow.hidden;
|
||
}
|
||
} else {
|
||
dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
|
||
}
|
||
|
||
// Store hidden/visible for toggle so `.stop().toggle()` "reverses"
|
||
if ( toggle ) {
|
||
dataShow.hidden = !hidden;
|
||
}
|
||
|
||
// Show elements before animating them
|
||
if ( hidden ) {
|
||
showHide( [ elem ], true );
|
||
}
|
||
|
||
/* eslint-disable no-loop-func */
|
||
|
||
anim.done( function() {
|
||
|
||
/* eslint-enable no-loop-func */
|
||
|
||
// The final step of a "hide" animation is actually hiding the element
|
||
if ( !hidden ) {
|
||
showHide( [ elem ] );
|
||
}
|
||
dataPriv.remove( elem, "fxshow" );
|
||
for ( prop in orig ) {
|
||
jQuery.style( elem, prop, orig[ prop ] );
|
||
}
|
||
} );
|
||
}
|
||
|
||
// Per-property setup
|
||
propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
|
||
if ( !( prop in dataShow ) ) {
|
||
dataShow[ prop ] = propTween.start;
|
||
if ( hidden ) {
|
||
propTween.end = propTween.start;
|
||
propTween.start = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function propFilter( props, specialEasing ) {
|
||
var index, name, easing, value, hooks;
|
||
|
||
// camelCase, specialEasing and expand cssHook pass
|
||
for ( index in props ) {
|
||
name = camelCase( index );
|
||
easing = specialEasing[ name ];
|
||
value = props[ index ];
|
||
if ( Array.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 won't overwrite existing keys.
|
||
// Reusing 'index' 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 = Animation.prefilters.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 ),
|
||
|
||
// Support: Android 2.3 only
|
||
// 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 there's more to do, yield
|
||
if ( percent < 1 && length ) {
|
||
return remaining;
|
||
}
|
||
|
||
// If this was an empty animation, synthesize a final progress notification
|
||
if ( !length ) {
|
||
deferred.notifyWith( elem, [ animation, 1, 0 ] );
|
||
}
|
||
|
||
// Resolve the animation and report its conclusion
|
||
deferred.resolveWith( elem, [ animation ] );
|
||
return false;
|
||
},
|
||
animation = deferred.promise( {
|
||
elem: elem,
|
||
props: jQuery.extend( {}, properties ),
|
||
opts: jQuery.extend( true, {
|
||
specialEasing: {},
|
||
easing: jQuery.easing._default
|
||
}, 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.notifyWith( elem, [ animation, 1, 0 ] );
|
||
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 = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
|
||
if ( result ) {
|
||
if ( isFunction( result.stop ) ) {
|
||
jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
|
||
result.stop.bind( result );
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
jQuery.map( props, createTween, animation );
|
||
|
||
if ( isFunction( animation.opts.start ) ) {
|
||
animation.opts.start.call( elem, animation );
|
||
}
|
||
|
||
// Attach callbacks from options
|
||
animation
|
||
.progress( animation.opts.progress )
|
||
.done( animation.opts.done, animation.opts.complete )
|
||
.fail( animation.opts.fail )
|
||
.always( animation.opts.always );
|
||
|
||
jQuery.fx.timer(
|
||
jQuery.extend( tick, {
|
||
elem: elem,
|
||
anim: animation,
|
||
queue: animation.opts.queue
|
||
} )
|
||
);
|
||
|
||
return animation;
|
||
}
|
||
|
||
jQuery.Animation = jQuery.extend( Animation, {
|
||
|
||
tweeners: {
|
||
"*": [ function( prop, value ) {
|
||
var tween = this.createTween( prop, value );
|
||
adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
|
||
return tween;
|
||
} ]
|
||
},
|
||
|
||
tweener: function( props, callback ) {
|
||
if ( isFunction( props ) ) {
|
||
callback = props;
|
||
props = [ "*" ];
|
||
} else {
|
||
props = props.match( rnothtmlwhite );
|
||
}
|
||
|
||
var prop,
|
||
index = 0,
|
||
length = props.length;
|
||
|
||
for ( ; index < length; index++ ) {
|
||
prop = props[ index ];
|
||
Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
|
||
Animation.tweeners[ prop ].unshift( callback );
|
||
}
|
||
},
|
||
|
||
prefilters: [ defaultPrefilter ],
|
||
|
||
prefilter: function( callback, prepend ) {
|
||
if ( prepend ) {
|
||
Animation.prefilters.unshift( callback );
|
||
} else {
|
||
Animation.prefilters.push( callback );
|
||
}
|
||
}
|
||
} );
|
||
|
||
jQuery.speed = function( speed, easing, fn ) {
|
||
var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
|
||
complete: fn || !fn && easing ||
|
||
isFunction( speed ) && speed,
|
||
duration: speed,
|
||
easing: fn && easing || easing && !isFunction( easing ) && easing
|
||
};
|
||
|
||
// Go to the end state if fx are off
|
||
if ( jQuery.fx.off ) {
|
||
opt.duration = 0;
|
||
|
||
} else {
|
||
if ( typeof opt.duration !== "number" ) {
|
||
if ( opt.duration in jQuery.fx.speeds ) {
|
||
opt.duration = jQuery.fx.speeds[ opt.duration ];
|
||
|
||
} else {
|
||
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 ( 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( isHiddenWithinTree ).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 || dataPriv.get( this, "finish" ) ) {
|
||
anim.stop( true );
|
||
}
|
||
};
|
||
doAnimation.finish = doAnimation;
|
||
|
||
return empty || optall.queue === false ?
|
||
this.each( doAnimation ) :
|
||
this.queue( optall.queue, doAnimation );
|
||
},
|
||
stop: function( type, clearQueue, gotoEnd ) {
|
||
var stopQueue = function( hooks ) {
|
||
var stop = hooks.stop;
|
||
delete hooks.stop;
|
||
stop( gotoEnd );
|
||
};
|
||
|
||
if ( typeof type !== "string" ) {
|
||
gotoEnd = clearQueue;
|
||
clearQueue = type;
|
||
type = undefined;
|
||
}
|
||
if ( clearQueue ) {
|
||
this.queue( type || "fx", [] );
|
||
}
|
||
|
||
return this.each( function() {
|
||
var dequeue = true,
|
||
index = type != null && type + "queueHooks",
|
||
timers = jQuery.timers,
|
||
data = dataPriv.get( this );
|
||
|
||
if ( index ) {
|
||
if ( data[ index ] && data[ index ].stop ) {
|
||
stopQueue( data[ index ] );
|
||
}
|
||
} else {
|
||
for ( index in data ) {
|
||
if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
|
||
stopQueue( data[ index ] );
|
||
}
|
||
}
|
||
}
|
||
|
||
for ( index = timers.length; index--; ) {
|
||
if ( timers[ index ].elem === this &&
|
||
( type == null || timers[ index ].queue === type ) ) {
|
||
|
||
timers[ index ].anim.stop( gotoEnd );
|
||
dequeue = false;
|
||
timers.splice( index, 1 );
|
||
}
|
||
}
|
||
|
||
// Start the next in the queue if the last step wasn't forced.
|
||
// Timers currently will call their complete callbacks, which
|
||
// will dequeue but only if they were gotoEnd.
|
||
if ( dequeue || !gotoEnd ) {
|
||
jQuery.dequeue( this, type );
|
||
}
|
||
} );
|
||
},
|
||
finish: function( type ) {
|
||
if ( type !== false ) {
|
||
type = type || "fx";
|
||
}
|
||
return this.each( function() {
|
||
var index,
|
||
data = dataPriv.get( this ),
|
||
queue = data[ type + "queue" ],
|
||
hooks = data[ type + "queueHooks" ],
|
||
timers = jQuery.timers,
|
||
length = queue ? queue.length : 0;
|
||
|
||
// Enable finishing flag on private data
|
||
data.finish = true;
|
||
|
||
// Empty the queue first
|
||
jQuery.queue( this, type, [] );
|
||
|
||
if ( hooks && hooks.stop ) {
|
||
hooks.stop.call( this, true );
|
||
}
|
||
|
||
// Look for any active animations, and finish them
|
||
for ( index = timers.length; index--; ) {
|
||
if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
|
||
timers[ index ].anim.stop( true );
|
||
timers.splice( index, 1 );
|
||
}
|
||
}
|
||
|
||
// Look for any animations in the old queue and finish them
|
||
for ( index = 0; index < length; index++ ) {
|
||
if ( queue[ index ] && queue[ index ].finish ) {
|
||
queue[ index ].finish.call( this );
|
||
}
|
||
}
|
||
|
||
// Turn off finishing flag
|
||
delete data.finish;
|
||
} );
|
||
}
|
||
} );
|
||
|
||
jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) {
|
||
var cssFn = jQuery.fn[ name ];
|
||
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
||
return speed == null || typeof speed === "boolean" ?
|
||
cssFn.apply( this, arguments ) :
|
||
this.animate( genFx( name, true ), speed, easing, callback );
|
||
};
|
||
} );
|
||
|
||
// Generate shortcuts for custom animations
|
||
jQuery.each( {
|
||
slideDown: genFx( "show" ),
|
||
slideUp: genFx( "hide" ),
|
||
slideToggle: genFx( "toggle" ),
|
||
fadeIn: { opacity: "show" },
|
||
fadeOut: { opacity: "hide" },
|
||
fadeToggle: { opacity: "toggle" }
|
||
}, function( name, props ) {
|
||
jQuery.fn[ name ] = function( speed, easing, callback ) {
|
||
return this.animate( props, speed, easing, callback );
|
||
};
|
||
} );
|
||
|
||
jQuery.timers = [];
|
||
jQuery.fx.tick = function() {
|
||
var timer,
|
||
i = 0,
|
||
timers = jQuery.timers;
|
||
|
||
fxNow = Date.now();
|
||
|
||
for ( ; i < timers.length; i++ ) {
|
||
timer = timers[ i ];
|
||
|
||
// Run the timer and safely remove it when done (allowing for external removal)
|
||
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 );
|
||
jQuery.fx.start();
|
||
};
|
||
|
||
jQuery.fx.interval = 13;
|
||
jQuery.fx.start = function() {
|
||
if ( inProgress ) {
|
||
return;
|
||
}
|
||
|
||
inProgress = true;
|
||
schedule();
|
||
};
|
||
|
||
jQuery.fx.stop = function() {
|
||
inProgress = null;
|
||
};
|
||
|
||
jQuery.fx.speeds = {
|
||
slow: 600,
|
||
fast: 200,
|
||
|
||
// Default speed
|
||
_default: 400
|
||
};
|
||
|
||
|
||
// Based off of the plugin by Clint Helfers, with permission.
|
||
// https://web.archive.org/web/20100324014747/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 = window.setTimeout( next, time );
|
||
hooks.stop = function() {
|
||
window.clearTimeout( timeout );
|
||
};
|
||
} );
|
||
};
|
||
|
||
|
||
( function() {
|
||
var input = document.createElement( "input" ),
|
||
select = document.createElement( "select" ),
|
||
opt = select.appendChild( document.createElement( "option" ) );
|
||
|
||
input.type = "checkbox";
|
||
|
||
// Support: Android <=4.3 only
|
||
// Default value for a checkbox should be "on"
|
||
support.checkOn = input.value !== "";
|
||
|
||
// Support: IE <=11 only
|
||
// Must access selectedIndex to make default options select
|
||
support.optSelected = opt.selected;
|
||
|
||
// Support: IE <=11 only
|
||
// An input loses its value after becoming a radio
|
||
input = document.createElement( "input" );
|
||
input.value = "t";
|
||
input.type = "radio";
|
||
support.radioValue = input.value === "t";
|
||
} )();
|
||
|
||
|
||
var boolHook,
|
||
attrHandle = jQuery.expr.attrHandle;
|
||
|
||
jQuery.fn.extend( {
|
||
attr: function( name, value ) {
|
||
return access( this, jQuery.attr, name, value, arguments.length > 1 );
|
||
},
|
||
|
||
removeAttr: function( name ) {
|
||
return this.each( function() {
|
||
jQuery.removeAttr( this, name );
|
||
} );
|
||
}
|
||
} );
|
||
|
||
jQuery.extend( {
|
||
attr: function( elem, name, value ) {
|
||
var ret, hooks,
|
||
nType = elem.nodeType;
|
||
|
||
// Don't get/set attributes on text, comment and attribute nodes
|
||
if ( nType === 3 || nType === 8 || nType === 2 ) {
|
||
return;
|
||
}
|
||
|
||
// Fallback to prop when attributes are not supported
|
||
if ( typeof elem.getAttribute === "undefined" ) {
|
||
return jQuery.prop( elem, name, value );
|
||
}
|
||
|
||
// Attribute hooks are determined by the lowercase version
|
||
// Grab necessary hook if one is defined
|
||
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
|
||
hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
|
||
( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
|
||
}
|
||
|
||
if ( value !== undefined ) {
|
||
if ( value === null ) {
|
||
jQuery.removeAttr( elem, name );
|
||
return;
|
||
}
|
||
|
||
if ( hooks && "set" in hooks &&
|
||
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
|
||
return ret;
|
||
}
|
||
|
||
elem.setAttribute( name, value + "" );
|
||
return value;
|
||
}
|
||
|
||
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
|
||
return ret;
|
||
}
|
||
|
||
ret = jQuery.find.attr( elem, name );
|
||
|
||
// Non-existent attributes return null, we normalize to undefined
|
||
return ret == null ? undefined : ret;
|
||
},
|
||
|
||
attrHooks: {
|
||
type: {
|
||
set: function( elem, value ) {
|
||
if ( !support.radioValue && value === "radio" &&
|
||
nodeName( elem, "input" ) ) {
|
||
var val = elem.value;
|
||
elem.setAttribute( "type", value );
|
||
if ( val ) {
|
||
elem.value = val;
|
||
}
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
removeAttr: function( elem, value ) {
|
||
var name,
|
||
i = 0,
|
||
|
||
// Attribute names can contain non-HTML whitespace characters
|
||
// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
|
||
attrNames = value && value.match( rnothtmlwhite );
|
||
|
||
if ( attrNames && elem.nodeType === 1 ) {
|
||
while ( ( name = attrNames[ i++ ] ) ) {
|
||
elem.removeAttribute( name );
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
|
||
// Hooks for boolean attributes
|
||
boolHook = {
|
||
set: function( elem, value, name ) {
|
||
if ( value === false ) {
|
||
|
||
// Remove boolean attributes when set to false
|
||
jQuery.removeAttr( elem, name );
|
||
} else {
|
||
elem.setAttribute( name, name );
|
||
}
|
||
return name;
|
||
}
|
||
};
|
||
|
||
jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) {
|
||
var getter = attrHandle[ name ] || jQuery.find.attr;
|
||
|
||
attrHandle[ name ] = function( elem, name, isXML ) {
|
||
var ret, handle,
|
||
lowercaseName = name.toLowerCase();
|
||
|
||
if ( !isXML ) {
|
||
|
||
// Avoid an infinite loop by temporarily removing this function from the getter
|
||
handle = attrHandle[ lowercaseName ];
|
||
attrHandle[ lowercaseName ] = ret;
|
||
ret = getter( elem, name, isXML ) != null ?
|
||
lowercaseName :
|
||
null;
|
||
attrHandle[ lowercaseName ] = handle;
|
||
}
|
||
return ret;
|
||
};
|
||
} );
|
||
|
||
|
||
|
||
|
||
var rfocusable = /^(?:input|select|textarea|button)$/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 ) {
|
||
return this.each( function() {
|
||
delete this[ jQuery.propFix[ name ] || name ];
|
||
} );
|
||
}
|
||
} );
|
||
|
||
jQuery.extend( {
|
||
prop: function( elem, name, value ) {
|
||
var ret, hooks,
|
||
nType = elem.nodeType;
|
||
|
||
// Don't get/set properties on text, comment and attribute nodes
|
||
if ( nType === 3 || nType === 8 || nType === 2 ) {
|
||
return;
|
||
}
|
||
|
||
if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
|
||
|
||
// Fix name and attach hooks
|
||
name = jQuery.propFix[ name ] || name;
|
||
hooks = jQuery.propHooks[ name ];
|
||
}
|
||
|
||
if ( value !== undefined ) {
|
||
if ( hooks && "set" in hooks &&
|
||
( ret = hooks.set( elem, value, name ) ) !== undefined ) {
|
||
return ret;
|
||
}
|
||
|
||
return ( elem[ name ] = value );
|
||
}
|
||
|
||
if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
|
||
return ret;
|
||
}
|
||
|
||
return elem[ name ];
|
||
},
|
||
|
||
propHooks: {
|
||
tabIndex: {
|
||
get: function( elem ) {
|
||
|
||
// Support: IE <=9 - 11 only
|
||
// elem.tabIndex doesn't always return the
|
||
// correct value when it hasn't been explicitly set
|
||
// https://web.archive.org/web/20141116233347/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" );
|
||
|
||
if ( tabindex ) {
|
||
return parseInt( tabindex, 10 );
|
||
}
|
||
|
||
if (
|
||
rfocusable.test( elem.nodeName ) ||
|
||
rclickable.test( elem.nodeName ) &&
|
||
elem.href
|
||
) {
|
||
return 0;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
}
|
||
},
|
||
|
||
propFix: {
|
||
"for": "htmlFor",
|
||
"class": "className"
|
||
}
|
||
} );
|
||
|
||
// Support: IE <=11 only
|
||
// Accessing the selectedIndex property
|
||
// forces the browser to respect setting selected
|
||
// on the option
|
||
// The getter ensures a default option is selected
|
||
// when in an optgroup
|
||
// eslint rule "no-unused-expressions" is disabled for this code
|
||
// since it considers such accessions noop
|
||
if ( !support.optSelected ) {
|
||
jQuery.propHooks.selected = {
|
||
get: function( elem ) {
|
||
|
||
/* eslint no-unused-expressions: "off" */
|
||
|
||
var parent = elem.parentNode;
|
||
if ( parent && parent.parentNode ) {
|
||
parent.parentNode.selectedIndex;
|
||
}
|
||
return null;
|
||
},
|
||
set: function( elem ) {
|
||
|
||
/* eslint no-unused-expressions: "off" */
|
||
|
||
var parent = elem.parentNode;
|
||
if ( parent ) {
|
||
parent.selectedIndex;
|
||
|
||
if ( parent.parentNode ) {
|
||
parent.parentNode.selectedIndex;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
jQuery.each( [
|
||
"tabIndex",
|
||
"readOnly",
|
||
"maxLength",
|
||
"cellSpacing",
|
||
"cellPadding",
|
||
"rowSpan",
|
||
"colSpan",
|
||
"useMap",
|
||
"frameBorder",
|
||
"contentEditable"
|
||
], function() {
|
||
jQuery.propFix[ this.toLowerCase() ] = this;
|
||
} );
|
||
|
||
|
||
|
||
|
||
// Strip and collapse whitespace according to HTML spec
|
||
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
|
||
function stripAndCollapse( value ) {
|
||
var tokens = value.match( rnothtmlwhite ) || [];
|
||
return tokens.join( " " );
|
||
}
|
||
|
||
|
||
function getClass( elem ) {
|
||
return elem.getAttribute && elem.getAttribute( "class" ) || "";
|
||
}
|
||
|
||
function classesToArray( value ) {
|
||
if ( Array.isArray( value ) ) {
|
||
return value;
|
||
}
|
||
if ( typeof value === "string" ) {
|
||
return value.match( rnothtmlwhite ) || [];
|
||
}
|
||
return [];
|
||
}
|
||
|
||
jQuery.fn.extend( {
|
||
addClass: function( value ) {
|
||
var classes, elem, cur, curValue, clazz, j, finalValue,
|
||
i = 0;
|
||
|
||
if ( isFunction( value ) ) {
|
||
return this.each( function( j ) {
|
||
jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
|
||
} );
|
||
}
|
||
|
||
classes = classesToArray( value );
|
||
|
||
if ( classes.length ) {
|
||
while ( ( elem = this[ i++ ] ) ) {
|
||
curValue = getClass( elem );
|
||
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
|
||
|
||
if ( cur ) {
|
||
j = 0;
|
||
while ( ( clazz = classes[ j++ ] ) ) {
|
||
if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
|
||
cur += clazz + " ";
|
||
}
|
||
}
|
||
|
||
// Only assign if different to avoid unneeded rendering.
|
||
finalValue = stripAndCollapse( cur );
|
||
if ( curValue !== finalValue ) {
|
||
elem.setAttribute( "class", finalValue );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
removeClass: function( value ) {
|
||
var classes, elem, cur, curValue, clazz, j, finalValue,
|
||
i = 0;
|
||
|
||
if ( isFunction( value ) ) {
|
||
return this.each( function( j ) {
|
||
jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
|
||
} );
|
||
}
|
||
|
||
if ( !arguments.length ) {
|
||
return this.attr( "class", "" );
|
||
}
|
||
|
||
classes = classesToArray( value );
|
||
|
||
if ( classes.length ) {
|
||
while ( ( elem = this[ i++ ] ) ) {
|
||
curValue = getClass( elem );
|
||
|
||
// This expression is here for better compressibility (see addClass)
|
||
cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
|
||
|
||
if ( cur ) {
|
||
j = 0;
|
||
while ( ( clazz = classes[ j++ ] ) ) {
|
||
|
||
// Remove *all* instances
|
||
while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
|
||
cur = cur.replace( " " + clazz + " ", " " );
|
||
}
|
||
}
|
||
|
||
// Only assign if different to avoid unneeded rendering.
|
||
finalValue = stripAndCollapse( cur );
|
||
if ( curValue !== finalValue ) {
|
||
elem.setAttribute( "class", finalValue );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
toggleClass: function( value, stateVal ) {
|
||
var type = typeof value,
|
||
isValidValue = type === "string" || Array.isArray( value );
|
||
|
||
if ( typeof stateVal === "boolean" && isValidValue ) {
|
||
return stateVal ? this.addClass( value ) : this.removeClass( value );
|
||
}
|
||
|
||
if ( isFunction( value ) ) {
|
||
return this.each( function( i ) {
|
||
jQuery( this ).toggleClass(
|
||
value.call( this, i, getClass( this ), stateVal ),
|
||
stateVal
|
||
);
|
||
} );
|
||
}
|
||
|
||
return this.each( function() {
|
||
var className, i, self, classNames;
|
||
|
||
if ( isValidValue ) {
|
||
|
||
// Toggle individual class names
|
||
i = 0;
|
||
self = jQuery( this );
|
||
classNames = classesToArray( value );
|
||
|
||
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 ( value === undefined || type === "boolean" ) {
|
||
className = getClass( this );
|
||
if ( className ) {
|
||
|
||
// Store className if set
|
||
dataPriv.set( this, "__className__", 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.
|
||
if ( this.setAttribute ) {
|
||
this.setAttribute( "class",
|
||
className || value === false ?
|
||
"" :
|
||
dataPriv.get( this, "__className__" ) || ""
|
||
);
|
||
}
|
||
}
|
||
} );
|
||
},
|
||
|
||
hasClass: function( selector ) {
|
||
var className, elem,
|
||
i = 0;
|
||
|
||
className = " " + selector + " ";
|
||
while ( ( elem = this[ i++ ] ) ) {
|
||
if ( elem.nodeType === 1 &&
|
||
( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
var rreturn = /\r/g;
|
||
|
||
jQuery.fn.extend( {
|
||
val: function( value ) {
|
||
var hooks, ret, valueIsFunction,
|
||
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;
|
||
|
||
// Handle most common string cases
|
||
if ( typeof ret === "string" ) {
|
||
return ret.replace( rreturn, "" );
|
||
}
|
||
|
||
// Handle cases where value is null/undef or number
|
||
return ret == null ? "" : ret;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
valueIsFunction = isFunction( value );
|
||
|
||
return this.each( function( i ) {
|
||
var val;
|
||
|
||
if ( this.nodeType !== 1 ) {
|
||
return;
|
||
}
|
||
|
||
if ( valueIsFunction ) {
|
||
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 ( Array.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 :
|
||
|
||
// Support: IE <=10 - 11 only
|
||
// option.text throws exceptions (#14686, #14858)
|
||
// Strip and collapse whitespace
|
||
// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
|
||
stripAndCollapse( jQuery.text( elem ) );
|
||
}
|
||
},
|
||
select: {
|
||
get: function( elem ) {
|
||
var value, option, i,
|
||
options = elem.options,
|
||
index = elem.selectedIndex,
|
||
one = elem.type === "select-one",
|
||
values = one ? null : [],
|
||
max = one ? index + 1 : options.length;
|
||
|
||
if ( index < 0 ) {
|
||
i = max;
|
||
|
||
} else {
|
||
i = one ? index : 0;
|
||
}
|
||
|
||
// Loop through all the selected options
|
||
for ( ; i < max; i++ ) {
|
||
option = options[ i ];
|
||
|
||
// Support: IE <=9 only
|
||
// IE8-9 doesn't update selected after form reset (#2551)
|
||
if ( ( option.selected || i === index ) &&
|
||
|
||
// Don't return options that are disabled or in a disabled optgroup
|
||
!option.disabled &&
|
||
( !option.parentNode.disabled ||
|
||
!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 ];
|
||
|
||
/* eslint-disable no-cond-assign */
|
||
|
||
if ( option.selected =
|
||
jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
|
||
) {
|
||
optionSet = true;
|
||
}
|
||
|
||
/* eslint-enable no-cond-assign */
|
||
}
|
||
|
||
// Force browsers to behave consistently when non-matching value is set
|
||
if ( !optionSet ) {
|
||
elem.selectedIndex = -1;
|
||
}
|
||
return values;
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
|
||
// Radios and checkboxes getter/setter
|
||
jQuery.each( [ "radio", "checkbox" ], function() {
|
||
jQuery.valHooks[ this ] = {
|
||
set: function( elem, value ) {
|
||
if ( Array.isArray( value ) ) {
|
||
return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
|
||
}
|
||
}
|
||
};
|
||
if ( !support.checkOn ) {
|
||
jQuery.valHooks[ this ].get = function( elem ) {
|
||
return elem.getAttribute( "value" ) === null ? "on" : elem.value;
|
||
};
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
// Return jQuery for attributes-only inclusion
|
||
|
||
|
||
support.focusin = "onfocusin" in window;
|
||
|
||
|
||
var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
|
||
stopPropagationCallback = function( e ) {
|
||
e.stopPropagation();
|
||
};
|
||
|
||
jQuery.extend( jQuery.event, {
|
||
|
||
trigger: function( event, data, elem, onlyHandlers ) {
|
||
|
||
var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
|
||
eventPath = [ elem || document ],
|
||
type = hasOwn.call( event, "type" ) ? event.type : event,
|
||
namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
|
||
|
||
cur = lastElement = 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( "." ) > -1 ) {
|
||
|
||
// 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.rnamespace = 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 && !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() ) {
|
||
lastElement = cur;
|
||
event.type = i > 1 ?
|
||
bubbleType :
|
||
special.bindType || type;
|
||
|
||
// jQuery handler
|
||
handle = (
|
||
dataPriv.get( cur, "events" ) || Object.create( null )
|
||
)[ event.type ] &&
|
||
dataPriv.get( cur, "handle" );
|
||
if ( handle ) {
|
||
handle.apply( cur, data );
|
||
}
|
||
|
||
// Native handler
|
||
handle = ontype && cur[ ontype ];
|
||
if ( handle && handle.apply && 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 ) &&
|
||
acceptData( elem ) ) {
|
||
|
||
// Call a native DOM method on the target with the same name as the event.
|
||
// Don't do default actions on window, that's where global variables be (#6170)
|
||
if ( ontype && isFunction( elem[ type ] ) && !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;
|
||
|
||
if ( event.isPropagationStopped() ) {
|
||
lastElement.addEventListener( type, stopPropagationCallback );
|
||
}
|
||
|
||
elem[ type ]();
|
||
|
||
if ( event.isPropagationStopped() ) {
|
||
lastElement.removeEventListener( type, stopPropagationCallback );
|
||
}
|
||
|
||
jQuery.event.triggered = undefined;
|
||
|
||
if ( tmp ) {
|
||
elem[ ontype ] = tmp;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return event.result;
|
||
},
|
||
|
||
// Piggyback on a donor event to simulate a different one
|
||
// Used only for `focus(in | out)` events
|
||
simulate: function( type, elem, event ) {
|
||
var e = jQuery.extend(
|
||
new jQuery.Event(),
|
||
event,
|
||
{
|
||
type: type,
|
||
isSimulated: true
|
||
}
|
||
);
|
||
|
||
jQuery.event.trigger( e, null, elem );
|
||
}
|
||
|
||
} );
|
||
|
||
jQuery.fn.extend( {
|
||
|
||
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 );
|
||
}
|
||
}
|
||
} );
|
||
|
||
|
||
// Support: Firefox <=44
|
||
// Firefox doesn't have focus(in | out) events
|
||
// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
|
||
//
|
||
// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
|
||
// focus(in | out) events fire after focus & blur events,
|
||
// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
|
||
// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
|
||
if ( !support.focusin ) {
|
||
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 ) );
|
||
};
|
||
|
||
jQuery.event.special[ fix ] = {
|
||
setup: function() {
|
||
|
||
// Handle: regular nodes (via `this.ownerDocument`), window
|
||
// (via `this.document`) & document (via `this`).
|
||
var doc = this.ownerDocument || this.document || this,
|
||
attaches = dataPriv.access( doc, fix );
|
||
|
||
if ( !attaches ) {
|
||
doc.addEventListener( orig, handler, true );
|
||
}
|
||
dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
|
||
},
|
||
teardown: function() {
|
||
var doc = this.ownerDocument || this.document || this,
|
||
attaches = dataPriv.access( doc, fix ) - 1;
|
||
|
||
if ( !attaches ) {
|
||
doc.removeEventListener( orig, handler, true );
|
||
dataPriv.remove( doc, fix );
|
||
|
||
} else {
|
||
dataPriv.access( doc, fix, attaches );
|
||
}
|
||
}
|
||
};
|
||
} );
|
||
}
|
||
var location = window.location;
|
||
|
||
var nonce = { guid: Date.now() };
|
||
|
||
var rquery = ( /\?/ );
|
||
|
||
|
||
|
||
// Cross-browser xml parsing
|
||
jQuery.parseXML = function( data ) {
|
||
var xml;
|
||
if ( !data || typeof data !== "string" ) {
|
||
return null;
|
||
}
|
||
|
||
// Support: IE 9 - 11 only
|
||
// IE throws on parseFromString with invalid input.
|
||
try {
|
||
xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
|
||
} catch ( e ) {
|
||
xml = undefined;
|
||
}
|
||
|
||
if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
|
||
jQuery.error( "Invalid XML: " + data );
|
||
}
|
||
return xml;
|
||
};
|
||
|
||
|
||
var
|
||
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 ( Array.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" && v != null ? i : "" ) + "]",
|
||
v,
|
||
traditional,
|
||
add
|
||
);
|
||
}
|
||
} );
|
||
|
||
} else if ( !traditional && toType( 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, valueOrFunction ) {
|
||
|
||
// If value is a function, invoke it and use its return value
|
||
var value = isFunction( valueOrFunction ) ?
|
||
valueOrFunction() :
|
||
valueOrFunction;
|
||
|
||
s[ s.length ] = encodeURIComponent( key ) + "=" +
|
||
encodeURIComponent( value == null ? "" : value );
|
||
};
|
||
|
||
if ( a == null ) {
|
||
return "";
|
||
}
|
||
|
||
// If an array was passed in, assume that it is an array of form elements.
|
||
if ( Array.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( "&" );
|
||
};
|
||
|
||
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();
|
||
|
||
if ( val == null ) {
|
||
return null;
|
||
}
|
||
|
||
if ( Array.isArray( val ) ) {
|
||
return jQuery.map( val, function( val ) {
|
||
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
||
} );
|
||
}
|
||
|
||
return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
|
||
} ).get();
|
||
}
|
||
} );
|
||
|
||
|
||
var
|
||
r20 = /%20/g,
|
||
rhash = /#.*$/,
|
||
rantiCache = /([?&])_=[^&]*/,
|
||
rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
|
||
|
||
// #7653, #8125, #8152: local protocol detection
|
||
rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
|
||
rnoContent = /^(?:GET|HEAD)$/,
|
||
rprotocol = /^\/\//,
|
||
|
||
/* 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( "*" ),
|
||
|
||
// Anchor tag for parsing the document origin
|
||
originAnchor = document.createElement( "a" );
|
||
originAnchor.href = location.href;
|
||
|
||
// 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( rnothtmlwhite ) || [];
|
||
|
||
if ( isFunction( func ) ) {
|
||
|
||
// For each dataType in the dataTypeExpression
|
||
while ( ( dataType = dataTypes[ i++ ] ) ) {
|
||
|
||
// Prepend if requested
|
||
if ( dataType[ 0 ] === "+" ) {
|
||
dataType = dataType.slice( 1 ) || "*";
|
||
( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
|
||
|
||
// Otherwise append
|
||
} else {
|
||
( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// Base inspection function for prefilters and transports
|
||
function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
|
||
|
||
var inspected = {},
|
||
seekingTransport = ( structure === transports );
|
||
|
||
function inspect( dataType ) {
|
||
var selected;
|
||
inspected[ dataType ] = true;
|
||
jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
|
||
var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
|
||
if ( typeof dataTypeOrTransport === "string" &&
|
||
!seekingTransport && !inspected[ dataTypeOrTransport ] ) {
|
||
|
||
options.dataTypes.unshift( dataTypeOrTransport );
|
||
inspect( dataTypeOrTransport );
|
||
return false;
|
||
} else if ( seekingTransport ) {
|
||
return !( selected = dataTypeOrTransport );
|
||
}
|
||
} );
|
||
return selected;
|
||
}
|
||
|
||
return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
|
||
}
|
||
|
||
// A special extend for ajax options
|
||
// that takes "flat" options (not to be deep extended)
|
||
// Fixes #9887
|
||
function ajaxExtend( target, src ) {
|
||
var key, deep,
|
||
flatOptions = jQuery.ajaxSettings.flatOptions || {};
|
||
|
||
for ( key in src ) {
|
||
if ( src[ key ] !== undefined ) {
|
||
( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
|
||
}
|
||
}
|
||
if ( deep ) {
|
||
jQuery.extend( true, target, deep );
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
/* Handles responses to an ajax request:
|
||
* - finds the right dataType (mediates between content-type and expected dataType)
|
||
* - returns the corresponding response
|
||
*/
|
||
function ajaxHandleResponses( s, jqXHR, responses ) {
|
||
|
||
var ct, type, finalDataType, firstDataType,
|
||
contents = s.contents,
|
||
dataTypes = s.dataTypes;
|
||
|
||
// Remove auto dataType and get content-type in the process
|
||
while ( dataTypes[ 0 ] === "*" ) {
|
||
dataTypes.shift();
|
||
if ( ct === undefined ) {
|
||
ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
|
||
}
|
||
}
|
||
|
||
// Check if we're dealing with a known content-type
|
||
if ( ct ) {
|
||
for ( type in contents ) {
|
||
if ( contents[ type ] && contents[ type ].test( ct ) ) {
|
||
dataTypes.unshift( type );
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Check to see if we have a response for the expected dataType
|
||
if ( dataTypes[ 0 ] in responses ) {
|
||
finalDataType = dataTypes[ 0 ];
|
||
} else {
|
||
|
||
// Try convertible dataTypes
|
||
for ( type in responses ) {
|
||
if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
|
||
finalDataType = type;
|
||
break;
|
||
}
|
||
if ( !firstDataType ) {
|
||
firstDataType = type;
|
||
}
|
||
}
|
||
|
||
// Or just use first one
|
||
finalDataType = finalDataType || firstDataType;
|
||
}
|
||
|
||
// If we found a dataType
|
||
// We add the dataType to the list if needed
|
||
// and return the corresponding response
|
||
if ( finalDataType ) {
|
||
if ( finalDataType !== dataTypes[ 0 ] ) {
|
||
dataTypes.unshift( finalDataType );
|
||
}
|
||
return responses[ finalDataType ];
|
||
}
|
||
}
|
||
|
||
/* Chain conversions given the request and the original response
|
||
* Also sets the responseXXX fields on the jqXHR instance
|
||
*/
|
||
function ajaxConvert( s, response, jqXHR, isSuccess ) {
|
||
var conv2, current, conv, tmp, prev,
|
||
converters = {},
|
||
|
||
// Work with a copy of dataTypes in case we need to modify it for conversion
|
||
dataTypes = s.dataTypes.slice();
|
||
|
||
// Create converters map with lowercased keys
|
||
if ( dataTypes[ 1 ] ) {
|
||
for ( conv in s.converters ) {
|
||
converters[ conv.toLowerCase() ] = s.converters[ conv ];
|
||
}
|
||
}
|
||
|
||
current = dataTypes.shift();
|
||
|
||
// Convert to each sequential dataType
|
||
while ( current ) {
|
||
|
||
if ( s.responseFields[ current ] ) {
|
||
jqXHR[ s.responseFields[ current ] ] = response;
|
||
}
|
||
|
||
// Apply the dataFilter if provided
|
||
if ( !prev && isSuccess && s.dataFilter ) {
|
||
response = s.dataFilter( response, s.dataType );
|
||
}
|
||
|
||
prev = current;
|
||
current = dataTypes.shift();
|
||
|
||
if ( current ) {
|
||
|
||
// There's only work to do if current dataType is non-auto
|
||
if ( current === "*" ) {
|
||
|
||
current = prev;
|
||
|
||
// Convert response if prev dataType is non-auto and differs from current
|
||
} else if ( prev !== "*" && prev !== current ) {
|
||
|
||
// Seek a direct converter
|
||
conv = converters[ prev + " " + current ] || converters[ "* " + current ];
|
||
|
||
// If none found, seek a pair
|
||
if ( !conv ) {
|
||
for ( conv2 in converters ) {
|
||
|
||
// If conv2 outputs current
|
||
tmp = conv2.split( " " );
|
||
if ( tmp[ 1 ] === current ) {
|
||
|
||
// If prev can be converted to accepted input
|
||
conv = converters[ prev + " " + tmp[ 0 ] ] ||
|
||
converters[ "* " + tmp[ 0 ] ];
|
||
if ( conv ) {
|
||
|
||
// Condense equivalence converters
|
||
if ( conv === true ) {
|
||
conv = converters[ conv2 ];
|
||
|
||
// Otherwise, insert the intermediate dataType
|
||
} else if ( converters[ conv2 ] !== true ) {
|
||
current = tmp[ 0 ];
|
||
dataTypes.unshift( tmp[ 1 ] );
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Apply converter (if not an equivalence)
|
||
if ( conv !== true ) {
|
||
|
||
// Unless errors are allowed to bubble, catch and return them
|
||
if ( conv && s.throws ) {
|
||
response = conv( response );
|
||
} else {
|
||
try {
|
||
response = conv( response );
|
||
} catch ( e ) {
|
||
return {
|
||
state: "parsererror",
|
||
error: conv ? e : "No conversion from " + prev + " to " + current
|
||
};
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return { state: "success", data: response };
|
||
}
|
||
|
||
jQuery.extend( {
|
||
|
||
// Counter for holding the number of active queries
|
||
active: 0,
|
||
|
||
// Last-Modified header cache for next request
|
||
lastModified: {},
|
||
etag: {},
|
||
|
||
ajaxSettings: {
|
||
url: location.href,
|
||
type: "GET",
|
||
isLocal: rlocalProtocol.test( location.protocol ),
|
||
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: /\bxml\b/,
|
||
html: /\bhtml/,
|
||
json: /\bjson\b/
|
||
},
|
||
|
||
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": JSON.parse,
|
||
|
||
// Parse text as xml
|
||
"text xml": jQuery.parseXML
|
||
},
|
||
|
||
// For options that shouldn't be deep extended:
|
||
// you can add your own custom options here if
|
||
// and when you create one that shouldn't be
|
||
// deep extended (see ajaxExtend)
|
||
flatOptions: {
|
||
url: true,
|
||
context: true
|
||
}
|
||
},
|
||
|
||
// Creates a full fledged settings object into target
|
||
// with both ajaxSettings and settings fields.
|
||
// If target is omitted, writes into ajaxSettings.
|
||
ajaxSetup: function( target, settings ) {
|
||
return settings ?
|
||
|
||
// Building a settings object
|
||
ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
|
||
|
||
// Extending ajaxSettings
|
||
ajaxExtend( jQuery.ajaxSettings, target );
|
||
},
|
||
|
||
ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
|
||
ajaxTransport: addToPrefiltersOrTransports( transports ),
|
||
|
||
// Main method
|
||
ajax: function( url, options ) {
|
||
|
||
// If url is an object, simulate pre-1.5 signature
|
||
if ( typeof url === "object" ) {
|
||
options = url;
|
||
url = undefined;
|
||
}
|
||
|
||
// Force options to be an object
|
||
options = options || {};
|
||
|
||
var transport,
|
||
|
||
// URL without anti-cache param
|
||
cacheURL,
|
||
|
||
// Response headers
|
||
responseHeadersString,
|
||
responseHeaders,
|
||
|
||
// timeout handle
|
||
timeoutTimer,
|
||
|
||
// Url cleanup var
|
||
urlAnchor,
|
||
|
||
// Request state (becomes false upon send and true upon completion)
|
||
completed,
|
||
|
||
// To know if global events are to be dispatched
|
||
fireGlobals,
|
||
|
||
// Loop variable
|
||
i,
|
||
|
||
// uncached part of the url
|
||
uncached,
|
||
|
||
// 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 = {},
|
||
|
||
// Default abort message
|
||
strAbort = "canceled",
|
||
|
||
// Fake xhr
|
||
jqXHR = {
|
||
readyState: 0,
|
||
|
||
// Builds headers hashtable if needed
|
||
getResponseHeader: function( key ) {
|
||
var match;
|
||
if ( completed ) {
|
||
if ( !responseHeaders ) {
|
||
responseHeaders = {};
|
||
while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
|
||
responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
|
||
( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
|
||
.concat( match[ 2 ] );
|
||
}
|
||
}
|
||
match = responseHeaders[ key.toLowerCase() + " " ];
|
||
}
|
||
return match == null ? null : match.join( ", " );
|
||
},
|
||
|
||
// Raw string
|
||
getAllResponseHeaders: function() {
|
||
return completed ? responseHeadersString : null;
|
||
},
|
||
|
||
// Caches the header
|
||
setRequestHeader: function( name, value ) {
|
||
if ( completed == null ) {
|
||
name = requestHeadersNames[ name.toLowerCase() ] =
|
||
requestHeadersNames[ name.toLowerCase() ] || name;
|
||
requestHeaders[ name ] = value;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
// Overrides response content-type header
|
||
overrideMimeType: function( type ) {
|
||
if ( completed == null ) {
|
||
s.mimeType = type;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
// Status-dependent callbacks
|
||
statusCode: function( map ) {
|
||
var code;
|
||
if ( map ) {
|
||
if ( completed ) {
|
||
|
||
// Execute the appropriate callbacks
|
||
jqXHR.always( map[ jqXHR.status ] );
|
||
} else {
|
||
|
||
// Lazy-add the new callbacks in a way that preserves old ones
|
||
for ( code in map ) {
|
||
statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
|
||
}
|
||
}
|
||
}
|
||
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 );
|
||
|
||
// Add protocol if not provided (prefilters might expect it)
|
||
// Handle falsy url in the settings object (#10093: consistency with old signature)
|
||
// We also use the url parameter if available
|
||
s.url = ( ( url || s.url || location.href ) + "" )
|
||
.replace( rprotocol, location.protocol + "//" );
|
||
|
||
// Alias method option to type as per ticket #12004
|
||
s.type = options.method || options.type || s.method || s.type;
|
||
|
||
// Extract dataTypes list
|
||
s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
|
||
|
||
// A cross-domain request is in order when the origin doesn't match the current origin.
|
||
if ( s.crossDomain == null ) {
|
||
urlAnchor = document.createElement( "a" );
|
||
|
||
// Support: IE <=8 - 11, Edge 12 - 15
|
||
// IE throws exception on accessing the href property if url is malformed,
|
||
// e.g. http://example.com:80x/
|
||
try {
|
||
urlAnchor.href = s.url;
|
||
|
||
// Support: IE <=8 - 11 only
|
||
// Anchor's host property isn't correctly set when s.url is relative
|
||
urlAnchor.href = urlAnchor.href;
|
||
s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
|
||
urlAnchor.protocol + "//" + urlAnchor.host;
|
||
} catch ( e ) {
|
||
|
||
// If there is an error parsing the URL, assume it is crossDomain,
|
||
// it can be rejected by the transport if it is invalid
|
||
s.crossDomain = true;
|
||
}
|
||
}
|
||
|
||
// 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 ( completed ) {
|
||
return jqXHR;
|
||
}
|
||
|
||
// We can fire global events as of now if asked to
|
||
// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
|
||
fireGlobals = jQuery.event && 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
|
||
// Remove hash to simplify url manipulation
|
||
cacheURL = s.url.replace( rhash, "" );
|
||
|
||
// More options handling for requests with no content
|
||
if ( !s.hasContent ) {
|
||
|
||
// Remember the hash so we can put it back
|
||
uncached = s.url.slice( cacheURL.length );
|
||
|
||
// If data is available and should be processed, append data to url
|
||
if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
|
||
cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
|
||
|
||
// #9682: remove data so that it's not used in an eventual retry
|
||
delete s.data;
|
||
}
|
||
|
||
// Add or update anti-cache param if needed
|
||
if ( s.cache === false ) {
|
||
cacheURL = cacheURL.replace( rantiCache, "$1" );
|
||
uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) +
|
||
uncached;
|
||
}
|
||
|
||
// Put hash and anti-cache on the URL that will be requested (gh-1732)
|
||
s.url = cacheURL + uncached;
|
||
|
||
// Change '%20' to '+' if this is encoded form body content (gh-2658)
|
||
} else if ( s.data && s.processData &&
|
||
( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
|
||
s.data = s.data.replace( r20, "+" );
|
||
}
|
||
|
||
// 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 || completed ) ) {
|
||
|
||
// Abort if not done already and return
|
||
return jqXHR.abort();
|
||
}
|
||
|
||
// Aborting is no longer a cancellation
|
||
strAbort = "abort";
|
||
|
||
// Install callbacks on deferreds
|
||
completeDeferred.add( s.complete );
|
||
jqXHR.done( s.success );
|
||
jqXHR.fail( s.error );
|
||
|
||
// 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 ] );
|
||
}
|
||
|
||
// If request was aborted inside ajaxSend, stop there
|
||
if ( completed ) {
|
||
return jqXHR;
|
||
}
|
||
|
||
// Timeout
|
||
if ( s.async && s.timeout > 0 ) {
|
||
timeoutTimer = window.setTimeout( function() {
|
||
jqXHR.abort( "timeout" );
|
||
}, s.timeout );
|
||
}
|
||
|
||
try {
|
||
completed = false;
|
||
transport.send( requestHeaders, done );
|
||
} catch ( e ) {
|
||
|
||
// Rethrow post-completion exceptions
|
||
if ( completed ) {
|
||
throw e;
|
||
}
|
||
|
||
// Propagate others as results
|
||
done( -1, e );
|
||
}
|
||
}
|
||
|
||
// Callback for when everything is done
|
||
function done( status, nativeStatusText, responses, headers ) {
|
||
var isSuccess, success, error, response, modified,
|
||
statusText = nativeStatusText;
|
||
|
||
// Ignore repeat invocations
|
||
if ( completed ) {
|
||
return;
|
||
}
|
||
|
||
completed = true;
|
||
|
||
// Clear timeout if it exists
|
||
if ( timeoutTimer ) {
|
||
window.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 );
|
||
}
|
||
|
||
// Use a noop converter for missing script
|
||
if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) {
|
||
s.converters[ "text script" ] = function() {};
|
||
}
|
||
|
||
// 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 {
|
||
|
||
// Extract error from statusText and normalize 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 ( isFunction( data ) ) {
|
||
type = type || callback;
|
||
callback = data;
|
||
data = undefined;
|
||
}
|
||
|
||
// The url can be an options object (which then must have .url)
|
||
return jQuery.ajax( jQuery.extend( {
|
||
url: url,
|
||
type: method,
|
||
dataType: type,
|
||
data: data,
|
||
success: callback
|
||
}, jQuery.isPlainObject( url ) && url ) );
|
||
};
|
||
} );
|
||
|
||
jQuery.ajaxPrefilter( function( s ) {
|
||
var i;
|
||
for ( i in s.headers ) {
|
||
if ( i.toLowerCase() === "content-type" ) {
|
||
s.contentType = s.headers[ i ] || "";
|
||
}
|
||
}
|
||
} );
|
||
|
||
|
||
jQuery._evalUrl = function( url, options, doc ) {
|
||
return jQuery.ajax( {
|
||
url: url,
|
||
|
||
// Make this explicit, since user can override this through ajaxSetup (#11264)
|
||
type: "GET",
|
||
dataType: "script",
|
||
cache: true,
|
||
async: false,
|
||
global: false,
|
||
|
||
// Only evaluate the response if it is successful (gh-4126)
|
||
// dataFilter is not invoked for failure responses, so using it instead
|
||
// of the default converter is kludgy but it works.
|
||
converters: {
|
||
"text script": function() {}
|
||
},
|
||
dataFilter: function( response ) {
|
||
jQuery.globalEval( response, options, doc );
|
||
}
|
||
} );
|
||
};
|
||
|
||
|
||
jQuery.fn.extend( {
|
||
wrapAll: function( html ) {
|
||
var wrap;
|
||
|
||
if ( this[ 0 ] ) {
|
||
if ( isFunction( html ) ) {
|
||
html = html.call( this[ 0 ] );
|
||
}
|
||
|
||
// The elements to wrap the target around
|
||
wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
|
||
|
||
if ( this[ 0 ].parentNode ) {
|
||
wrap.insertBefore( this[ 0 ] );
|
||
}
|
||
|
||
wrap.map( function() {
|
||
var elem = this;
|
||
|
||
while ( elem.firstElementChild ) {
|
||
elem = elem.firstElementChild;
|
||
}
|
||
|
||
return elem;
|
||
} ).append( this );
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
wrapInner: function( html ) {
|
||
if ( 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 htmlIsFunction = isFunction( html );
|
||
|
||
return this.each( function( i ) {
|
||
jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
|
||
} );
|
||
},
|
||
|
||
unwrap: function( selector ) {
|
||
this.parent( selector ).not( "body" ).each( function() {
|
||
jQuery( this ).replaceWith( this.childNodes );
|
||
} );
|
||
return this;
|
||
}
|
||
} );
|
||
|
||
|
||
jQuery.expr.pseudos.hidden = function( elem ) {
|
||
return !jQuery.expr.pseudos.visible( elem );
|
||
};
|
||
jQuery.expr.pseudos.visible = function( elem ) {
|
||
return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
|
||
};
|
||
|
||
|
||
|
||
|
||
jQuery.ajaxSettings.xhr = function() {
|
||
try {
|
||
return new window.XMLHttpRequest();
|
||
} catch ( e ) {}
|
||
};
|
||
|
||
var xhrSuccessStatus = {
|
||
|
||
// File protocol always yields status code 0, assume 200
|
||
0: 200,
|
||
|
||
// Support: IE <=9 only
|
||
// #1450: sometimes IE returns 1223 when it should be 204
|
||
1223: 204
|
||
},
|
||
xhrSupported = jQuery.ajaxSettings.xhr();
|
||
|
||
support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
|
||
support.ajax = xhrSupported = !!xhrSupported;
|
||
|
||
jQuery.ajaxTransport( function( options ) {
|
||
var callback, errorCallback;
|
||
|
||
// Cross domain only allowed if supported through XMLHttpRequest
|
||
if ( support.cors || xhrSupported && !options.crossDomain ) {
|
||
return {
|
||
send: function( headers, complete ) {
|
||
var i,
|
||
xhr = options.xhr();
|
||
|
||
xhr.open(
|
||
options.type,
|
||
options.url,
|
||
options.async,
|
||
options.username,
|
||
options.password
|
||
);
|
||
|
||
// Apply custom fields if provided
|
||
if ( options.xhrFields ) {
|
||
for ( i in options.xhrFields ) {
|
||
xhr[ i ] = options.xhrFields[ i ];
|
||
}
|
||
}
|
||
|
||
// Override mime type if needed
|
||
if ( options.mimeType && xhr.overrideMimeType ) {
|
||
xhr.overrideMimeType( options.mimeType );
|
||
}
|
||
|
||
// X-Requested-With header
|
||
// For cross-domain requests, seeing as conditions for a preflight are
|
||
// akin to a jigsaw puzzle, we simply never set it to be sure.
|
||
// (it can always be set on a per-request basis or even using ajaxSetup)
|
||
// For same-domain requests, won't change header if already provided.
|
||
if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
|
||
headers[ "X-Requested-With" ] = "XMLHttpRequest";
|
||
}
|
||
|
||
// Set headers
|
||
for ( i in headers ) {
|
||
xhr.setRequestHeader( i, headers[ i ] );
|
||
}
|
||
|
||
// Callback
|
||
callback = function( type ) {
|
||
return function() {
|
||
if ( callback ) {
|
||
callback = errorCallback = xhr.onload =
|
||
xhr.onerror = xhr.onabort = xhr.ontimeout =
|
||
xhr.onreadystatechange = null;
|
||
|
||
if ( type === "abort" ) {
|
||
xhr.abort();
|
||
} else if ( type === "error" ) {
|
||
|
||
// Support: IE <=9 only
|
||
// On a manual native abort, IE9 throws
|
||
// errors on any property access that is not readyState
|
||
if ( typeof xhr.status !== "number" ) {
|
||
complete( 0, "error" );
|
||
} else {
|
||
complete(
|
||
|
||
// File: protocol always yields status 0; see #8605, #14207
|
||
xhr.status,
|
||
xhr.statusText
|
||
);
|
||
}
|
||
} else {
|
||
complete(
|
||
xhrSuccessStatus[ xhr.status ] || xhr.status,
|
||
xhr.statusText,
|
||
|
||
// Support: IE <=9 only
|
||
// IE9 has no XHR2 but throws on binary (trac-11426)
|
||
// For XHR2 non-text, let the caller handle it (gh-2498)
|
||
( xhr.responseType || "text" ) !== "text" ||
|
||
typeof xhr.responseText !== "string" ?
|
||
{ binary: xhr.response } :
|
||
{ text: xhr.responseText },
|
||
xhr.getAllResponseHeaders()
|
||
);
|
||
}
|
||
}
|
||
};
|
||
};
|
||
|
||
// Listen to events
|
||
xhr.onload = callback();
|
||
errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
|
||
|
||
// Support: IE 9 only
|
||
// Use onreadystatechange to replace onabort
|
||
// to handle uncaught aborts
|
||
if ( xhr.onabort !== undefined ) {
|
||
xhr.onabort = errorCallback;
|
||
} else {
|
||
xhr.onreadystatechange = function() {
|
||
|
||
// Check readyState before timeout as it changes
|
||
if ( xhr.readyState === 4 ) {
|
||
|
||
// Allow onerror to be called first,
|
||
// but that will not handle a native abort
|
||
// Also, save errorCallback to a variable
|
||
// as xhr.onerror cannot be accessed
|
||
window.setTimeout( function() {
|
||
if ( callback ) {
|
||
errorCallback();
|
||
}
|
||
} );
|
||
}
|
||
};
|
||
}
|
||
|
||
// Create the abort callback
|
||
callback = callback( "abort" );
|
||
|
||
try {
|
||
|
||
// Do send the request (this may raise an exception)
|
||
xhr.send( options.hasContent && options.data || null );
|
||
} catch ( e ) {
|
||
|
||
// #14683: Only rethrow if this hasn't been notified as an error yet
|
||
if ( callback ) {
|
||
throw e;
|
||
}
|
||
}
|
||
},
|
||
|
||
abort: function() {
|
||
if ( callback ) {
|
||
callback();
|
||
}
|
||
}
|
||
};
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
|
||
jQuery.ajaxPrefilter( function( s ) {
|
||
if ( s.crossDomain ) {
|
||
s.contents.script = false;
|
||
}
|
||
} );
|
||
|
||
// Install script dataType
|
||
jQuery.ajaxSetup( {
|
||
accepts: {
|
||
script: "text/javascript, application/javascript, " +
|
||
"application/ecmascript, application/x-ecmascript"
|
||
},
|
||
contents: {
|
||
script: /\b(?:java|ecma)script\b/
|
||
},
|
||
converters: {
|
||
"text script": function( text ) {
|
||
jQuery.globalEval( text );
|
||
return text;
|
||
}
|
||
}
|
||
} );
|
||
|
||
// Handle cache's special case and crossDomain
|
||
jQuery.ajaxPrefilter( "script", function( s ) {
|
||
if ( s.cache === undefined ) {
|
||
s.cache = false;
|
||
}
|
||
if ( s.crossDomain ) {
|
||
s.type = "GET";
|
||
}
|
||
} );
|
||
|
||
// Bind script tag hack transport
|
||
jQuery.ajaxTransport( "script", function( s ) {
|
||
|
||
// This transport only deals with cross domain or forced-by-attrs requests
|
||
if ( s.crossDomain || s.scriptAttrs ) {
|
||
var script, callback;
|
||
return {
|
||
send: function( _, complete ) {
|
||
script = jQuery( "<script>" )
|
||
.attr( s.scriptAttrs || {} )
|
||
.prop( { charset: s.scriptCharset, src: s.url } )
|
||
.on( "load error", callback = function( evt ) {
|
||
script.remove();
|
||
callback = null;
|
||
if ( evt ) {
|
||
complete( evt.type === "error" ? 404 : 200, evt.type );
|
||
}
|
||
} );
|
||
|
||
// Use native DOM manipulation to avoid our domManip AJAX trickery
|
||
document.head.appendChild( script[ 0 ] );
|
||
},
|
||
abort: function() {
|
||
if ( callback ) {
|
||
callback();
|
||
}
|
||
}
|
||
};
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
var oldCallbacks = [],
|
||
rjsonp = /(=)\?(?=&|$)|\?\?/;
|
||
|
||
// Default jsonp settings
|
||
jQuery.ajaxSetup( {
|
||
jsonp: "callback",
|
||
jsonpCallback: function() {
|
||
var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce.guid++ ) );
|
||
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" ) === 0 &&
|
||
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 = 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() {
|
||
|
||
// If previous value didn't exist - remove it
|
||
if ( overwritten === undefined ) {
|
||
jQuery( window ).removeProp( callbackName );
|
||
|
||
// Otherwise restore preexisting value
|
||
} else {
|
||
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 && isFunction( overwritten ) ) {
|
||
overwritten( responseContainer[ 0 ] );
|
||
}
|
||
|
||
responseContainer = overwritten = undefined;
|
||
} );
|
||
|
||
// Delegate to script
|
||
return "script";
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
// Support: Safari 8 only
|
||
// In Safari 8 documents created via document.implementation.createHTMLDocument
|
||
// collapse sibling forms: the second one becomes a child of the first one.
|
||
// Because of that, this security measure has to be disabled in Safari 8.
|
||
// https://bugs.webkit.org/show_bug.cgi?id=137337
|
||
support.createHTMLDocument = ( function() {
|
||
var body = document.implementation.createHTMLDocument( "" ).body;
|
||
body.innerHTML = "<form></form><form></form>";
|
||
return body.childNodes.length === 2;
|
||
} )();
|
||
|
||
|
||
// Argument "data" should be 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 ( typeof data !== "string" ) {
|
||
return [];
|
||
}
|
||
if ( typeof context === "boolean" ) {
|
||
keepScripts = context;
|
||
context = false;
|
||
}
|
||
|
||
var base, parsed, scripts;
|
||
|
||
if ( !context ) {
|
||
|
||
// Stop scripts or inline event handlers from being executed immediately
|
||
// by using document.implementation
|
||
if ( support.createHTMLDocument ) {
|
||
context = document.implementation.createHTMLDocument( "" );
|
||
|
||
// Set the base href for the created document
|
||
// so any parsed elements with URLs
|
||
// are based on the document's URL (gh-2965)
|
||
base = context.createElement( "base" );
|
||
base.href = document.location.href;
|
||
context.head.appendChild( base );
|
||
} else {
|
||
context = document;
|
||
}
|
||
}
|
||
|
||
parsed = rsingleTag.exec( data );
|
||
scripts = !keepScripts && [];
|
||
|
||
// Single tag
|
||
if ( parsed ) {
|
||
return [ context.createElement( parsed[ 1 ] ) ];
|
||
}
|
||
|
||
parsed = buildFragment( [ data ], context, scripts );
|
||
|
||
if ( scripts && scripts.length ) {
|
||
jQuery( scripts ).remove();
|
||
}
|
||
|
||
return jQuery.merge( [], parsed.childNodes );
|
||
};
|
||
|
||
|
||
/**
|
||
* Load a url into a page
|
||
*/
|
||
jQuery.fn.load = function( url, params, callback ) {
|
||
var selector, type, response,
|
||
self = this,
|
||
off = url.indexOf( " " );
|
||
|
||
if ( off > -1 ) {
|
||
selector = stripAndCollapse( url.slice( off ) );
|
||
url = url.slice( 0, off );
|
||
}
|
||
|
||
// If it's a function
|
||
if ( 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.
|
||
// Make value of this field explicit since
|
||
// user can override it through ajaxSetup method
|
||
type: type || "GET",
|
||
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 );
|
||
|
||
// If the request succeeds, this function gets "data", "status", "jqXHR"
|
||
// but they are ignored because response was set above.
|
||
// If it fails, this function gets "jqXHR", "status", "error"
|
||
} ).always( callback && function( jqXHR, status ) {
|
||
self.each( function() {
|
||
callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
|
||
} );
|
||
} );
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
|
||
|
||
|
||
jQuery.expr.pseudos.animated = function( elem ) {
|
||
return jQuery.grep( jQuery.timers, function( fn ) {
|
||
return elem === fn.elem;
|
||
} ).length;
|
||
};
|
||
|
||
|
||
|
||
|
||
jQuery.offset = {
|
||
setOffset: function( elem, options, i ) {
|
||
var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
|
||
position = jQuery.css( elem, "position" ),
|
||
curElem = jQuery( elem ),
|
||
props = {};
|
||
|
||
// Set position first, in-case top/left are set even on static elem
|
||
if ( position === "static" ) {
|
||
elem.style.position = "relative";
|
||
}
|
||
|
||
curOffset = curElem.offset();
|
||
curCSSTop = jQuery.css( elem, "top" );
|
||
curCSSLeft = jQuery.css( elem, "left" );
|
||
calculatePosition = ( position === "absolute" || position === "fixed" ) &&
|
||
( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
|
||
|
||
// Need to be able to calculate position if either
|
||
// top or left is auto and position is either absolute or fixed
|
||
if ( calculatePosition ) {
|
||
curPosition = curElem.position();
|
||
curTop = curPosition.top;
|
||
curLeft = curPosition.left;
|
||
|
||
} else {
|
||
curTop = parseFloat( curCSSTop ) || 0;
|
||
curLeft = parseFloat( curCSSLeft ) || 0;
|
||
}
|
||
|
||
if ( isFunction( options ) ) {
|
||
|
||
// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
|
||
options = options.call( elem, i, jQuery.extend( {}, 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 {
|
||
if ( typeof props.top === "number" ) {
|
||
props.top += "px";
|
||
}
|
||
if ( typeof props.left === "number" ) {
|
||
props.left += "px";
|
||
}
|
||
curElem.css( props );
|
||
}
|
||
}
|
||
};
|
||
|
||
jQuery.fn.extend( {
|
||
|
||
// offset() relates an element's border box to the document origin
|
||
offset: function( options ) {
|
||
|
||
// Preserve chaining for setter
|
||
if ( arguments.length ) {
|
||
return options === undefined ?
|
||
this :
|
||
this.each( function( i ) {
|
||
jQuery.offset.setOffset( this, options, i );
|
||
} );
|
||
}
|
||
|
||
var rect, win,
|
||
elem = this[ 0 ];
|
||
|
||
if ( !elem ) {
|
||
return;
|
||
}
|
||
|
||
// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
|
||
// Support: IE <=11 only
|
||
// Running getBoundingClientRect on a
|
||
// disconnected node in IE throws an error
|
||
if ( !elem.getClientRects().length ) {
|
||
return { top: 0, left: 0 };
|
||
}
|
||
|
||
// Get document-relative position by adding viewport scroll to viewport-relative gBCR
|
||
rect = elem.getBoundingClientRect();
|
||
win = elem.ownerDocument.defaultView;
|
||
return {
|
||
top: rect.top + win.pageYOffset,
|
||
left: rect.left + win.pageXOffset
|
||
};
|
||
},
|
||
|
||
// position() relates an element's margin box to its offset parent's padding box
|
||
// This corresponds to the behavior of CSS absolute positioning
|
||
position: function() {
|
||
if ( !this[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
var offsetParent, offset, doc,
|
||
elem = this[ 0 ],
|
||
parentOffset = { top: 0, left: 0 };
|
||
|
||
// position:fixed elements are offset from the viewport, which itself always has zero offset
|
||
if ( jQuery.css( elem, "position" ) === "fixed" ) {
|
||
|
||
// Assume position:fixed implies availability of getBoundingClientRect
|
||
offset = elem.getBoundingClientRect();
|
||
|
||
} else {
|
||
offset = this.offset();
|
||
|
||
// Account for the *real* offset parent, which can be the document or its root element
|
||
// when a statically positioned element is identified
|
||
doc = elem.ownerDocument;
|
||
offsetParent = elem.offsetParent || doc.documentElement;
|
||
while ( offsetParent &&
|
||
( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
|
||
jQuery.css( offsetParent, "position" ) === "static" ) {
|
||
|
||
offsetParent = offsetParent.parentNode;
|
||
}
|
||
if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
|
||
|
||
// Incorporate borders into its offset, since they are outside its content origin
|
||
parentOffset = jQuery( offsetParent ).offset();
|
||
parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
|
||
parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
|
||
}
|
||
}
|
||
|
||
// Subtract parent offsets and element margins
|
||
return {
|
||
top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
|
||
left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
|
||
};
|
||
},
|
||
|
||
// This method will return documentElement in the following cases:
|
||
// 1) For the element inside the iframe without offsetParent, this method will return
|
||
// documentElement of the parent window
|
||
// 2) For the hidden or detached element
|
||
// 3) For body or html element, i.e. in case of the html node - it will return itself
|
||
//
|
||
// but those exceptions were never presented as a real life use-cases
|
||
// and might be considered as more preferable results.
|
||
//
|
||
// This logic, however, is not guaranteed and can change at any point in the future
|
||
offsetParent: function() {
|
||
return this.map( function() {
|
||
var offsetParent = this.offsetParent;
|
||
|
||
while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
|
||
offsetParent = offsetParent.offsetParent;
|
||
}
|
||
|
||
return offsetParent || documentElement;
|
||
} );
|
||
}
|
||
} );
|
||
|
||
// Create scrollLeft and scrollTop methods
|
||
jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
|
||
var top = "pageYOffset" === prop;
|
||
|
||
jQuery.fn[ method ] = function( val ) {
|
||
return access( this, function( elem, method, val ) {
|
||
|
||
// Coalesce documents and windows
|
||
var win;
|
||
if ( isWindow( elem ) ) {
|
||
win = elem;
|
||
} else if ( elem.nodeType === 9 ) {
|
||
win = elem.defaultView;
|
||
}
|
||
|
||
if ( val === undefined ) {
|
||
return win ? win[ prop ] : elem[ method ];
|
||
}
|
||
|
||
if ( win ) {
|
||
win.scrollTo(
|
||
!top ? val : win.pageXOffset,
|
||
top ? val : win.pageYOffset
|
||
);
|
||
|
||
} else {
|
||
elem[ method ] = val;
|
||
}
|
||
}, method, val, arguments.length );
|
||
};
|
||
} );
|
||
|
||
// Support: Safari <=7 - 9.1, Chrome <=37 - 49
|
||
// Add the top/left cssHooks using jQuery.fn.position
|
||
// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
|
||
// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
|
||
// getComputedStyle returns percent when specified for top/left/bottom/right;
|
||
// rather than make the css module depend on the offset module, 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 ( isWindow( elem ) ) {
|
||
|
||
// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
|
||
return funcName.indexOf( "outer" ) === 0 ?
|
||
elem[ "inner" + name ] :
|
||
elem.document.documentElement[ "client" + name ];
|
||
}
|
||
|
||
// Get document width or height
|
||
if ( elem.nodeType === 9 ) {
|
||
doc = elem.documentElement;
|
||
|
||
// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
|
||
// whichever is greatest
|
||
return Math.max(
|
||
elem.body[ "scroll" + name ], doc[ "scroll" + name ],
|
||
elem.body[ "offset" + name ], doc[ "offset" + name ],
|
||
doc[ "client" + name ]
|
||
);
|
||
}
|
||
|
||
return value === undefined ?
|
||
|
||
// Get width or height on the element, requesting but not forcing parseFloat
|
||
jQuery.css( elem, type, extra ) :
|
||
|
||
// Set width or height on the element
|
||
jQuery.style( elem, type, value, extra );
|
||
}, type, chainable ? margin : undefined, chainable );
|
||
};
|
||
} );
|
||
} );
|
||
|
||
|
||
jQuery.each( [
|
||
"ajaxStart",
|
||
"ajaxStop",
|
||
"ajaxComplete",
|
||
"ajaxError",
|
||
"ajaxSuccess",
|
||
"ajaxSend"
|
||
], function( _i, type ) {
|
||
jQuery.fn[ type ] = function( fn ) {
|
||
return this.on( type, fn );
|
||
};
|
||
} );
|
||
|
||
|
||
|
||
|
||
jQuery.fn.extend( {
|
||
|
||
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 );
|
||
},
|
||
|
||
hover: function( fnOver, fnOut ) {
|
||
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
|
||
}
|
||
} );
|
||
|
||
jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
|
||
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
|
||
"change select submit keydown keypress keyup 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 );
|
||
};
|
||
} );
|
||
|
||
|
||
|
||
|
||
// Support: Android <=4.0 only
|
||
// Make sure we trim BOM and NBSP
|
||
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
|
||
|
||
// Bind a function to a context, optionally partially applying any
|
||
// arguments.
|
||
// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
|
||
// However, it is not slated for removal any time soon
|
||
jQuery.proxy = function( fn, context ) {
|
||
var tmp, args, proxy;
|
||
|
||
if ( typeof context === "string" ) {
|
||
tmp = fn[ context ];
|
||
context = fn;
|
||
fn = tmp;
|
||
}
|
||
|
||
// Quick check to determine if target is callable, in the spec
|
||
// this throws a TypeError, but we will just return undefined.
|
||
if ( !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;
|
||
};
|
||
|
||
jQuery.holdReady = function( hold ) {
|
||
if ( hold ) {
|
||
jQuery.readyWait++;
|
||
} else {
|
||
jQuery.ready( true );
|
||
}
|
||
};
|
||
jQuery.isArray = Array.isArray;
|
||
jQuery.parseJSON = JSON.parse;
|
||
jQuery.nodeName = nodeName;
|
||
jQuery.isFunction = isFunction;
|
||
jQuery.isWindow = isWindow;
|
||
jQuery.camelCase = camelCase;
|
||
jQuery.type = toType;
|
||
|
||
jQuery.now = Date.now;
|
||
|
||
jQuery.isNumeric = function( obj ) {
|
||
|
||
// As of jQuery 3.0, isNumeric is limited to
|
||
// strings and numbers (primitives or objects)
|
||
// that can be coerced to finite numbers (gh-2662)
|
||
var type = jQuery.type( obj );
|
||
return ( type === "number" || type === "string" ) &&
|
||
|
||
// parseFloat NaNs numeric-cast false positives ("")
|
||
// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
|
||
// subtraction forces infinities to NaN
|
||
!isNaN( obj - parseFloat( obj ) );
|
||
};
|
||
|
||
jQuery.trim = function( text ) {
|
||
return text == null ?
|
||
"" :
|
||
( text + "" ).replace( rtrim, "" );
|
||
};
|
||
|
||
|
||
|
||
// 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.
|
||
|
||
// Note that for maximum portability, libraries that are not jQuery should
|
||
// declare themselves as anonymous modules, and avoid setting a global if an
|
||
// AMD loader is present. jQuery is a special case. For more information, see
|
||
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
|
||
|
||
if ( typeof define === "function" && define.amd ) {
|
||
define( "jquery", [], function() {
|
||
return jQuery;
|
||
} );
|
||
}
|
||
|
||
|
||
|
||
|
||
var
|
||
|
||
// Map over jQuery in case of overwrite
|
||
_jQuery = window.jQuery,
|
||
|
||
// Map over the $ in case of overwrite
|
||
_$ = window.$;
|
||
|
||
jQuery.noConflict = function( deep ) {
|
||
if ( window.$ === jQuery ) {
|
||
window.$ = _$;
|
||
}
|
||
|
||
if ( deep && window.jQuery === jQuery ) {
|
||
window.jQuery = _jQuery;
|
||
}
|
||
|
||
return jQuery;
|
||
};
|
||
|
||
// Expose jQuery and $ identifiers, even in AMD
|
||
// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
|
||
// and CommonJS for browser emulators (#13566)
|
||
if ( typeof noGlobal === "undefined" ) {
|
||
window.jQuery = window.$ = jQuery;
|
||
}
|
||
|
||
|
||
|
||
|
||
return jQuery;
|
||
} );
|
||
|
||
/*! Select2 4.0.13 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||
!function(n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof module&&module.exports?module.exports=function(e,t){return void 0===t&&(t="undefined"!=typeof window?require("jquery"):require("jquery")(e)),n(t),t}:n(jQuery)}(function(d){var e=function(){if(d&&d.fn&&d.fn.select2&&d.fn.select2.amd)var e=d.fn.select2.amd;var t,n,i,h,o,s,f,g,m,v,y,_,r,a,w,l;function b(e,t){return r.call(e,t)}function c(e,t){var n,i,r,o,s,a,l,c,u,d,p,h=t&&t.split("/"),f=y.map,g=f&&f["*"]||{};if(e){for(s=(e=e.split("/")).length-1,y.nodeIdCompat&&w.test(e[s])&&(e[s]=e[s].replace(w,"")),"."===e[0].charAt(0)&&h&&(e=h.slice(0,h.length-1).concat(e)),u=0;u<e.length;u++)if("."===(p=e[u]))e.splice(u,1),--u;else if(".."===p){if(0===u||1===u&&".."===e[2]||".."===e[u-1])continue;0<u&&(e.splice(u-1,2),u-=2)}e=e.join("/")}if((h||g)&&f){for(u=(n=e.split("/")).length;0<u;--u){if(i=n.slice(0,u).join("/"),h)for(d=h.length;0<d;--d)if(r=(r=f[h.slice(0,d).join("/")])&&r[i]){o=r,a=u;break}if(o)break;!l&&g&&g[i]&&(l=g[i],c=u)}!o&&l&&(o=l,a=c),o&&(n.splice(0,a,o),e=n.join("/"))}return e}function A(t,n){return function(){var e=a.call(arguments,0);return"string"!=typeof e[0]&&1===e.length&&e.push(null),s.apply(h,e.concat([t,n]))}}function x(t){return function(e){m[t]=e}}function D(e){if(b(v,e)){var t=v[e];delete v[e],_[e]=!0,o.apply(h,t)}if(!b(m,e)&&!b(_,e))throw new Error("No "+e);return m[e]}function u(e){var t,n=e?e.indexOf("!"):-1;return-1<n&&(t=e.substring(0,n),e=e.substring(n+1,e.length)),[t,e]}function S(e){return e?u(e):[]}return e&&e.requirejs||(e?n=e:e={},m={},v={},y={},_={},r=Object.prototype.hasOwnProperty,a=[].slice,w=/\.js$/,f=function(e,t){var n,i,r=u(e),o=r[0],s=t[1];return e=r[1],o&&(n=D(o=c(o,s))),o?e=n&&n.normalize?n.normalize(e,(i=s,function(e){return c(e,i)})):c(e,s):(o=(r=u(e=c(e,s)))[0],e=r[1],o&&(n=D(o))),{f:o?o+"!"+e:e,n:e,pr:o,p:n}},g={require:function(e){return A(e)},exports:function(e){var t=m[e];return void 0!==t?t:m[e]={}},module:function(e){return{id:e,uri:"",exports:m[e],config:(t=e,function(){return y&&y.config&&y.config[t]||{}})};var t}},o=function(e,t,n,i){var r,o,s,a,l,c,u,d=[],p=typeof n;if(c=S(i=i||e),"undefined"==p||"function"==p){for(t=!t.length&&n.length?["require","exports","module"]:t,l=0;l<t.length;l+=1)if("require"===(o=(a=f(t[l],c)).f))d[l]=g.require(e);else if("exports"===o)d[l]=g.exports(e),u=!0;else if("module"===o)r=d[l]=g.module(e);else if(b(m,o)||b(v,o)||b(_,o))d[l]=D(o);else{if(!a.p)throw new Error(e+" missing "+o);a.p.load(a.n,A(i,!0),x(o),{}),d[l]=m[o]}s=n?n.apply(m[e],d):void 0,e&&(r&&r.exports!==h&&r.exports!==m[e]?m[e]=r.exports:s===h&&u||(m[e]=s))}else e&&(m[e]=n)},t=n=s=function(e,t,n,i,r){if("string"==typeof e)return g[e]?g[e](t):D(f(e,S(t)).f);if(!e.splice){if((y=e).deps&&s(y.deps,y.callback),!t)return;t.splice?(e=t,t=n,n=null):e=h}return t=t||function(){},"function"==typeof n&&(n=i,i=r),i?o(h,e,t,n):setTimeout(function(){o(h,e,t,n)},4),s},s.config=function(e){return s(e)},t._defined=m,(i=function(e,t,n){if("string"!=typeof e)throw new Error("See almond README: incorrect module build, no module name");t.splice||(n=t,t=[]),b(m,e)||b(v,e)||(v[e]=[e,t,n])}).amd={jQuery:!0},e.requirejs=t,e.require=n,e.define=i),e.define("almond",function(){}),e.define("jquery",[],function(){var e=d||$;return null==e&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),e}),e.define("select2/utils",["jquery"],function(o){var r={};function u(e){var t=e.prototype,n=[];for(var i in t){"function"==typeof t[i]&&"constructor"!==i&&n.push(i)}return n}r.Extend=function(e,t){var n={}.hasOwnProperty;function i(){this.constructor=e}for(var r in t)n.call(t,r)&&(e[r]=t[r]);return i.prototype=t.prototype,e.prototype=new i,e.__super__=t.prototype,e},r.Decorate=function(i,r){var e=u(r),t=u(i);function o(){var e=Array.prototype.unshift,t=r.prototype.constructor.length,n=i.prototype.constructor;0<t&&(e.call(arguments,i.prototype.constructor),n=r.prototype.constructor),n.apply(this,arguments)}r.displayName=i.displayName,o.prototype=new function(){this.constructor=o};for(var n=0;n<t.length;n++){var s=t[n];o.prototype[s]=i.prototype[s]}function a(e){var t=function(){};e in o.prototype&&(t=o.prototype[e]);var n=r.prototype[e];return function(){return Array.prototype.unshift.call(arguments,t),n.apply(this,arguments)}}for(var l=0;l<e.length;l++){var c=e[l];o.prototype[c]=a(c)}return o};function e(){this.listeners={}}e.prototype.on=function(e,t){this.listeners=this.listeners||{},e in this.listeners?this.listeners[e].push(t):this.listeners[e]=[t]},e.prototype.trigger=function(e){var t=Array.prototype.slice,n=t.call(arguments,1);this.listeners=this.listeners||{},null==n&&(n=[]),0===n.length&&n.push({}),(n[0]._type=e)in this.listeners&&this.invoke(this.listeners[e],t.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},e.prototype.invoke=function(e,t){for(var n=0,i=e.length;n<i;n++)e[n].apply(this,t)},r.Observable=e,r.generateChars=function(e){for(var t="",n=0;n<e;n++){t+=Math.floor(36*Math.random()).toString(36)}return t},r.bind=function(e,t){return function(){e.apply(t,arguments)}},r._convertData=function(e){for(var t in e){var n=t.split("-"),i=e;if(1!==n.length){for(var r=0;r<n.length;r++){var o=n[r];(o=o.substring(0,1).toLowerCase()+o.substring(1))in i||(i[o]={}),r==n.length-1&&(i[o]=e[t]),i=i[o]}delete e[t]}}return e},r.hasScroll=function(e,t){var n=o(t),i=t.style.overflowX,r=t.style.overflowY;return(i!==r||"hidden"!==r&&"visible"!==r)&&("scroll"===i||"scroll"===r||(n.innerHeight()<t.scrollHeight||n.innerWidth()<t.scrollWidth))},r.escapeMarkup=function(e){var t={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof e?e:String(e).replace(/[&<>"'\/\\]/g,function(e){return t[e]})},r.appendMany=function(e,t){if("1.7"===o.fn.jquery.substr(0,3)){var n=o();o.map(t,function(e){n=n.add(e)}),t=n}e.append(t)},r.__cache={};var n=0;return r.GetUniqueElementId=function(e){var t=e.getAttribute("data-select2-id");return null==t&&(e.id?(t=e.id,e.setAttribute("data-select2-id",t)):(e.setAttribute("data-select2-id",++n),t=n.toString())),t},r.StoreData=function(e,t,n){var i=r.GetUniqueElementId(e);r.__cache[i]||(r.__cache[i]={}),r.__cache[i][t]=n},r.GetData=function(e,t){var n=r.GetUniqueElementId(e);return t?r.__cache[n]&&null!=r.__cache[n][t]?r.__cache[n][t]:o(e).data(t):r.__cache[n]},r.RemoveData=function(e){var t=r.GetUniqueElementId(e);null!=r.__cache[t]&&delete r.__cache[t],e.removeAttribute("data-select2-id")},r}),e.define("select2/results",["jquery","./utils"],function(h,f){function i(e,t,n){this.$element=e,this.data=n,this.options=t,i.__super__.constructor.call(this)}return f.Extend(i,f.Observable),i.prototype.render=function(){var e=h('<ul class="select2-results__options" role="listbox"></ul>');return this.options.get("multiple")&&e.attr("aria-multiselectable","true"),this.$results=e},i.prototype.clear=function(){this.$results.empty()},i.prototype.displayMessage=function(e){var t=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var n=h('<li role="alert" aria-live="assertive" class="select2-results__option"></li>'),i=this.options.get("translations").get(e.message);n.append(t(i(e.args))),n[0].className+=" select2-results__message",this.$results.append(n)},i.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},i.prototype.append=function(e){this.hideLoading();var t=[];if(null!=e.results&&0!==e.results.length){e.results=this.sort(e.results);for(var n=0;n<e.results.length;n++){var i=e.results[n],r=this.option(i);t.push(r)}this.$results.append(t)}else 0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"})},i.prototype.position=function(e,t){t.find(".select2-results").append(e)},i.prototype.sort=function(e){return this.options.get("sorter")(e)},i.prototype.highlightFirstItem=function(){var e=this.$results.find(".select2-results__option[aria-selected]"),t=e.filter("[aria-selected=true]");0<t.length?t.first().trigger("mouseenter"):e.first().trigger("mouseenter"),this.ensureHighlightVisible()},i.prototype.setClasses=function(){var t=this;this.data.current(function(e){var i=h.map(e,function(e){return e.id.toString()});t.$results.find(".select2-results__option[aria-selected]").each(function(){var e=h(this),t=f.GetData(this,"data"),n=""+t.id;null!=t.element&&t.element.selected||null==t.element&&-1<h.inArray(n,i)?e.attr("aria-selected","true"):e.attr("aria-selected","false")})})},i.prototype.showLoading=function(e){this.hideLoading();var t={disabled:!0,loading:!0,text:this.options.get("translations").get("searching")(e)},n=this.option(t);n.className+=" loading-results",this.$results.prepend(n)},i.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},i.prototype.option=function(e){var t=document.createElement("li");t.className="select2-results__option";var n={role:"option","aria-selected":"false"},i=window.Element.prototype.matches||window.Element.prototype.msMatchesSelector||window.Element.prototype.webkitMatchesSelector;for(var r in(null!=e.element&&i.call(e.element,":disabled")||null==e.element&&e.disabled)&&(delete n["aria-selected"],n["aria-disabled"]="true"),null==e.id&&delete n["aria-selected"],null!=e._resultId&&(t.id=e._resultId),e.title&&(t.title=e.title),e.children&&(n.role="group",n["aria-label"]=e.text,delete n["aria-selected"]),n){var o=n[r];t.setAttribute(r,o)}if(e.children){var s=h(t),a=document.createElement("strong");a.className="select2-results__group";h(a);this.template(e,a);for(var l=[],c=0;c<e.children.length;c++){var u=e.children[c],d=this.option(u);l.push(d)}var p=h("<ul></ul>",{class:"select2-results__options select2-results__options--nested"});p.append(l),s.append(a),s.append(p)}else this.template(e,t);return f.StoreData(t,"data",e),t},i.prototype.bind=function(t,e){var l=this,n=t.id+"-results";this.$results.attr("id",n),t.on("results:all",function(e){l.clear(),l.append(e.data),t.isOpen()&&(l.setClasses(),l.highlightFirstItem())}),t.on("results:append",function(e){l.append(e.data),t.isOpen()&&l.setClasses()}),t.on("query",function(e){l.hideMessages(),l.showLoading(e)}),t.on("select",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("unselect",function(){t.isOpen()&&(l.setClasses(),l.options.get("scrollAfterSelect")&&l.highlightFirstItem())}),t.on("open",function(){l.$results.attr("aria-expanded","true"),l.$results.attr("aria-hidden","false"),l.setClasses(),l.ensureHighlightVisible()}),t.on("close",function(){l.$results.attr("aria-expanded","false"),l.$results.attr("aria-hidden","true"),l.$results.removeAttr("aria-activedescendant")}),t.on("results:toggle",function(){var e=l.getHighlightedResults();0!==e.length&&e.trigger("mouseup")}),t.on("results:select",function(){var e=l.getHighlightedResults();if(0!==e.length){var t=f.GetData(e[0],"data");"true"==e.attr("aria-selected")?l.trigger("close",{}):l.trigger("select",{data:t})}}),t.on("results:previous",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e);if(!(n<=0)){var i=n-1;0===e.length&&(i=0);var r=t.eq(i);r.trigger("mouseenter");var o=l.$results.offset().top,s=r.offset().top,a=l.$results.scrollTop()+(s-o);0===i?l.$results.scrollTop(0):s-o<0&&l.$results.scrollTop(a)}}),t.on("results:next",function(){var e=l.getHighlightedResults(),t=l.$results.find("[aria-selected]"),n=t.index(e)+1;if(!(n>=t.length)){var i=t.eq(n);i.trigger("mouseenter");var r=l.$results.offset().top+l.$results.outerHeight(!1),o=i.offset().top+i.outerHeight(!1),s=l.$results.scrollTop()+o-r;0===n?l.$results.scrollTop(0):r<o&&l.$results.scrollTop(s)}}),t.on("results:focus",function(e){e.element.addClass("select2-results__option--highlighted")}),t.on("results:message",function(e){l.displayMessage(e)}),h.fn.mousewheel&&this.$results.on("mousewheel",function(e){var t=l.$results.scrollTop(),n=l.$results.get(0).scrollHeight-t+e.deltaY,i=0<e.deltaY&&t-e.deltaY<=0,r=e.deltaY<0&&n<=l.$results.height();i?(l.$results.scrollTop(0),e.preventDefault(),e.stopPropagation()):r&&(l.$results.scrollTop(l.$results.get(0).scrollHeight-l.$results.height()),e.preventDefault(),e.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(e){var t=h(this),n=f.GetData(this,"data");"true"!==t.attr("aria-selected")?l.trigger("select",{originalEvent:e,data:n}):l.options.get("multiple")?l.trigger("unselect",{originalEvent:e,data:n}):l.trigger("close",{})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(e){var t=f.GetData(this,"data");l.getHighlightedResults().removeClass("select2-results__option--highlighted"),l.trigger("results:focus",{data:t,element:h(this)})})},i.prototype.getHighlightedResults=function(){return this.$results.find(".select2-results__option--highlighted")},i.prototype.destroy=function(){this.$results.remove()},i.prototype.ensureHighlightVisible=function(){var e=this.getHighlightedResults();if(0!==e.length){var t=this.$results.find("[aria-selected]").index(e),n=this.$results.offset().top,i=e.offset().top,r=this.$results.scrollTop()+(i-n),o=i-n;r-=2*e.outerHeight(!1),t<=2?this.$results.scrollTop(0):(o>this.$results.outerHeight()||o<0)&&this.$results.scrollTop(r)}},i.prototype.template=function(e,t){var n=this.options.get("templateResult"),i=this.options.get("escapeMarkup"),r=n(e,t);null==r?t.style.display="none":"string"==typeof r?t.innerHTML=i(r):h(t).append(r)},i}),e.define("select2/keys",[],function(){return{BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46}}),e.define("select2/selection/base",["jquery","../utils","../keys"],function(n,i,r){function o(e,t){this.$element=e,this.options=t,o.__super__.constructor.call(this)}return i.Extend(o,i.Observable),o.prototype.render=function(){var e=n('<span class="select2-selection" role="combobox" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=i.GetData(this.$element[0],"old-tabindex")?this._tabindex=i.GetData(this.$element[0],"old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),e.attr("title",this.$element.attr("title")),e.attr("tabindex",this._tabindex),e.attr("aria-disabled","false"),this.$selection=e},o.prototype.bind=function(e,t){var n=this,i=e.id+"-results";this.container=e,this.$selection.on("focus",function(e){n.trigger("focus",e)}),this.$selection.on("blur",function(e){n._handleBlur(e)}),this.$selection.on("keydown",function(e){n.trigger("keypress",e),e.which===r.SPACE&&e.preventDefault()}),e.on("results:focus",function(e){n.$selection.attr("aria-activedescendant",e.data._resultId)}),e.on("selection:update",function(e){n.update(e.data)}),e.on("open",function(){n.$selection.attr("aria-expanded","true"),n.$selection.attr("aria-owns",i),n._attachCloseHandler(e)}),e.on("close",function(){n.$selection.attr("aria-expanded","false"),n.$selection.removeAttr("aria-activedescendant"),n.$selection.removeAttr("aria-owns"),n.$selection.trigger("focus"),n._detachCloseHandler(e)}),e.on("enable",function(){n.$selection.attr("tabindex",n._tabindex),n.$selection.attr("aria-disabled","false")}),e.on("disable",function(){n.$selection.attr("tabindex","-1"),n.$selection.attr("aria-disabled","true")})},o.prototype._handleBlur=function(e){var t=this;window.setTimeout(function(){document.activeElement==t.$selection[0]||n.contains(t.$selection[0],document.activeElement)||t.trigger("blur",e)},1)},o.prototype._attachCloseHandler=function(e){n(document.body).on("mousedown.select2."+e.id,function(e){var t=n(e.target).closest(".select2");n(".select2.select2-container--open").each(function(){this!=t[0]&&i.GetData(this,"element").select2("close")})})},o.prototype._detachCloseHandler=function(e){n(document.body).off("mousedown.select2."+e.id)},o.prototype.position=function(e,t){t.find(".selection").append(e)},o.prototype.destroy=function(){this._detachCloseHandler(this.container)},o.prototype.update=function(e){throw new Error("The `update` method must be defined in child classes.")},o.prototype.isEnabled=function(){return!this.isDisabled()},o.prototype.isDisabled=function(){return this.options.get("disabled")},o}),e.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(e,t,n,i){function r(){r.__super__.constructor.apply(this,arguments)}return n.Extend(r,t),r.prototype.render=function(){var e=r.__super__.render.call(this);return e.addClass("select2-selection--single"),e.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),e},r.prototype.bind=function(t,e){var n=this;r.__super__.bind.apply(this,arguments);var i=t.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",i).attr("role","textbox").attr("aria-readonly","true"),this.$selection.attr("aria-labelledby",i),this.$selection.on("mousedown",function(e){1===e.which&&n.trigger("toggle",{originalEvent:e})}),this.$selection.on("focus",function(e){}),this.$selection.on("blur",function(e){}),t.on("focus",function(e){t.isOpen()||n.$selection.trigger("focus")})},r.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},r.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},r.prototype.selectionContainer=function(){return e("<span></span>")},r.prototype.update=function(e){if(0!==e.length){var t=e[0],n=this.$selection.find(".select2-selection__rendered"),i=this.display(t,n);n.empty().append(i);var r=t.title||t.text;r?n.attr("title",r):n.removeAttr("title")}else this.clear()},r}),e.define("select2/selection/multiple",["jquery","./base","../utils"],function(r,e,l){function n(e,t){n.__super__.constructor.apply(this,arguments)}return l.Extend(n,e),n.prototype.render=function(){var e=n.__super__.render.call(this);return e.addClass("select2-selection--multiple"),e.html('<ul class="select2-selection__rendered"></ul>'),e},n.prototype.bind=function(e,t){var i=this;n.__super__.bind.apply(this,arguments),this.$selection.on("click",function(e){i.trigger("toggle",{originalEvent:e})}),this.$selection.on("click",".select2-selection__choice__remove",function(e){if(!i.isDisabled()){var t=r(this).parent(),n=l.GetData(t[0],"data");i.trigger("unselect",{originalEvent:e,data:n})}})},n.prototype.clear=function(){var e=this.$selection.find(".select2-selection__rendered");e.empty(),e.removeAttr("title")},n.prototype.display=function(e,t){var n=this.options.get("templateSelection");return this.options.get("escapeMarkup")(n(e,t))},n.prototype.selectionContainer=function(){return r('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">×</span></li>')},n.prototype.update=function(e){if(this.clear(),0!==e.length){for(var t=[],n=0;n<e.length;n++){var i=e[n],r=this.selectionContainer(),o=this.display(i,r);r.append(o);var s=i.title||i.text;s&&r.attr("title",s),l.StoreData(r[0],"data",i),t.push(r)}var a=this.$selection.find(".select2-selection__rendered");l.appendMany(a,t)}},n}),e.define("select2/selection/placeholder",["../utils"],function(e){function t(e,t,n){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n)}return t.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},t.prototype.createPlaceholder=function(e,t){var n=this.selectionContainer();return n.html(this.display(t)),n.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),n},t.prototype.update=function(e,t){var n=1==t.length&&t[0].id!=this.placeholder.id;if(1<t.length||n)return e.call(this,t);this.clear();var i=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(i)},t}),e.define("select2/selection/allowClear",["jquery","../keys","../utils"],function(r,i,a){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(e){i._handleClear(e)}),t.on("keypress",function(e){i._handleKeyboardClear(e,t)})},e.prototype._handleClear=function(e,t){if(!this.isDisabled()){var n=this.$selection.find(".select2-selection__clear");if(0!==n.length){t.stopPropagation();var i=a.GetData(n[0],"data"),r=this.$element.val();this.$element.val(this.placeholder.id);var o={data:i};if(this.trigger("clear",o),o.prevented)this.$element.val(r);else{for(var s=0;s<i.length;s++)if(o={data:i[s]},this.trigger("unselect",o),o.prevented)return void this.$element.val(r);this.$element.trigger("input").trigger("change"),this.trigger("toggle",{})}}}},e.prototype._handleKeyboardClear=function(e,t,n){n.isOpen()||t.which!=i.DELETE&&t.which!=i.BACKSPACE||this._handleClear(t)},e.prototype.update=function(e,t){if(e.call(this,t),!(0<this.$selection.find(".select2-selection__placeholder").length||0===t.length)){var n=this.options.get("translations").get("removeAllItems"),i=r('<span class="select2-selection__clear" title="'+n()+'">×</span>');a.StoreData(i[0],"data",t),this.$selection.find(".select2-selection__rendered").prepend(i)}},e}),e.define("select2/selection/search",["jquery","../utils","../keys"],function(i,a,l){function e(e,t,n){e.call(this,t,n)}return e.prototype.render=function(e){var t=i('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="searchbox" aria-autocomplete="list" /></li>');this.$searchContainer=t,this.$search=t.find("input");var n=e.call(this);return this._transferTabIndex(),n},e.prototype.bind=function(e,t,n){var i=this,r=t.id+"-results";e.call(this,t,n),t.on("open",function(){i.$search.attr("aria-controls",r),i.$search.trigger("focus")}),t.on("close",function(){i.$search.val(""),i.$search.removeAttr("aria-controls"),i.$search.removeAttr("aria-activedescendant"),i.$search.trigger("focus")}),t.on("enable",function(){i.$search.prop("disabled",!1),i._transferTabIndex()}),t.on("disable",function(){i.$search.prop("disabled",!0)}),t.on("focus",function(e){i.$search.trigger("focus")}),t.on("results:focus",function(e){e.data._resultId?i.$search.attr("aria-activedescendant",e.data._resultId):i.$search.removeAttr("aria-activedescendant")}),this.$selection.on("focusin",".select2-search--inline",function(e){i.trigger("focus",e)}),this.$selection.on("focusout",".select2-search--inline",function(e){i._handleBlur(e)}),this.$selection.on("keydown",".select2-search--inline",function(e){if(e.stopPropagation(),i.trigger("keypress",e),i._keyUpPrevented=e.isDefaultPrevented(),e.which===l.BACKSPACE&&""===i.$search.val()){var t=i.$searchContainer.prev(".select2-selection__choice");if(0<t.length){var n=a.GetData(t[0],"data");i.searchRemoveChoice(n),e.preventDefault()}}}),this.$selection.on("click",".select2-search--inline",function(e){i.$search.val()&&e.stopPropagation()});var o=document.documentMode,s=o&&o<=11;this.$selection.on("input.searchcheck",".select2-search--inline",function(e){s?i.$selection.off("input.search input.searchcheck"):i.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(e){if(s&&"input"===e.type)i.$selection.off("input.search input.searchcheck");else{var t=e.which;t!=l.SHIFT&&t!=l.CTRL&&t!=l.ALT&&t!=l.TAB&&i.handleSearch(e)}})},e.prototype._transferTabIndex=function(e){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},e.prototype.createPlaceholder=function(e,t){this.$search.attr("placeholder",t.text)},e.prototype.update=function(e,t){var n=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),e.call(this,t),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),n&&this.$search.trigger("focus")},e.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var e=this.$search.val();this.trigger("query",{term:e})}this._keyUpPrevented=!1},e.prototype.searchRemoveChoice=function(e,t){this.trigger("unselect",{data:t}),this.$search.val(t.text),this.handleSearch()},e.prototype.resizeSearch=function(){this.$search.css("width","25px");var e="";""!==this.$search.attr("placeholder")?e=this.$selection.find(".select2-selection__rendered").width():e=.75*(this.$search.val().length+1)+"em";this.$search.css("width",e)},e}),e.define("select2/selection/eventRelay",["jquery"],function(s){function e(){}return e.prototype.bind=function(e,t,n){var i=this,r=["open","opening","close","closing","select","selecting","unselect","unselecting","clear","clearing"],o=["opening","closing","selecting","unselecting","clearing"];e.call(this,t,n),t.on("*",function(e,t){if(-1!==s.inArray(e,r)){t=t||{};var n=s.Event("select2:"+e,{params:t});i.$element.trigger(n),-1!==s.inArray(e,o)&&(t.prevented=n.isDefaultPrevented())}})},e}),e.define("select2/translation",["jquery","require"],function(t,n){function i(e){this.dict=e||{}}return i.prototype.all=function(){return this.dict},i.prototype.get=function(e){return this.dict[e]},i.prototype.extend=function(e){this.dict=t.extend({},e.all(),this.dict)},i._cache={},i.loadPath=function(e){if(!(e in i._cache)){var t=n(e);i._cache[e]=t}return new i(i._cache[e])},i}),e.define("select2/diacritics",[],function(){return{"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Œ":"OE","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","œ":"oe","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ώ":"ω","ς":"σ","’":"'"}}),e.define("select2/data/base",["../utils"],function(i){function n(e,t){n.__super__.constructor.call(this)}return i.Extend(n,i.Observable),n.prototype.current=function(e){throw new Error("The `current` method must be defined in child classes.")},n.prototype.query=function(e,t){throw new Error("The `query` method must be defined in child classes.")},n.prototype.bind=function(e,t){},n.prototype.destroy=function(){},n.prototype.generateResultId=function(e,t){var n=e.id+"-result-";return n+=i.generateChars(4),null!=t.id?n+="-"+t.id.toString():n+="-"+i.generateChars(4),n},n}),e.define("select2/data/select",["./base","../utils","jquery"],function(e,a,l){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return a.Extend(n,e),n.prototype.current=function(e){var n=[],i=this;this.$element.find(":selected").each(function(){var e=l(this),t=i.item(e);n.push(t)}),e(n)},n.prototype.select=function(r){var o=this;if(r.selected=!0,l(r.element).is("option"))return r.element.selected=!0,void this.$element.trigger("input").trigger("change");if(this.$element.prop("multiple"))this.current(function(e){var t=[];(r=[r]).push.apply(r,e);for(var n=0;n<r.length;n++){var i=r[n].id;-1===l.inArray(i,t)&&t.push(i)}o.$element.val(t),o.$element.trigger("input").trigger("change")});else{var e=r.id;this.$element.val(e),this.$element.trigger("input").trigger("change")}},n.prototype.unselect=function(r){var o=this;if(this.$element.prop("multiple")){if(r.selected=!1,l(r.element).is("option"))return r.element.selected=!1,void this.$element.trigger("input").trigger("change");this.current(function(e){for(var t=[],n=0;n<e.length;n++){var i=e[n].id;i!==r.id&&-1===l.inArray(i,t)&&t.push(i)}o.$element.val(t),o.$element.trigger("input").trigger("change")})}},n.prototype.bind=function(e,t){var n=this;(this.container=e).on("select",function(e){n.select(e.data)}),e.on("unselect",function(e){n.unselect(e.data)})},n.prototype.destroy=function(){this.$element.find("*").each(function(){a.RemoveData(this)})},n.prototype.query=function(i,e){var r=[],o=this;this.$element.children().each(function(){var e=l(this);if(e.is("option")||e.is("optgroup")){var t=o.item(e),n=o.matches(i,t);null!==n&&r.push(n)}}),e({results:r})},n.prototype.addOptions=function(e){a.appendMany(this.$element,e)},n.prototype.option=function(e){var t;e.children?(t=document.createElement("optgroup")).label=e.text:void 0!==(t=document.createElement("option")).textContent?t.textContent=e.text:t.innerText=e.text,void 0!==e.id&&(t.value=e.id),e.disabled&&(t.disabled=!0),e.selected&&(t.selected=!0),e.title&&(t.title=e.title);var n=l(t),i=this._normalizeItem(e);return i.element=t,a.StoreData(t,"data",i),n},n.prototype.item=function(e){var t={};if(null!=(t=a.GetData(e[0],"data")))return t;if(e.is("option"))t={id:e.val(),text:e.text(),disabled:e.prop("disabled"),selected:e.prop("selected"),title:e.prop("title")};else if(e.is("optgroup")){t={text:e.prop("label"),children:[],title:e.prop("title")};for(var n=e.children("option"),i=[],r=0;r<n.length;r++){var o=l(n[r]),s=this.item(o);i.push(s)}t.children=i}return(t=this._normalizeItem(t)).element=e[0],a.StoreData(e[0],"data",t),t},n.prototype._normalizeItem=function(e){e!==Object(e)&&(e={id:e,text:e});return null!=(e=l.extend({},{text:""},e)).id&&(e.id=e.id.toString()),null!=e.text&&(e.text=e.text.toString()),null==e._resultId&&e.id&&null!=this.container&&(e._resultId=this.generateResultId(this.container,e)),l.extend({},{selected:!1,disabled:!1},e)},n.prototype.matches=function(e,t){return this.options.get("matcher")(e,t)},n}),e.define("select2/data/array",["./select","../utils","jquery"],function(e,f,g){function i(e,t){this._dataToConvert=t.get("data")||[],i.__super__.constructor.call(this,e,t)}return f.Extend(i,e),i.prototype.bind=function(e,t){i.__super__.bind.call(this,e,t),this.addOptions(this.convertToOptions(this._dataToConvert))},i.prototype.select=function(n){var e=this.$element.find("option").filter(function(e,t){return t.value==n.id.toString()});0===e.length&&(e=this.option(n),this.addOptions(e)),i.__super__.select.call(this,n)},i.prototype.convertToOptions=function(e){var t=this,n=this.$element.find("option"),i=n.map(function(){return t.item(g(this)).id}).get(),r=[];function o(e){return function(){return g(this).val()==e.id}}for(var s=0;s<e.length;s++){var a=this._normalizeItem(e[s]);if(0<=g.inArray(a.id,i)){var l=n.filter(o(a)),c=this.item(l),u=g.extend(!0,{},a,c),d=this.option(u);l.replaceWith(d)}else{var p=this.option(a);if(a.children){var h=this.convertToOptions(a.children);f.appendMany(p,h)}r.push(p)}}return r},i}),e.define("select2/data/ajax",["./array","../utils","jquery"],function(e,t,o){function n(e,t){this.ajaxOptions=this._applyDefaults(t.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),n.__super__.constructor.call(this,e,t)}return t.Extend(n,e),n.prototype._applyDefaults=function(e){var t={data:function(e){return o.extend({},e,{q:e.term})},transport:function(e,t,n){var i=o.ajax(e);return i.then(t),i.fail(n),i}};return o.extend({},t,e,!0)},n.prototype.processResults=function(e){return e},n.prototype.query=function(n,i){var r=this;null!=this._request&&(o.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var t=o.extend({type:"GET"},this.ajaxOptions);function e(){var e=t.transport(t,function(e){var t=r.processResults(e,n);r.options.get("debug")&&window.console&&console.error&&(t&&t.results&&o.isArray(t.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),i(t)},function(){"status"in e&&(0===e.status||"0"===e.status)||r.trigger("results:message",{message:"errorLoading"})});r._request=e}"function"==typeof t.url&&(t.url=t.url.call(this.$element,n)),"function"==typeof t.data&&(t.data=t.data.call(this.$element,n)),this.ajaxOptions.delay&&null!=n.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(e,this.ajaxOptions.delay)):e()},n}),e.define("select2/data/tags",["jquery"],function(u){function e(e,t,n){var i=n.get("tags"),r=n.get("createTag");void 0!==r&&(this.createTag=r);var o=n.get("insertTag");if(void 0!==o&&(this.insertTag=o),e.call(this,t,n),u.isArray(i))for(var s=0;s<i.length;s++){var a=i[s],l=this._normalizeItem(a),c=this.option(l);this.$element.append(c)}}return e.prototype.query=function(e,c,u){var d=this;this._removeOldTags(),null!=c.term&&null==c.page?e.call(this,c,function e(t,n){for(var i=t.results,r=0;r<i.length;r++){var o=i[r],s=null!=o.children&&!e({results:o.children},!0);if((o.text||"").toUpperCase()===(c.term||"").toUpperCase()||s)return!n&&(t.data=i,void u(t))}if(n)return!0;var a=d.createTag(c);if(null!=a){var l=d.option(a);l.attr("data-select2-tag",!0),d.addOptions([l]),d.insertTag(i,a)}t.results=i,u(t)}):e.call(this,c,u)},e.prototype.createTag=function(e,t){var n=u.trim(t.term);return""===n?null:{id:n,text:n}},e.prototype.insertTag=function(e,t,n){t.unshift(n)},e.prototype._removeOldTags=function(e){this.$element.find("option[data-select2-tag]").each(function(){this.selected||u(this).remove()})},e}),e.define("select2/data/tokenizer",["jquery"],function(d){function e(e,t,n){var i=n.get("tokenizer");void 0!==i&&(this.tokenizer=i),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){e.call(this,t,n),this.$search=t.dropdown.$search||t.selection.$search||n.find(".select2-search__field")},e.prototype.query=function(e,t,n){var r=this;t.term=t.term||"";var i=this.tokenizer(t,this.options,function(e){var t,n=r._normalizeItem(e);if(!r.$element.find("option").filter(function(){return d(this).val()===n.id}).length){var i=r.option(n);i.attr("data-select2-tag",!0),r._removeOldTags(),r.addOptions([i])}t=n,r.trigger("select",{data:t})});i.term!==t.term&&(this.$search.length&&(this.$search.val(i.term),this.$search.trigger("focus")),t.term=i.term),e.call(this,t,n)},e.prototype.tokenizer=function(e,t,n,i){for(var r=n.get("tokenSeparators")||[],o=t.term,s=0,a=this.createTag||function(e){return{id:e.term,text:e.term}};s<o.length;){var l=o[s];if(-1!==d.inArray(l,r)){var c=o.substr(0,s),u=a(d.extend({},t,{term:c}));null!=u?(i(u),o=o.substr(s+1)||"",s=0):s++}else s++}return{term:o}},e}),e.define("select2/data/minimumInputLength",[],function(){function e(e,t,n){this.minimumInputLength=n.get("minimumInputLength"),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.term=t.term||"",t.term.length<this.minimumInputLength?this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumInputLength",[],function(){function e(e,t,n){this.maximumInputLength=n.get("maximumInputLength"),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.term=t.term||"",0<this.maximumInputLength&&t.term.length>this.maximumInputLength?this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:t.term,params:t}}):e.call(this,t,n)},e}),e.define("select2/data/maximumSelectionLength",[],function(){function e(e,t,n){this.maximumSelectionLength=n.get("maximumSelectionLength"),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("select",function(){i._checkIfMaximumSelected()})},e.prototype.query=function(e,t,n){var i=this;this._checkIfMaximumSelected(function(){e.call(i,t,n)})},e.prototype._checkIfMaximumSelected=function(e,n){var i=this;this.current(function(e){var t=null!=e?e.length:0;0<i.maximumSelectionLength&&t>=i.maximumSelectionLength?i.trigger("results:message",{message:"maximumSelected",args:{maximum:i.maximumSelectionLength}}):n&&n()})},e}),e.define("select2/dropdown",["jquery","./utils"],function(t,e){function n(e,t){this.$element=e,this.options=t,n.__super__.constructor.call(this)}return e.Extend(n,e.Observable),n.prototype.render=function(){var e=t('<span class="select2-dropdown"><span class="select2-results"></span></span>');return e.attr("dir",this.options.get("dir")),this.$dropdown=e},n.prototype.bind=function(){},n.prototype.position=function(e,t){},n.prototype.destroy=function(){this.$dropdown.remove()},n}),e.define("select2/dropdown/search",["jquery","../utils"],function(o,e){function t(){}return t.prototype.render=function(e){var t=e.call(this),n=o('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="none" spellcheck="false" role="searchbox" aria-autocomplete="list" /></span>');return this.$searchContainer=n,this.$search=n.find("input"),t.prepend(n),t},t.prototype.bind=function(e,t,n){var i=this,r=t.id+"-results";e.call(this,t,n),this.$search.on("keydown",function(e){i.trigger("keypress",e),i._keyUpPrevented=e.isDefaultPrevented()}),this.$search.on("input",function(e){o(this).off("keyup")}),this.$search.on("keyup input",function(e){i.handleSearch(e)}),t.on("open",function(){i.$search.attr("tabindex",0),i.$search.attr("aria-controls",r),i.$search.trigger("focus"),window.setTimeout(function(){i.$search.trigger("focus")},0)}),t.on("close",function(){i.$search.attr("tabindex",-1),i.$search.removeAttr("aria-controls"),i.$search.removeAttr("aria-activedescendant"),i.$search.val(""),i.$search.trigger("blur")}),t.on("focus",function(){t.isOpen()||i.$search.trigger("focus")}),t.on("results:all",function(e){null!=e.query.term&&""!==e.query.term||(i.showSearch(e)?i.$searchContainer.removeClass("select2-search--hide"):i.$searchContainer.addClass("select2-search--hide"))}),t.on("results:focus",function(e){e.data._resultId?i.$search.attr("aria-activedescendant",e.data._resultId):i.$search.removeAttr("aria-activedescendant")})},t.prototype.handleSearch=function(e){if(!this._keyUpPrevented){var t=this.$search.val();this.trigger("query",{term:t})}this._keyUpPrevented=!1},t.prototype.showSearch=function(e,t){return!0},t}),e.define("select2/dropdown/hidePlaceholder",[],function(){function e(e,t,n,i){this.placeholder=this.normalizePlaceholder(n.get("placeholder")),e.call(this,t,n,i)}return e.prototype.append=function(e,t){t.results=this.removePlaceholder(t.results),e.call(this,t)},e.prototype.normalizePlaceholder=function(e,t){return"string"==typeof t&&(t={id:"",text:t}),t},e.prototype.removePlaceholder=function(e,t){for(var n=t.slice(0),i=t.length-1;0<=i;i--){var r=t[i];this.placeholder.id===r.id&&n.splice(i,1)}return n},e}),e.define("select2/dropdown/infiniteScroll",["jquery"],function(n){function e(e,t,n,i){this.lastParams={},e.call(this,t,n,i),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return e.prototype.append=function(e,t){this.$loadingMore.remove(),this.loading=!1,e.call(this,t),this.showLoadingMore(t)&&(this.$results.append(this.$loadingMore),this.loadMoreIfNeeded())},e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("query",function(e){i.lastParams=e,i.loading=!0}),t.on("query:append",function(e){i.lastParams=e,i.loading=!0}),this.$results.on("scroll",this.loadMoreIfNeeded.bind(this))},e.prototype.loadMoreIfNeeded=function(){var e=n.contains(document.documentElement,this.$loadingMore[0]);if(!this.loading&&e){var t=this.$results.offset().top+this.$results.outerHeight(!1);this.$loadingMore.offset().top+this.$loadingMore.outerHeight(!1)<=t+50&&this.loadMore()}},e.prototype.loadMore=function(){this.loading=!0;var e=n.extend({},{page:1},this.lastParams);e.page++,this.trigger("query:append",e)},e.prototype.showLoadingMore=function(e,t){return t.pagination&&t.pagination.more},e.prototype.createLoadingMore=function(){var e=n('<li class="select2-results__option select2-results__option--load-more"role="option" aria-disabled="true"></li>'),t=this.options.get("translations").get("loadingMore");return e.html(t(this.lastParams)),e},e}),e.define("select2/dropdown/attachBody",["jquery","../utils"],function(f,a){function e(e,t,n){this.$dropdownParent=f(n.get("dropdownParent")||document.body),e.call(this,t,n)}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("open",function(){i._showDropdown(),i._attachPositioningHandler(t),i._bindContainerResultHandlers(t)}),t.on("close",function(){i._hideDropdown(),i._detachPositioningHandler(t)}),this.$dropdownContainer.on("mousedown",function(e){e.stopPropagation()})},e.prototype.destroy=function(e){e.call(this),this.$dropdownContainer.remove()},e.prototype.position=function(e,t,n){t.attr("class",n.attr("class")),t.removeClass("select2"),t.addClass("select2-container--open"),t.css({position:"absolute",top:-999999}),this.$container=n},e.prototype.render=function(e){var t=f("<span></span>"),n=e.call(this);return t.append(n),this.$dropdownContainer=t},e.prototype._hideDropdown=function(e){this.$dropdownContainer.detach()},e.prototype._bindContainerResultHandlers=function(e,t){if(!this._containerResultsHandlersBound){var n=this;t.on("results:all",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:append",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("results:message",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("select",function(){n._positionDropdown(),n._resizeDropdown()}),t.on("unselect",function(){n._positionDropdown(),n._resizeDropdown()}),this._containerResultsHandlersBound=!0}},e.prototype._attachPositioningHandler=function(e,t){var n=this,i="scroll.select2."+t.id,r="resize.select2."+t.id,o="orientationchange.select2."+t.id,s=this.$container.parents().filter(a.hasScroll);s.each(function(){a.StoreData(this,"select2-scroll-position",{x:f(this).scrollLeft(),y:f(this).scrollTop()})}),s.on(i,function(e){var t=a.GetData(this,"select2-scroll-position");f(this).scrollTop(t.y)}),f(window).on(i+" "+r+" "+o,function(e){n._positionDropdown(),n._resizeDropdown()})},e.prototype._detachPositioningHandler=function(e,t){var n="scroll.select2."+t.id,i="resize.select2."+t.id,r="orientationchange.select2."+t.id;this.$container.parents().filter(a.hasScroll).off(n),f(window).off(n+" "+i+" "+r)},e.prototype._positionDropdown=function(){var e=f(window),t=this.$dropdown.hasClass("select2-dropdown--above"),n=this.$dropdown.hasClass("select2-dropdown--below"),i=null,r=this.$container.offset();r.bottom=r.top+this.$container.outerHeight(!1);var o={height:this.$container.outerHeight(!1)};o.top=r.top,o.bottom=r.top+o.height;var s=this.$dropdown.outerHeight(!1),a=e.scrollTop(),l=e.scrollTop()+e.height(),c=a<r.top-s,u=l>r.bottom+s,d={left:r.left,top:o.bottom},p=this.$dropdownParent;"static"===p.css("position")&&(p=p.offsetParent());var h={top:0,left:0};(f.contains(document.body,p[0])||p[0].isConnected)&&(h=p.offset()),d.top-=h.top,d.left-=h.left,t||n||(i="below"),u||!c||t?!c&&u&&t&&(i="below"):i="above",("above"==i||t&&"below"!==i)&&(d.top=o.top-h.top-s),null!=i&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+i),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+i)),this.$dropdownContainer.css(d)},e.prototype._resizeDropdown=function(){var e={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(e.minWidth=e.width,e.position="relative",e.width="auto"),this.$dropdown.css(e)},e.prototype._showDropdown=function(e){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},e}),e.define("select2/dropdown/minimumResultsForSearch",[],function(){function e(e,t,n,i){this.minimumResultsForSearch=n.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),e.call(this,t,n,i)}return e.prototype.showSearch=function(e,t){return!(function e(t){for(var n=0,i=0;i<t.length;i++){var r=t[i];r.children?n+=e(r.children):n++}return n}(t.data.results)<this.minimumResultsForSearch)&&e.call(this,t)},e}),e.define("select2/dropdown/selectOnClose",["../utils"],function(o){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("close",function(e){i._handleSelectOnClose(e)})},e.prototype._handleSelectOnClose=function(e,t){if(t&&null!=t.originalSelect2Event){var n=t.originalSelect2Event;if("select"===n._type||"unselect"===n._type)return}var i=this.getHighlightedResults();if(!(i.length<1)){var r=o.GetData(i[0],"data");null!=r.element&&r.element.selected||null==r.element&&r.selected||this.trigger("select",{data:r})}},e}),e.define("select2/dropdown/closeOnSelect",[],function(){function e(){}return e.prototype.bind=function(e,t,n){var i=this;e.call(this,t,n),t.on("select",function(e){i._selectTriggered(e)}),t.on("unselect",function(e){i._selectTriggered(e)})},e.prototype._selectTriggered=function(e,t){var n=t.originalEvent;n&&(n.ctrlKey||n.metaKey)||this.trigger("close",{originalEvent:n,originalSelect2Event:t})},e}),e.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Please delete "+t+" character";return 1!=t&&(n+="s"),n},inputTooShort:function(e){return"Please enter "+(e.minimum-e.input.length)+" or more characters"},loadingMore:function(){return"Loading more results…"},maximumSelected:function(e){var t="You can only select "+e.maximum+" item";return 1!=e.maximum&&(t+="s"),t},noResults:function(){return"No results found"},searching:function(){return"Searching…"},removeAllItems:function(){return"Remove all items"}}}),e.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(c,u,d,p,h,f,g,m,v,y,s,t,_,w,$,b,A,x,D,S,C,E,O,T,q,j,L,I,e){function n(){this.reset()}return n.prototype.apply=function(e){if(null==(e=c.extend(!0,{},this.defaults,e)).dataAdapter){if(null!=e.ajax?e.dataAdapter=$:null!=e.data?e.dataAdapter=w:e.dataAdapter=_,0<e.minimumInputLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,x)),0<e.maximumInputLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,D)),0<e.maximumSelectionLength&&(e.dataAdapter=y.Decorate(e.dataAdapter,S)),e.tags&&(e.dataAdapter=y.Decorate(e.dataAdapter,b)),null==e.tokenSeparators&&null==e.tokenizer||(e.dataAdapter=y.Decorate(e.dataAdapter,A)),null!=e.query){var t=u(e.amdBase+"compat/query");e.dataAdapter=y.Decorate(e.dataAdapter,t)}if(null!=e.initSelection){var n=u(e.amdBase+"compat/initSelection");e.dataAdapter=y.Decorate(e.dataAdapter,n)}}if(null==e.resultsAdapter&&(e.resultsAdapter=d,null!=e.ajax&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,T)),null!=e.placeholder&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,O)),e.selectOnClose&&(e.resultsAdapter=y.Decorate(e.resultsAdapter,L))),null==e.dropdownAdapter){if(e.multiple)e.dropdownAdapter=C;else{var i=y.Decorate(C,E);e.dropdownAdapter=i}if(0!==e.minimumResultsForSearch&&(e.dropdownAdapter=y.Decorate(e.dropdownAdapter,j)),e.closeOnSelect&&(e.dropdownAdapter=y.Decorate(e.dropdownAdapter,I)),null!=e.dropdownCssClass||null!=e.dropdownCss||null!=e.adaptDropdownCssClass){var r=u(e.amdBase+"compat/dropdownCss");e.dropdownAdapter=y.Decorate(e.dropdownAdapter,r)}e.dropdownAdapter=y.Decorate(e.dropdownAdapter,q)}if(null==e.selectionAdapter){if(e.multiple?e.selectionAdapter=h:e.selectionAdapter=p,null!=e.placeholder&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,f)),e.allowClear&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,g)),e.multiple&&(e.selectionAdapter=y.Decorate(e.selectionAdapter,m)),null!=e.containerCssClass||null!=e.containerCss||null!=e.adaptContainerCssClass){var o=u(e.amdBase+"compat/containerCss");e.selectionAdapter=y.Decorate(e.selectionAdapter,o)}e.selectionAdapter=y.Decorate(e.selectionAdapter,v)}e.language=this._resolveLanguage(e.language),e.language.push("en");for(var s=[],a=0;a<e.language.length;a++){var l=e.language[a];-1===s.indexOf(l)&&s.push(l)}return e.language=s,e.translations=this._processTranslations(e.language,e.debug),e},n.prototype.reset=function(){function a(e){return e.replace(/[^\u0000-\u007E]/g,function(e){return t[e]||e})}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:y.escapeMarkup,language:{},matcher:function e(t,n){if(""===c.trim(t.term))return n;if(n.children&&0<n.children.length){for(var i=c.extend(!0,{},n),r=n.children.length-1;0<=r;r--)null==e(t,n.children[r])&&i.children.splice(r,1);return 0<i.children.length?i:e(t,i)}var o=a(n.text).toUpperCase(),s=a(t.term).toUpperCase();return-1<o.indexOf(s)?n:null},minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,scrollAfterSelect:!1,sorter:function(e){return e},templateResult:function(e){return e.text},templateSelection:function(e){return e.text},theme:"default",width:"resolve"}},n.prototype.applyFromElement=function(e,t){var n=e.language,i=this.defaults.language,r=t.prop("lang"),o=t.closest("[lang]").prop("lang"),s=Array.prototype.concat.call(this._resolveLanguage(r),this._resolveLanguage(n),this._resolveLanguage(i),this._resolveLanguage(o));return e.language=s,e},n.prototype._resolveLanguage=function(e){if(!e)return[];if(c.isEmptyObject(e))return[];if(c.isPlainObject(e))return[e];var t;t=c.isArray(e)?e:[e];for(var n=[],i=0;i<t.length;i++)if(n.push(t[i]),"string"==typeof t[i]&&0<t[i].indexOf("-")){var r=t[i].split("-")[0];n.push(r)}return n},n.prototype._processTranslations=function(e,t){for(var n=new s,i=0;i<e.length;i++){var r=new s,o=e[i];if("string"==typeof o)try{r=s.loadPath(o)}catch(e){try{o=this.defaults.amdLanguageBase+o,r=s.loadPath(o)}catch(e){t&&window.console&&console.warn&&console.warn('Select2: The language file for "'+o+'" could not be automatically loaded. A fallback will be used instead.')}}else r=c.isPlainObject(o)?new s(o):o;n.extend(r)}return n},n.prototype.set=function(e,t){var n={};n[c.camelCase(e)]=t;var i=y._convertData(n);c.extend(!0,this.defaults,i)},new n}),e.define("select2/options",["require","jquery","./defaults","./utils"],function(i,d,r,p){function e(e,t){if(this.options=e,null!=t&&this.fromElement(t),null!=t&&(this.options=r.applyFromElement(this.options,t)),this.options=r.apply(this.options),t&&t.is("input")){var n=i(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=p.Decorate(this.options.dataAdapter,n)}}return e.prototype.fromElement=function(e){var t=["select2"];null==this.options.multiple&&(this.options.multiple=e.prop("multiple")),null==this.options.disabled&&(this.options.disabled=e.prop("disabled")),null==this.options.dir&&(e.prop("dir")?this.options.dir=e.prop("dir"):e.closest("[dir]").prop("dir")?this.options.dir=e.closest("[dir]").prop("dir"):this.options.dir="ltr"),e.prop("disabled",this.options.disabled),e.prop("multiple",this.options.multiple),p.GetData(e[0],"select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),p.StoreData(e[0],"data",p.GetData(e[0],"select2Tags")),p.StoreData(e[0],"tags",!0)),p.GetData(e[0],"ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),e.attr("ajax--url",p.GetData(e[0],"ajaxUrl")),p.StoreData(e[0],"ajax-Url",p.GetData(e[0],"ajaxUrl")));var n={};function i(e,t){return t.toUpperCase()}for(var r=0;r<e[0].attributes.length;r++){var o=e[0].attributes[r].name,s="data-";if(o.substr(0,s.length)==s){var a=o.substring(s.length),l=p.GetData(e[0],a);n[a.replace(/-([a-z])/g,i)]=l}}d.fn.jquery&&"1."==d.fn.jquery.substr(0,2)&&e[0].dataset&&(n=d.extend(!0,{},e[0].dataset,n));var c=d.extend(!0,{},p.GetData(e[0]),n);for(var u in c=p._convertData(c))-1<d.inArray(u,t)||(d.isPlainObject(this.options[u])?d.extend(this.options[u],c[u]):this.options[u]=c[u]);return this},e.prototype.get=function(e){return this.options[e]},e.prototype.set=function(e,t){this.options[e]=t},e}),e.define("select2/core",["jquery","./options","./utils","./keys"],function(o,c,u,i){var d=function(e,t){null!=u.GetData(e[0],"select2")&&u.GetData(e[0],"select2").destroy(),this.$element=e,this.id=this._generateId(e),t=t||{},this.options=new c(t,e),d.__super__.constructor.call(this);var n=e.attr("tabindex")||0;u.StoreData(e[0],"old-tabindex",n),e.attr("tabindex","-1");var i=this.options.get("dataAdapter");this.dataAdapter=new i(e,this.options);var r=this.render();this._placeContainer(r);var o=this.options.get("selectionAdapter");this.selection=new o(e,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,r);var s=this.options.get("dropdownAdapter");this.dropdown=new s(e,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,r);var a=this.options.get("resultsAdapter");this.results=new a(e,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var l=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(e){l.trigger("selection:update",{data:e})}),e.addClass("select2-hidden-accessible"),e.attr("aria-hidden","true"),this._syncAttributes(),u.StoreData(e[0],"select2",this),e.data("select2",this)};return u.Extend(d,u.Observable),d.prototype._generateId=function(e){return"select2-"+(null!=e.attr("id")?e.attr("id"):null!=e.attr("name")?e.attr("name")+"-"+u.generateChars(2):u.generateChars(4)).replace(/(:|\.|\[|\]|,)/g,"")},d.prototype._placeContainer=function(e){e.insertAfter(this.$element);var t=this._resolveWidth(this.$element,this.options.get("width"));null!=t&&e.css("width",t)},d.prototype._resolveWidth=function(e,t){var n=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==t){var i=this._resolveWidth(e,"style");return null!=i?i:this._resolveWidth(e,"element")}if("element"==t){var r=e.outerWidth(!1);return r<=0?"auto":r+"px"}if("style"!=t)return"computedstyle"!=t?t:window.getComputedStyle(e[0]).width;var o=e.attr("style");if("string"!=typeof o)return null;for(var s=o.split(";"),a=0,l=s.length;a<l;a+=1){var c=s[a].replace(/\s/g,"").match(n);if(null!==c&&1<=c.length)return c[1]}return null},d.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},d.prototype._registerDomEvents=function(){var t=this;this.$element.on("change.select2",function(){t.dataAdapter.current(function(e){t.trigger("selection:update",{data:e})})}),this.$element.on("focus.select2",function(e){t.trigger("focus",e)}),this._syncA=u.bind(this._syncAttributes,this),this._syncS=u.bind(this._syncSubtree,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._syncA);var e=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=e?(this._observer=new e(function(e){t._syncA(),t._syncS(null,e)}),this._observer.observe(this.$element[0],{attributes:!0,childList:!0,subtree:!1})):this.$element[0].addEventListener&&(this.$element[0].addEventListener("DOMAttrModified",t._syncA,!1),this.$element[0].addEventListener("DOMNodeInserted",t._syncS,!1),this.$element[0].addEventListener("DOMNodeRemoved",t._syncS,!1))},d.prototype._registerDataEvents=function(){var n=this;this.dataAdapter.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerSelectionEvents=function(){var n=this,i=["toggle","focus"];this.selection.on("toggle",function(){n.toggleDropdown()}),this.selection.on("focus",function(e){n.focus(e)}),this.selection.on("*",function(e,t){-1===o.inArray(e,i)&&n.trigger(e,t)})},d.prototype._registerDropdownEvents=function(){var n=this;this.dropdown.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerResultsEvents=function(){var n=this;this.results.on("*",function(e,t){n.trigger(e,t)})},d.prototype._registerEvents=function(){var n=this;this.on("open",function(){n.$container.addClass("select2-container--open")}),this.on("close",function(){n.$container.removeClass("select2-container--open")}),this.on("enable",function(){n.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){n.$container.addClass("select2-container--disabled")}),this.on("blur",function(){n.$container.removeClass("select2-container--focus")}),this.on("query",function(t){n.isOpen()||n.trigger("open",{}),this.dataAdapter.query(t,function(e){n.trigger("results:all",{data:e,query:t})})}),this.on("query:append",function(t){this.dataAdapter.query(t,function(e){n.trigger("results:append",{data:e,query:t})})}),this.on("keypress",function(e){var t=e.which;n.isOpen()?t===i.ESC||t===i.TAB||t===i.UP&&e.altKey?(n.close(e),e.preventDefault()):t===i.ENTER?(n.trigger("results:select",{}),e.preventDefault()):t===i.SPACE&&e.ctrlKey?(n.trigger("results:toggle",{}),e.preventDefault()):t===i.UP?(n.trigger("results:previous",{}),e.preventDefault()):t===i.DOWN&&(n.trigger("results:next",{}),e.preventDefault()):(t===i.ENTER||t===i.SPACE||t===i.DOWN&&e.altKey)&&(n.open(),e.preventDefault())})},d.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.isDisabled()?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},d.prototype._isChangeMutation=function(e,t){var n=!1,i=this;if(!e||!e.target||"OPTION"===e.target.nodeName||"OPTGROUP"===e.target.nodeName){if(t)if(t.addedNodes&&0<t.addedNodes.length)for(var r=0;r<t.addedNodes.length;r++){t.addedNodes[r].selected&&(n=!0)}else t.removedNodes&&0<t.removedNodes.length?n=!0:o.isArray(t)&&o.each(t,function(e,t){if(i._isChangeMutation(e,t))return!(n=!0)});else n=!0;return n}},d.prototype._syncSubtree=function(e,t){var n=this._isChangeMutation(e,t),i=this;n&&this.dataAdapter.current(function(e){i.trigger("selection:update",{data:e})})},d.prototype.trigger=function(e,t){var n=d.__super__.trigger,i={open:"opening",close:"closing",select:"selecting",unselect:"unselecting",clear:"clearing"};if(void 0===t&&(t={}),e in i){var r=i[e],o={prevented:!1,name:e,args:t};if(n.call(this,r,o),o.prevented)return void(t.prevented=!0)}n.call(this,e,t)},d.prototype.toggleDropdown=function(){this.isDisabled()||(this.isOpen()?this.close():this.open())},d.prototype.open=function(){this.isOpen()||this.isDisabled()||this.trigger("query",{})},d.prototype.close=function(e){this.isOpen()&&this.trigger("close",{originalEvent:e})},d.prototype.isEnabled=function(){return!this.isDisabled()},d.prototype.isDisabled=function(){return this.options.get("disabled")},d.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},d.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},d.prototype.focus=function(e){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},d.prototype.enable=function(e){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),null!=e&&0!==e.length||(e=[!0]);var t=!e[0];this.$element.prop("disabled",t)},d.prototype.data=function(){this.options.get("debug")&&0<arguments.length&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var t=[];return this.dataAdapter.current(function(e){t=e}),t},d.prototype.val=function(e){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==e||0===e.length)return this.$element.val();var t=e[0];o.isArray(t)&&(t=o.map(t,function(e){return e.toString()})),this.$element.val(t).trigger("input").trigger("change")},d.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._syncA),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&(this.$element[0].removeEventListener("DOMAttrModified",this._syncA,!1),this.$element[0].removeEventListener("DOMNodeInserted",this._syncS,!1),this.$element[0].removeEventListener("DOMNodeRemoved",this._syncS,!1)),this._syncA=null,this._syncS=null,this.$element.off(".select2"),this.$element.attr("tabindex",u.GetData(this.$element[0],"old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),u.RemoveData(this.$element[0]),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},d.prototype.render=function(){var e=o('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return e.attr("dir",this.options.get("dir")),this.$container=e,this.$container.addClass("select2-container--"+this.options.get("theme")),u.StoreData(e[0],"element",this.$element),e},d}),e.define("select2/compat/utils",["jquery"],function(s){return{syncCssClasses:function(e,t,n){var i,r,o=[];(i=s.trim(e.attr("class")))&&s((i=""+i).split(/\s+/)).each(function(){0===this.indexOf("select2-")&&o.push(this)}),(i=s.trim(t.attr("class")))&&s((i=""+i).split(/\s+/)).each(function(){0!==this.indexOf("select2-")&&null!=(r=n(this))&&o.push(r)}),e.attr("class",o.join(" "))}}}),e.define("select2/compat/containerCss",["jquery","./utils"],function(s,a){function l(e){return null}function e(){}return e.prototype.render=function(e){var t=e.call(this),n=this.options.get("containerCssClass")||"";s.isFunction(n)&&(n=n(this.$element));var i=this.options.get("adaptContainerCssClass");if(i=i||l,-1!==n.indexOf(":all:")){n=n.replace(":all:","");var r=i;i=function(e){var t=r(e);return null!=t?t+" "+e:e}}var o=this.options.get("containerCss")||{};return s.isFunction(o)&&(o=o(this.$element)),a.syncCssClasses(t,this.$element,i),t.css(o),t.addClass(n),t},e}),e.define("select2/compat/dropdownCss",["jquery","./utils"],function(s,a){function l(e){return null}function e(){}return e.prototype.render=function(e){var t=e.call(this),n=this.options.get("dropdownCssClass")||"";s.isFunction(n)&&(n=n(this.$element));var i=this.options.get("adaptDropdownCssClass");if(i=i||l,-1!==n.indexOf(":all:")){n=n.replace(":all:","");var r=i;i=function(e){var t=r(e);return null!=t?t+" "+e:e}}var o=this.options.get("dropdownCss")||{};return s.isFunction(o)&&(o=o(this.$element)),a.syncCssClasses(t,this.$element,i),t.css(o),t.addClass(n),t},e}),e.define("select2/compat/initSelection",["jquery"],function(i){function e(e,t,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `initSelection` option has been deprecated in favor of a custom data adapter that overrides the `current` method. This method is now called multiple times instead of a single time when the instance is initialized. Support will be removed for the `initSelection` option in future versions of Select2"),this.initSelection=n.get("initSelection"),this._isInitialized=!1,e.call(this,t,n)}return e.prototype.current=function(e,t){var n=this;this._isInitialized?e.call(this,t):this.initSelection.call(null,this.$element,function(e){n._isInitialized=!0,i.isArray(e)||(e=[e]),t(e)})},e}),e.define("select2/compat/inputData",["jquery","../utils"],function(s,i){function e(e,t,n){this._currentData=[],this._valueSeparator=n.get("valueSeparator")||",","hidden"===t.prop("type")&&n.get("debug")&&console&&console.warn&&console.warn("Select2: Using a hidden input with Select2 is no longer supported and may stop working in the future. It is recommended to use a `<select>` element instead."),e.call(this,t,n)}return e.prototype.current=function(e,t){function i(e,t){var n=[];return e.selected||-1!==s.inArray(e.id,t)?(e.selected=!0,n.push(e)):e.selected=!1,e.children&&n.push.apply(n,i(e.children,t)),n}for(var n=[],r=0;r<this._currentData.length;r++){var o=this._currentData[r];n.push.apply(n,i(o,this.$element.val().split(this._valueSeparator)))}t(n)},e.prototype.select=function(e,t){if(this.options.get("multiple")){var n=this.$element.val();n+=this._valueSeparator+t.id,this.$element.val(n),this.$element.trigger("input").trigger("change")}else this.current(function(e){s.map(e,function(e){e.selected=!1})}),this.$element.val(t.id),this.$element.trigger("input").trigger("change")},e.prototype.unselect=function(e,r){var o=this;r.selected=!1,this.current(function(e){for(var t=[],n=0;n<e.length;n++){var i=e[n];r.id!=i.id&&t.push(i.id)}o.$element.val(t.join(o._valueSeparator)),o.$element.trigger("input").trigger("change")})},e.prototype.query=function(e,t,n){for(var i=[],r=0;r<this._currentData.length;r++){var o=this._currentData[r],s=this.matches(t,o);null!==s&&i.push(s)}n({results:i})},e.prototype.addOptions=function(e,t){var n=s.map(t,function(e){return i.GetData(e[0],"data")});this._currentData.push.apply(this._currentData,n)},e}),e.define("select2/compat/matcher",["jquery"],function(s){return function(o){return function(e,t){var n=s.extend(!0,{},t);if(null==e.term||""===s.trim(e.term))return n;if(t.children){for(var i=t.children.length-1;0<=i;i--){var r=t.children[i];o(e.term,r.text,r)||n.children.splice(i,1)}if(0<n.children.length)return n}return o(e.term,t.text,t)?n:null}}}),e.define("select2/compat/query",[],function(){function e(e,t,n){n.get("debug")&&window.console&&console.warn&&console.warn("Select2: The `query` option has been deprecated in favor of a custom data adapter that overrides the `query` method. Support will be removed for the `query` option in future versions of Select2."),e.call(this,t,n)}return e.prototype.query=function(e,t,n){t.callback=n,this.options.get("query").call(null,t)},e}),e.define("select2/dropdown/attachContainer",[],function(){function e(e,t,n){e.call(this,t,n)}return e.prototype.position=function(e,t,n){n.find(".dropdown-wrapper").append(t),t.addClass("select2-dropdown--below"),n.addClass("select2-container--below")},e}),e.define("select2/dropdown/stopPropagation",[],function(){function e(){}return e.prototype.bind=function(e,t,n){e.call(this,t,n);this.$dropdown.on(["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"].join(" "),function(e){e.stopPropagation()})},e}),e.define("select2/selection/stopPropagation",[],function(){function e(){}return e.prototype.bind=function(e,t,n){e.call(this,t,n);this.$selection.on(["blur","change","click","dblclick","focus","focusin","focusout","input","keydown","keyup","keypress","mousedown","mouseenter","mouseleave","mousemove","mouseover","mouseup","search","touchend","touchstart"].join(" "),function(e){e.stopPropagation()})},e}),l=function(p){var h,f,e=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],t="onwheel"in document||9<=document.documentMode?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],g=Array.prototype.slice;if(p.event.fixHooks)for(var n=e.length;n;)p.event.fixHooks[e[--n]]=p.event.mouseHooks;var m=p.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var e=t.length;e;)this.addEventListener(t[--e],i,!1);else this.onmousewheel=i;p.data(this,"mousewheel-line-height",m.getLineHeight(this)),p.data(this,"mousewheel-page-height",m.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var e=t.length;e;)this.removeEventListener(t[--e],i,!1);else this.onmousewheel=null;p.removeData(this,"mousewheel-line-height"),p.removeData(this,"mousewheel-page-height")},getLineHeight:function(e){var t=p(e),n=t["offsetParent"in p.fn?"offsetParent":"parent"]();return n.length||(n=p("body")),parseInt(n.css("fontSize"),10)||parseInt(t.css("fontSize"),10)||16},getPageHeight:function(e){return p(e).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};function i(e){var t,n=e||window.event,i=g.call(arguments,1),r=0,o=0,s=0,a=0,l=0;if((e=p.event.fix(n)).type="mousewheel","detail"in n&&(s=-1*n.detail),"wheelDelta"in n&&(s=n.wheelDelta),"wheelDeltaY"in n&&(s=n.wheelDeltaY),"wheelDeltaX"in n&&(o=-1*n.wheelDeltaX),"axis"in n&&n.axis===n.HORIZONTAL_AXIS&&(o=-1*s,s=0),r=0===s?o:s,"deltaY"in n&&(r=s=-1*n.deltaY),"deltaX"in n&&(o=n.deltaX,0===s&&(r=-1*o)),0!==s||0!==o){if(1===n.deltaMode){var c=p.data(this,"mousewheel-line-height");r*=c,s*=c,o*=c}else if(2===n.deltaMode){var u=p.data(this,"mousewheel-page-height");r*=u,s*=u,o*=u}if(t=Math.max(Math.abs(s),Math.abs(o)),(!f||t<f)&&y(n,f=t)&&(f/=40),y(n,t)&&(r/=40,o/=40,s/=40),r=Math[1<=r?"floor":"ceil"](r/f),o=Math[1<=o?"floor":"ceil"](o/f),s=Math[1<=s?"floor":"ceil"](s/f),m.settings.normalizeOffset&&this.getBoundingClientRect){var d=this.getBoundingClientRect();a=e.clientX-d.left,l=e.clientY-d.top}return e.deltaX=o,e.deltaY=s,e.deltaFactor=f,e.offsetX=a,e.offsetY=l,e.deltaMode=0,i.unshift(e,r,o,s),h&&clearTimeout(h),h=setTimeout(v,200),(p.event.dispatch||p.event.handle).apply(this,i)}}function v(){f=null}function y(e,t){return m.settings.adjustOldDeltas&&"mousewheel"===e.type&&t%120==0}p.fn.extend({mousewheel:function(e){return e?this.bind("mousewheel",e):this.trigger("mousewheel")},unmousewheel:function(e){return this.unbind("mousewheel",e)}})},"function"==typeof e.define&&e.define.amd?e.define("jquery-mousewheel",["jquery"],l):"object"==typeof exports?module.exports=l:l(d),e.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults","./select2/utils"],function(r,e,o,t,s){if(null==r.fn.select2){var a=["open","close","destroy"];r.fn.select2=function(t){if("object"==typeof(t=t||{}))return this.each(function(){var e=r.extend(!0,{},t);new o(r(this),e)}),this;if("string"!=typeof t)throw new Error("Invalid arguments for Select2: "+t);var n,i=Array.prototype.slice.call(arguments,1);return this.each(function(){var e=s.GetData(this,"select2");null==e&&window.console&&console.error&&console.error("The select2('"+t+"') method was called on an element that is not using Select2."),n=e[t].apply(e,i)}),-1<r.inArray(t,a)?this:n}}return null==r.fn.select2.defaults&&(r.fn.select2.defaults=t),o}),{define:e.define,require:e.require}}(),t=e.require("jquery.select2");return d.fn.select2.amd=e,t});
|
||
/*! AdminLTE app.js
|
||
* ================
|
||
* Main JS application file for AdminLTE v2. This file
|
||
* should be included in all pages. It controls some layout
|
||
* options and implements exclusive AdminLTE plugins.
|
||
*
|
||
* @author Colorlib
|
||
* @support <https://github.com/ColorlibHQ/AdminLTE/issues>
|
||
* @version v2.4.18
|
||
* @repository git://github.com/ColorlibHQ/AdminLTE.git
|
||
* @license MIT <http://opensource.org/licenses/MIT>
|
||
*/
|
||
if("undefined"==typeof jQuery)throw new Error("AdminLTE requires jQuery");!function(i){"use strict";function s(t,e){if(this.element=t,this.options=e,this.$overlay=i(e.overlayTemplate),""===e.source)throw new Error("Source url was not defined. Please specify a url in your BoxRefresh source option.");this._setUpListeners(),this.load()}var r="lte.boxrefresh",a={source:"",params:{},trigger:".refresh-btn",content:".box-body",loadInContent:!0,responseType:"",overlayTemplate:'<div class="overlay"><div class="fa fa-refresh fa-spin"></div></div>',onLoadStart:function(){},onLoadDone:function(t){return t}},t='[data-widget="box-refresh"]';function e(n){return this.each(function(){var t=i(this),e=t.data(r);if(!e){var o=i.extend({},a,t.data(),"object"==typeof n&&n);t.data(r,e=new s(t,o))}if("string"==typeof e){if(void 0===e[n])throw new Error("No method named "+n);e[n]()}})}s.prototype.load=function(){this._addOverlay(),this.options.onLoadStart.call(i(this)),i.get(this.options.source,this.options.params,function(t){this.options.loadInContent&&i(this.element).find(this.options.content).html(t),this.options.onLoadDone.call(i(this),t),this._removeOverlay()}.bind(this),""!==this.options.responseType&&this.options.responseType)},s.prototype._setUpListeners=function(){i(this.element).on("click",this.options.trigger,function(t){t&&t.preventDefault(),this.load()}.bind(this))},s.prototype._addOverlay=function(){i(this.element).append(this.$overlay)},s.prototype._removeOverlay=function(){i(this.$overlay).remove()};var o=i.fn.boxRefresh;i.fn.boxRefresh=e,i.fn.boxRefresh.Constructor=s,i.fn.boxRefresh.noConflict=function(){return i.fn.boxRefresh=o,this},i(window).on("load",function(){i(t).each(function(){e.call(i(this))})})}(jQuery),function(i){"use strict";function s(t,e){this.element=t,this.options=e,this._setUpListeners()}var r="lte.boxwidget",a={animationSpeed:500,collapseTrigger:'[data-widget="collapse"]',removeTrigger:'[data-widget="remove"]',collapseIcon:"fa-minus",expandIcon:"fa-plus",removeIcon:"fa-times"},t=".box",e=".collapsed-box",d=".box-header",l=".box-body",c=".box-footer",h=".box-tools",f="collapsed-box",p="collapsing.boxwidget",u="collapsed.boxwidget",g="expanding.boxwidget",v="expanded.boxwidget",o="removing.boxwidget",n="removed.boxwidget";function b(n){return this.each(function(){var t=i(this),e=t.data(r);if(!e){var o=i.extend({},a,t.data(),"object"==typeof n&&n);t.data(r,e=new s(t,o))}if("string"==typeof n){if(void 0===e[n])throw new Error("No method named "+n);e[n]()}})}s.prototype.toggle=function(){!i(this.element).is(e)?this.collapse():this.expand()},s.prototype.expand=function(){var t=i.Event(v),e=i.Event(g),o=this.options.collapseIcon,n=this.options.expandIcon;i(this.element).removeClass(f),i(this.element).children(d+", "+l+", "+c).children(h).find("."+n).removeClass(n).addClass(o),i(this.element).children(l+", "+c).slideDown(this.options.animationSpeed,function(){i(this.element).trigger(t)}.bind(this)).trigger(e)},s.prototype.collapse=function(){var t=i.Event(u),e=i.Event(p),o=this.options.collapseIcon,n=this.options.expandIcon;i(this.element).children(d+", "+l+", "+c).children(h).find("."+o).removeClass(o).addClass(n),i(this.element).children(l+", "+c).slideUp(this.options.animationSpeed,function(){i(this.element).addClass(f),i(this.element).trigger(t)}.bind(this)).trigger(e)},s.prototype.remove=function(){var t=i.Event(n),e=i.Event(o);i(this.element).slideUp(this.options.animationSpeed,function(){i(this.element).trigger(t),i(this.element).remove()}.bind(this)).trigger(e)},s.prototype._setUpListeners=function(){var e=this;i(this.element).on("click",this.options.collapseTrigger,function(t){return t&&t.preventDefault(),e.toggle(i(this)),!1}),i(this.element).on("click",this.options.removeTrigger,function(t){return t&&t.preventDefault(),e.remove(i(this)),!1})};var m=i.fn.boxWidget;i.fn.boxWidget=b,i.fn.boxWidget.Constructor=s,i.fn.boxWidget.noConflict=function(){return i.fn.boxWidget=m,this},i(window).on("load",function(){i(t).each(function(){b.call(i(this))})})}(jQuery),function(i){"use strict";function s(t,e){this.element=t,this.options=e,this.hasBindedResize=!1,this.init()}var r="lte.controlsidebar",a={controlsidebarSlide:!0},e=".control-sidebar",t='[data-toggle="control-sidebar"]',o=".control-sidebar-open",n=".control-sidebar-bg",d=".wrapper",l=".layout-boxed",c="control-sidebar-open",h="control-sidebar-hold-transition",f="collapsed.controlsidebar",p="expanded.controlsidebar";function u(n){return this.each(function(){var t=i(this),e=t.data(r);if(!e){var o=i.extend({},a,t.data(),"object"==typeof n&&n);t.data(r,e=new s(t,o))}"string"==typeof n&&e.toggle()})}s.prototype.init=function(){i(this.element).is(t)||i(this).on("click",this.toggle),this.fix(),i(window).resize(function(){this.fix()}.bind(this))},s.prototype.toggle=function(t){t&&t.preventDefault(),this.fix(),i(e).is(o)||i("body").is(o)?this.collapse():this.expand()},s.prototype.expand=function(){i(e).show(),this.options.controlsidebarSlide?i(e).addClass(c):i("body").addClass(h).addClass(c).delay(50).queue(function(){i("body").removeClass(h),i(this).dequeue()}),i(this.element).trigger(i.Event(p))},s.prototype.collapse=function(){this.options.controlsidebarSlide?i(e).removeClass(c):i("body").addClass(h).removeClass(c).delay(50).queue(function(){i("body").removeClass(h),i(this).dequeue()}),i(e).fadeOut(),i(this.element).trigger(i.Event(f))},s.prototype.fix=function(){i("body").is(l)&&this._fixForBoxed(i(n))},s.prototype._fixForBoxed=function(t){t.css({position:"absolute",height:i(d).height()})};var g=i.fn.controlSidebar;i.fn.controlSidebar=u,i.fn.controlSidebar.Constructor=s,i.fn.controlSidebar.noConflict=function(){return i.fn.controlSidebar=g,this},i(document).on("click",t,function(t){t&&t.preventDefault(),u.call(i(this),"toggle")})}(jQuery),function(n){"use strict";function i(t){this.element=t}var s="lte.directchat",t='[data-widget="chat-pane-toggle"]',e=".direct-chat",o="direct-chat-contacts-open";function r(o){return this.each(function(){var t=n(this),e=t.data(s);e||t.data(s,e=new i(t)),"string"==typeof o&&e.toggle(t)})}i.prototype.toggle=function(t){t.parents(e).first().toggleClass(o)};var a=n.fn.directChat;n.fn.directChat=r,n.fn.directChat.Constructor=i,n.fn.directChat.noConflict=function(){return n.fn.directChat=a,this},n(document).on("click",t,function(t){t&&t.preventDefault(),r.call(n(this),"toggle")})}(jQuery),function(i){"use strict";function s(t){this.options=t,this.init()}var r="lte.pushmenu",a={collapseScreenSize:767,expandOnHover:!1,expandTransitionDelay:200},t=".sidebar-collapse",e=".main-sidebar",o=".content-wrapper",n=".sidebar-form .form-control",d='[data-toggle="push-menu"]',l=".sidebar-mini",c=".sidebar-expanded-on-hover",h=".fixed",f="sidebar-collapse",p="sidebar-open",u="sidebar-expanded-on-hover",g="sidebar-mini-expand-feature",v="expanded.pushMenu",b="collapsed.pushMenu";function m(n){return this.each(function(){var t=i(this),e=t.data(r);if(!e){var o=i.extend({},a,t.data(),"object"==typeof n&&n);t.data(r,e=new s(o))}"toggle"===n&&e.toggle()})}s.prototype.init=function(){(this.options.expandOnHover||i("body").is(l+h))&&(this.expandOnHover(),i("body").addClass(g)),i(o).click(function(){i(window).width()<=this.options.collapseScreenSize&&i("body").hasClass(p)&&this.close()}.bind(this)),i(n).click(function(t){t.stopPropagation()})},s.prototype.toggle=function(){var t=i(window).width(),e=!i("body").hasClass(f);t<=this.options.collapseScreenSize&&(e=i("body").hasClass(p)),e?this.close():this.open()},s.prototype.open=function(){i(window).width()>this.options.collapseScreenSize?i("body").removeClass(f).trigger(i.Event(v)):i("body").addClass(p).trigger(i.Event(v))},s.prototype.close=function(){i(window).width()>this.options.collapseScreenSize?i("body").addClass(f).trigger(i.Event(b)):i("body").removeClass(p+" "+f).trigger(i.Event(b))},s.prototype.expandOnHover=function(){i(e).hover(function(){i("body").is(l+t)&&i(window).width()>this.options.collapseScreenSize&&this.expand()}.bind(this),function(){i("body").is(c)&&this.collapse()}.bind(this))},s.prototype.expand=function(){setTimeout(function(){i("body").removeClass(f).addClass(u)},this.options.expandTransitionDelay)},s.prototype.collapse=function(){setTimeout(function(){i("body").removeClass(u).addClass(f)},this.options.expandTransitionDelay)};var y=i.fn.pushMenu;i.fn.pushMenu=m,i.fn.pushMenu.Constructor=s,i.fn.pushMenu.noConflict=function(){return i.fn.pushMenu=y,this},i(document).on("click",d,function(t){t.preventDefault(),m.call(i(this),"toggle")}),i(window).on("load",function(){m.call(i(d))})}(jQuery),function(i){"use strict";function s(t,e){this.element=t,this.options=e,this._setUpListeners()}var r="lte.todolist",a={onCheck:function(t){return t},onUnCheck:function(t){return t}},e={data:'[data-widget="todo-list"]'},o="done";function t(n){return this.each(function(){var t=i(this),e=t.data(r);if(!e){var o=i.extend({},a,t.data(),"object"==typeof n&&n);t.data(r,e=new s(t,o))}if("string"==typeof e){if(void 0===e[n])throw new Error("No method named "+n);e[n]()}})}s.prototype.toggle=function(t){t.parents(e.li).first().toggleClass(o),t.prop("checked")?this.check(t):this.unCheck(t)},s.prototype.check=function(t){this.options.onCheck.call(t)},s.prototype.unCheck=function(t){this.options.onUnCheck.call(t)},s.prototype._setUpListeners=function(){var t=this;i(this.element).on("change ifChanged","input:checkbox",function(){t.toggle(i(this))})};var n=i.fn.todoList;i.fn.todoList=t,i.fn.todoList.Constructor=s,i.fn.todoList.noConflict=function(){return i.fn.todoList=n,this},i(window).on("load",function(){i(e.data).each(function(){t.call(i(this))})})}(jQuery),function(s){"use strict";function n(t,e){this.element=t,this.options=e,s(this.element).addClass(h),s(a+o,this.element).addClass(c),this._setUpListeners()}var i="lte.tree",r={animationSpeed:500,accordion:!0,followLink:!1,trigger:".treeview a"},a=".treeview",d=".treeview-menu",l=".menu-open, .active",t='[data-widget="tree"]',o=".active",c="menu-open",h="tree",f="collapsed.tree",p="expanded.tree";function e(o){return this.each(function(){var t=s(this);if(!t.data(i)){var e=s.extend({},r,t.data(),"object"==typeof o&&o);t.data(i,new n(t,e))}})}n.prototype.toggle=function(t,e){var o=t.next(d),n=t.parent(),i=n.hasClass(c);n.is(a)&&(this.options.followLink&&"#"!==t.attr("href")||e.preventDefault(),i?this.collapse(o,n):this.expand(o,n))},n.prototype.expand=function(t,e){var o=s.Event(p);if(this.options.accordion){var n=e.siblings(l),i=n.children(d);this.collapse(i,n)}e.addClass(c),t.stop().slideDown(this.options.animationSpeed,function(){s(this.element).trigger(o),e.height("auto")}.bind(this))},n.prototype.collapse=function(t,e){var o=s.Event(f);e.removeClass(c),t.stop().slideUp(this.options.animationSpeed,function(){s(this.element).trigger(o),e.find(a).removeClass(c).find(d).hide()}.bind(this))},n.prototype._setUpListeners=function(){var e=this;s(this.element).on("click",this.options.trigger,function(t){e.toggle(s(this),t)})};var u=s.fn.tree;s.fn.tree=e,s.fn.tree.Constructor=n,s.fn.tree.noConflict=function(){return s.fn.tree=u,this},s(window).on("load",function(){s(t).each(function(){e.call(s(this))})})}(jQuery),function(a){"use strict";function i(t){this.options=t,this.bindedResize=!1,this.activate()}var s="lte.layout",r={slimscroll:!0,resetHeight:!0},d=".wrapper",l=".content-wrapper",c=".layout-boxed",h=".main-footer",f=".main-header",t=".main-sidebar",e="slimScrollDiv",p=".sidebar",u=".control-sidebar",o=".sidebar-menu",n=".main-header .logo",g="fixed",v="hold-transition";function b(n){return this.each(function(){var t=a(this),e=t.data(s);if(!e){var o=a.extend({},r,t.data(),"object"==typeof n&&n);t.data(s,e=new i(o))}if("string"==typeof n){if(void 0===e[n])throw new Error("No method named "+n);e[n]()}})}i.prototype.activate=function(){this.fix(),this.fixSidebar(),a("body").removeClass(v),this.options.resetHeight&&a("body, html, "+d).css({height:"auto","min-height":"100%"}),this.bindedResize||(a(window).resize(function(){this.fix(),this.fixSidebar(),a(n+", "+p).one("webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend",function(){this.fix(),this.fixSidebar()}.bind(this))}.bind(this)),this.bindedResize=!0),a(o).on("expanded.tree",function(){this.fix(),this.fixSidebar()}.bind(this)),a(o).on("collapsed.tree",function(){this.fix(),this.fixSidebar()}.bind(this))},i.prototype.fix=function(){a(c+" > "+d).css("overflow","hidden");var t=a(h).outerHeight()||0,e=a(f).outerHeight()||0,o=e+t,n=a(window).height(),i=a(p).outerHeight()||0;if(a("body").hasClass(g))a(l).css("min-height",n-t);else{var s;s=i+e<=n?(a(l).css("min-height",n-o),n-o):(a(l).css("min-height",i),i);var r=a(u);void 0!==r&&r.height()>s&&a(l).css("min-height",r.height())}},i.prototype.fixSidebar=function(){a("body").hasClass(g)?this.options.slimscroll&&void 0!==a.fn.slimScroll&&0===a(t).find(e).length&&a(p).slimScroll({height:a(window).height()-a(f).height()+"px"}):void 0!==a.fn.slimScroll&&a(p).slimScroll({destroy:!0}).height("auto")};var m=a.fn.layout;a.fn.layout=b,a.fn.layout.Constuctor=i,a.fn.layout.noConflict=function(){return a.fn.layout=m,this},a(window).on("load",function(){b.call(a("body"))})}(jQuery);
|
||
/*! tether 1.4.7 */
|
||
|
||
(function(root, factory) {
|
||
if (typeof define === 'function' && define.amd) {
|
||
define([], factory);
|
||
} else if (typeof exports === 'object') {
|
||
module.exports = factory();
|
||
} else {
|
||
root.Tether = factory();
|
||
}
|
||
}(this, function() {
|
||
|
||
'use strict';
|
||
|
||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
||
|
||
var TetherBase = undefined;
|
||
if (typeof TetherBase === 'undefined') {
|
||
TetherBase = { modules: [] };
|
||
}
|
||
|
||
var zeroElement = null;
|
||
|
||
// Same as native getBoundingClientRect, except it takes into account parent <frame> offsets
|
||
// if the element lies within a nested document (<frame> or <iframe>-like).
|
||
function getActualBoundingClientRect(node) {
|
||
var boundingRect = node.getBoundingClientRect();
|
||
|
||
// The original object returned by getBoundingClientRect is immutable, so we clone it
|
||
// We can't use extend because the properties are not considered part of the object by hasOwnProperty in IE9
|
||
var rect = {};
|
||
for (var k in boundingRect) {
|
||
rect[k] = boundingRect[k];
|
||
}
|
||
|
||
try {
|
||
if (node.ownerDocument !== document) {
|
||
var _frameElement = node.ownerDocument.defaultView.frameElement;
|
||
if (_frameElement) {
|
||
var frameRect = getActualBoundingClientRect(_frameElement);
|
||
rect.top += frameRect.top;
|
||
rect.bottom += frameRect.top;
|
||
rect.left += frameRect.left;
|
||
rect.right += frameRect.left;
|
||
}
|
||
}
|
||
} catch (err) {
|
||
// Ignore "Access is denied" in IE11/Edge
|
||
}
|
||
|
||
return rect;
|
||
}
|
||
|
||
function getScrollParents(el) {
|
||
// In firefox if the el is inside an iframe with display: none; window.getComputedStyle() will return null;
|
||
// https://bugzilla.mozilla.org/show_bug.cgi?id=548397
|
||
var computedStyle = getComputedStyle(el) || {};
|
||
var position = computedStyle.position;
|
||
var parents = [];
|
||
|
||
if (position === 'fixed') {
|
||
return [el];
|
||
}
|
||
|
||
var parent = el;
|
||
while ((parent = parent.parentNode) && parent && parent.nodeType === 1) {
|
||
var style = undefined;
|
||
try {
|
||
style = getComputedStyle(parent);
|
||
} catch (err) {}
|
||
|
||
if (typeof style === 'undefined' || style === null) {
|
||
parents.push(parent);
|
||
return parents;
|
||
}
|
||
|
||
var _style = style;
|
||
var overflow = _style.overflow;
|
||
var overflowX = _style.overflowX;
|
||
var overflowY = _style.overflowY;
|
||
|
||
if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) {
|
||
if (position !== 'absolute' || ['relative', 'absolute', 'fixed'].indexOf(style.position) >= 0) {
|
||
parents.push(parent);
|
||
}
|
||
}
|
||
}
|
||
|
||
parents.push(el.ownerDocument.body);
|
||
|
||
// If the node is within a frame, account for the parent window scroll
|
||
if (el.ownerDocument !== document) {
|
||
parents.push(el.ownerDocument.defaultView);
|
||
}
|
||
|
||
return parents;
|
||
}
|
||
|
||
var uniqueId = (function () {
|
||
var id = 0;
|
||
return function () {
|
||
return ++id;
|
||
};
|
||
})();
|
||
|
||
var zeroPosCache = {};
|
||
var getOrigin = function getOrigin() {
|
||
// getBoundingClientRect is unfortunately too accurate. It introduces a pixel or two of
|
||
// jitter as the user scrolls that messes with our ability to detect if two positions
|
||
// are equivilant or not. We place an element at the top left of the page that will
|
||
// get the same jitter, so we can cancel the two out.
|
||
var node = zeroElement;
|
||
if (!node || !document.body.contains(node)) {
|
||
node = document.createElement('div');
|
||
node.setAttribute('data-tether-id', uniqueId());
|
||
extend(node.style, {
|
||
top: 0,
|
||
left: 0,
|
||
position: 'absolute'
|
||
});
|
||
|
||
document.body.appendChild(node);
|
||
|
||
zeroElement = node;
|
||
}
|
||
|
||
var id = node.getAttribute('data-tether-id');
|
||
if (typeof zeroPosCache[id] === 'undefined') {
|
||
zeroPosCache[id] = getActualBoundingClientRect(node);
|
||
|
||
// Clear the cache when this position call is done
|
||
defer(function () {
|
||
delete zeroPosCache[id];
|
||
});
|
||
}
|
||
|
||
return zeroPosCache[id];
|
||
};
|
||
|
||
function removeUtilElements() {
|
||
if (zeroElement) {
|
||
document.body.removeChild(zeroElement);
|
||
}
|
||
zeroElement = null;
|
||
};
|
||
|
||
function getBounds(el) {
|
||
var doc = undefined;
|
||
if (el === document) {
|
||
doc = document;
|
||
el = document.documentElement;
|
||
} else {
|
||
doc = el.ownerDocument;
|
||
}
|
||
|
||
var docEl = doc.documentElement;
|
||
|
||
var box = getActualBoundingClientRect(el);
|
||
|
||
var origin = getOrigin();
|
||
|
||
box.top -= origin.top;
|
||
box.left -= origin.left;
|
||
|
||
if (typeof box.width === 'undefined') {
|
||
box.width = document.body.scrollWidth - box.left - box.right;
|
||
}
|
||
if (typeof box.height === 'undefined') {
|
||
box.height = document.body.scrollHeight - box.top - box.bottom;
|
||
}
|
||
|
||
box.top = box.top - docEl.clientTop;
|
||
box.left = box.left - docEl.clientLeft;
|
||
box.right = doc.body.clientWidth - box.width - box.left;
|
||
box.bottom = doc.body.clientHeight - box.height - box.top;
|
||
|
||
return box;
|
||
}
|
||
|
||
function getOffsetParent(el) {
|
||
return el.offsetParent || document.documentElement;
|
||
}
|
||
|
||
var _scrollBarSize = null;
|
||
function getScrollBarSize() {
|
||
if (_scrollBarSize) {
|
||
return _scrollBarSize;
|
||
}
|
||
var inner = document.createElement('div');
|
||
inner.style.width = '100%';
|
||
inner.style.height = '200px';
|
||
|
||
var outer = document.createElement('div');
|
||
extend(outer.style, {
|
||
position: 'absolute',
|
||
top: 0,
|
||
left: 0,
|
||
pointerEvents: 'none',
|
||
visibility: 'hidden',
|
||
width: '200px',
|
||
height: '150px',
|
||
overflow: 'hidden'
|
||
});
|
||
|
||
outer.appendChild(inner);
|
||
|
||
document.body.appendChild(outer);
|
||
|
||
var widthContained = inner.offsetWidth;
|
||
outer.style.overflow = 'scroll';
|
||
var widthScroll = inner.offsetWidth;
|
||
|
||
if (widthContained === widthScroll) {
|
||
widthScroll = outer.clientWidth;
|
||
}
|
||
|
||
document.body.removeChild(outer);
|
||
|
||
var width = widthContained - widthScroll;
|
||
|
||
_scrollBarSize = { width: width, height: width };
|
||
return _scrollBarSize;
|
||
}
|
||
|
||
function extend() {
|
||
var out = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
|
||
|
||
var args = [];
|
||
|
||
Array.prototype.push.apply(args, arguments);
|
||
|
||
args.slice(1).forEach(function (obj) {
|
||
if (obj) {
|
||
for (var key in obj) {
|
||
if (({}).hasOwnProperty.call(obj, key)) {
|
||
out[key] = obj[key];
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
return out;
|
||
}
|
||
|
||
function removeClass(el, name) {
|
||
if (typeof el.classList !== 'undefined') {
|
||
name.split(' ').forEach(function (cls) {
|
||
if (cls.trim()) {
|
||
el.classList.remove(cls);
|
||
}
|
||
});
|
||
} else {
|
||
var regex = new RegExp('(^| )' + name.split(' ').join('|') + '( |$)', 'gi');
|
||
var className = getClassName(el).replace(regex, ' ');
|
||
setClassName(el, className);
|
||
}
|
||
}
|
||
|
||
function addClass(el, name) {
|
||
if (typeof el.classList !== 'undefined') {
|
||
name.split(' ').forEach(function (cls) {
|
||
if (cls.trim()) {
|
||
el.classList.add(cls);
|
||
}
|
||
});
|
||
} else {
|
||
removeClass(el, name);
|
||
var cls = getClassName(el) + (' ' + name);
|
||
setClassName(el, cls);
|
||
}
|
||
}
|
||
|
||
function hasClass(el, name) {
|
||
if (typeof el.classList !== 'undefined') {
|
||
return el.classList.contains(name);
|
||
}
|
||
var className = getClassName(el);
|
||
return new RegExp('(^| )' + name + '( |$)', 'gi').test(className);
|
||
}
|
||
|
||
function getClassName(el) {
|
||
// Can't use just SVGAnimatedString here since nodes within a Frame in IE have
|
||
// completely separately SVGAnimatedString base classes
|
||
if (el.className instanceof el.ownerDocument.defaultView.SVGAnimatedString) {
|
||
return el.className.baseVal;
|
||
}
|
||
return el.className;
|
||
}
|
||
|
||
function setClassName(el, className) {
|
||
el.setAttribute('class', className);
|
||
}
|
||
|
||
function updateClasses(el, add, all) {
|
||
// Of the set of 'all' classes, we need the 'add' classes, and only the
|
||
// 'add' classes to be set.
|
||
all.forEach(function (cls) {
|
||
if (add.indexOf(cls) === -1 && hasClass(el, cls)) {
|
||
removeClass(el, cls);
|
||
}
|
||
});
|
||
|
||
add.forEach(function (cls) {
|
||
if (!hasClass(el, cls)) {
|
||
addClass(el, cls);
|
||
}
|
||
});
|
||
}
|
||
|
||
var deferred = [];
|
||
|
||
var defer = function defer(fn) {
|
||
deferred.push(fn);
|
||
};
|
||
|
||
var flush = function flush() {
|
||
var fn = undefined;
|
||
while (fn = deferred.pop()) {
|
||
fn();
|
||
}
|
||
};
|
||
|
||
var Evented = (function () {
|
||
function Evented() {
|
||
_classCallCheck(this, Evented);
|
||
}
|
||
|
||
_createClass(Evented, [{
|
||
key: 'on',
|
||
value: function on(event, handler, ctx) {
|
||
var once = arguments.length <= 3 || arguments[3] === undefined ? false : arguments[3];
|
||
|
||
if (typeof this.bindings === 'undefined') {
|
||
this.bindings = {};
|
||
}
|
||
if (typeof this.bindings[event] === 'undefined') {
|
||
this.bindings[event] = [];
|
||
}
|
||
this.bindings[event].push({ handler: handler, ctx: ctx, once: once });
|
||
}
|
||
}, {
|
||
key: 'once',
|
||
value: function once(event, handler, ctx) {
|
||
this.on(event, handler, ctx, true);
|
||
}
|
||
}, {
|
||
key: 'off',
|
||
value: function off(event, handler) {
|
||
if (typeof this.bindings === 'undefined' || typeof this.bindings[event] === 'undefined') {
|
||
return;
|
||
}
|
||
|
||
if (typeof handler === 'undefined') {
|
||
delete this.bindings[event];
|
||
} else {
|
||
var i = 0;
|
||
while (i < this.bindings[event].length) {
|
||
if (this.bindings[event][i].handler === handler) {
|
||
this.bindings[event].splice(i, 1);
|
||
} else {
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}, {
|
||
key: 'trigger',
|
||
value: function trigger(event) {
|
||
if (typeof this.bindings !== 'undefined' && this.bindings[event]) {
|
||
var i = 0;
|
||
|
||
for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||
args[_key - 1] = arguments[_key];
|
||
}
|
||
|
||
while (i < this.bindings[event].length) {
|
||
var _bindings$event$i = this.bindings[event][i];
|
||
var handler = _bindings$event$i.handler;
|
||
var ctx = _bindings$event$i.ctx;
|
||
var once = _bindings$event$i.once;
|
||
|
||
var context = ctx;
|
||
if (typeof context === 'undefined') {
|
||
context = this;
|
||
}
|
||
|
||
handler.apply(context, args);
|
||
|
||
if (once) {
|
||
this.bindings[event].splice(i, 1);
|
||
} else {
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}]);
|
||
|
||
return Evented;
|
||
})();
|
||
|
||
TetherBase.Utils = {
|
||
getActualBoundingClientRect: getActualBoundingClientRect,
|
||
getScrollParents: getScrollParents,
|
||
getBounds: getBounds,
|
||
getOffsetParent: getOffsetParent,
|
||
extend: extend,
|
||
addClass: addClass,
|
||
removeClass: removeClass,
|
||
hasClass: hasClass,
|
||
updateClasses: updateClasses,
|
||
defer: defer,
|
||
flush: flush,
|
||
uniqueId: uniqueId,
|
||
Evented: Evented,
|
||
getScrollBarSize: getScrollBarSize,
|
||
removeUtilElements: removeUtilElements
|
||
};
|
||
/* globals TetherBase, performance */
|
||
|
||
'use strict';
|
||
|
||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
|
||
|
||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
|
||
|
||
var _get = function get(_x6, _x7, _x8) { var _again = true; _function: while (_again) { var object = _x6, property = _x7, receiver = _x8; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x6 = parent; _x7 = property; _x8 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
||
|
||
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
||
|
||
if (typeof TetherBase === 'undefined') {
|
||
throw new Error('You must include the utils.js file before tether.js');
|
||
}
|
||
|
||
var _TetherBase$Utils = TetherBase.Utils;
|
||
var getScrollParents = _TetherBase$Utils.getScrollParents;
|
||
var getBounds = _TetherBase$Utils.getBounds;
|
||
var getOffsetParent = _TetherBase$Utils.getOffsetParent;
|
||
var extend = _TetherBase$Utils.extend;
|
||
var addClass = _TetherBase$Utils.addClass;
|
||
var removeClass = _TetherBase$Utils.removeClass;
|
||
var updateClasses = _TetherBase$Utils.updateClasses;
|
||
var defer = _TetherBase$Utils.defer;
|
||
var flush = _TetherBase$Utils.flush;
|
||
var getScrollBarSize = _TetherBase$Utils.getScrollBarSize;
|
||
var removeUtilElements = _TetherBase$Utils.removeUtilElements;
|
||
|
||
function within(a, b) {
|
||
var diff = arguments.length <= 2 || arguments[2] === undefined ? 1 : arguments[2];
|
||
|
||
return a + diff >= b && b >= a - diff;
|
||
}
|
||
|
||
var transformKey = (function () {
|
||
if (typeof document === 'undefined') {
|
||
return '';
|
||
}
|
||
var el = document.createElement('div');
|
||
|
||
var transforms = ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform'];
|
||
for (var i = 0; i < transforms.length; ++i) {
|
||
var key = transforms[i];
|
||
if (el.style[key] !== undefined) {
|
||
return key;
|
||
}
|
||
}
|
||
})();
|
||
|
||
var tethers = [];
|
||
|
||
var position = function position() {
|
||
tethers.forEach(function (tether) {
|
||
tether.position(false);
|
||
});
|
||
flush();
|
||
};
|
||
|
||
function now() {
|
||
if (typeof performance === 'object' && typeof performance.now === 'function') {
|
||
return performance.now();
|
||
}
|
||
return +new Date();
|
||
}
|
||
|
||
(function () {
|
||
var lastCall = null;
|
||
var lastDuration = null;
|
||
var pendingTimeout = null;
|
||
|
||
var tick = function tick() {
|
||
if (typeof lastDuration !== 'undefined' && lastDuration > 16) {
|
||
// We voluntarily throttle ourselves if we can't manage 60fps
|
||
lastDuration = Math.min(lastDuration - 16, 250);
|
||
|
||
// Just in case this is the last event, remember to position just once more
|
||
pendingTimeout = setTimeout(tick, 250);
|
||
return;
|
||
}
|
||
|
||
if (typeof lastCall !== 'undefined' && now() - lastCall < 10) {
|
||
// Some browsers call events a little too frequently, refuse to run more than is reasonable
|
||
return;
|
||
}
|
||
|
||
if (pendingTimeout != null) {
|
||
clearTimeout(pendingTimeout);
|
||
pendingTimeout = null;
|
||
}
|
||
|
||
lastCall = now();
|
||
position();
|
||
lastDuration = now() - lastCall;
|
||
};
|
||
|
||
if (typeof window !== 'undefined' && typeof window.addEventListener !== 'undefined') {
|
||
['resize', 'scroll', 'touchmove'].forEach(function (event) {
|
||
window.addEventListener(event, tick);
|
||
});
|
||
}
|
||
})();
|
||
|
||
var MIRROR_LR = {
|
||
center: 'center',
|
||
left: 'right',
|
||
right: 'left'
|
||
};
|
||
|
||
var MIRROR_TB = {
|
||
middle: 'middle',
|
||
top: 'bottom',
|
||
bottom: 'top'
|
||
};
|
||
|
||
var OFFSET_MAP = {
|
||
top: 0,
|
||
left: 0,
|
||
middle: '50%',
|
||
center: '50%',
|
||
bottom: '100%',
|
||
right: '100%'
|
||
};
|
||
|
||
var autoToFixedAttachment = function autoToFixedAttachment(attachment, relativeToAttachment) {
|
||
var left = attachment.left;
|
||
var top = attachment.top;
|
||
|
||
if (left === 'auto') {
|
||
left = MIRROR_LR[relativeToAttachment.left];
|
||
}
|
||
|
||
if (top === 'auto') {
|
||
top = MIRROR_TB[relativeToAttachment.top];
|
||
}
|
||
|
||
return { left: left, top: top };
|
||
};
|
||
|
||
var attachmentToOffset = function attachmentToOffset(attachment) {
|
||
var left = attachment.left;
|
||
var top = attachment.top;
|
||
|
||
if (typeof OFFSET_MAP[attachment.left] !== 'undefined') {
|
||
left = OFFSET_MAP[attachment.left];
|
||
}
|
||
|
||
if (typeof OFFSET_MAP[attachment.top] !== 'undefined') {
|
||
top = OFFSET_MAP[attachment.top];
|
||
}
|
||
|
||
return { left: left, top: top };
|
||
};
|
||
|
||
function addOffset() {
|
||
var out = { top: 0, left: 0 };
|
||
|
||
for (var _len = arguments.length, offsets = Array(_len), _key = 0; _key < _len; _key++) {
|
||
offsets[_key] = arguments[_key];
|
||
}
|
||
|
||
offsets.forEach(function (_ref) {
|
||
var top = _ref.top;
|
||
var left = _ref.left;
|
||
|
||
if (typeof top === 'string') {
|
||
top = parseFloat(top, 10);
|
||
}
|
||
if (typeof left === 'string') {
|
||
left = parseFloat(left, 10);
|
||
}
|
||
|
||
out.top += top;
|
||
out.left += left;
|
||
});
|
||
|
||
return out;
|
||
}
|
||
|
||
function offsetToPx(offset, size) {
|
||
if (typeof offset.left === 'string' && offset.left.indexOf('%') !== -1) {
|
||
offset.left = parseFloat(offset.left, 10) / 100 * size.width;
|
||
}
|
||
if (typeof offset.top === 'string' && offset.top.indexOf('%') !== -1) {
|
||
offset.top = parseFloat(offset.top, 10) / 100 * size.height;
|
||
}
|
||
|
||
return offset;
|
||
}
|
||
|
||
var parseOffset = function parseOffset(value) {
|
||
var _value$split = value.split(' ');
|
||
|
||
var _value$split2 = _slicedToArray(_value$split, 2);
|
||
|
||
var top = _value$split2[0];
|
||
var left = _value$split2[1];
|
||
|
||
return { top: top, left: left };
|
||
};
|
||
var parseAttachment = parseOffset;
|
||
|
||
var TetherClass = (function (_Evented) {
|
||
_inherits(TetherClass, _Evented);
|
||
|
||
function TetherClass(options) {
|
||
var _this = this;
|
||
|
||
_classCallCheck(this, TetherClass);
|
||
|
||
_get(Object.getPrototypeOf(TetherClass.prototype), 'constructor', this).call(this);
|
||
this.position = this.position.bind(this);
|
||
|
||
tethers.push(this);
|
||
|
||
this.history = [];
|
||
|
||
this.setOptions(options, false);
|
||
|
||
TetherBase.modules.forEach(function (module) {
|
||
if (typeof module.initialize !== 'undefined') {
|
||
module.initialize.call(_this);
|
||
}
|
||
});
|
||
|
||
this.position();
|
||
}
|
||
|
||
_createClass(TetherClass, [{
|
||
key: 'getClass',
|
||
value: function getClass() {
|
||
var key = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0];
|
||
var classes = this.options.classes;
|
||
|
||
if (typeof classes !== 'undefined' && classes[key]) {
|
||
return this.options.classes[key];
|
||
} else if (this.options.classPrefix) {
|
||
return this.options.classPrefix + '-' + key;
|
||
} else {
|
||
return key;
|
||
}
|
||
}
|
||
}, {
|
||
key: 'setOptions',
|
||
value: function setOptions(options) {
|
||
var _this2 = this;
|
||
|
||
var pos = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1];
|
||
|
||
var defaults = {
|
||
offset: '0 0',
|
||
targetOffset: '0 0',
|
||
targetAttachment: 'auto auto',
|
||
classPrefix: 'tether'
|
||
};
|
||
|
||
this.options = extend(defaults, options);
|
||
|
||
var _options = this.options;
|
||
var element = _options.element;
|
||
var target = _options.target;
|
||
var targetModifier = _options.targetModifier;
|
||
|
||
this.element = element;
|
||
this.target = target;
|
||
this.targetModifier = targetModifier;
|
||
|
||
if (this.target === 'viewport') {
|
||
this.target = document.body;
|
||
this.targetModifier = 'visible';
|
||
} else if (this.target === 'scroll-handle') {
|
||
this.target = document.body;
|
||
this.targetModifier = 'scroll-handle';
|
||
}
|
||
|
||
['element', 'target'].forEach(function (key) {
|
||
if (typeof _this2[key] === 'undefined') {
|
||
throw new Error('Tether Error: Both element and target must be defined');
|
||
}
|
||
|
||
if (typeof _this2[key].jquery !== 'undefined') {
|
||
_this2[key] = _this2[key][0];
|
||
} else if (typeof _this2[key] === 'string') {
|
||
_this2[key] = document.querySelector(_this2[key]);
|
||
}
|
||
});
|
||
|
||
addClass(this.element, this.getClass('element'));
|
||
if (!(this.options.addTargetClasses === false)) {
|
||
addClass(this.target, this.getClass('target'));
|
||
}
|
||
|
||
if (!this.options.attachment) {
|
||
throw new Error('Tether Error: You must provide an attachment');
|
||
}
|
||
|
||
this.targetAttachment = parseAttachment(this.options.targetAttachment);
|
||
this.attachment = parseAttachment(this.options.attachment);
|
||
this.offset = parseOffset(this.options.offset);
|
||
this.targetOffset = parseOffset(this.options.targetOffset);
|
||
|
||
if (typeof this.scrollParents !== 'undefined') {
|
||
this.disable();
|
||
}
|
||
|
||
if (this.targetModifier === 'scroll-handle') {
|
||
this.scrollParents = [this.target];
|
||
} else {
|
||
this.scrollParents = getScrollParents(this.target);
|
||
}
|
||
|
||
if (!(this.options.enabled === false)) {
|
||
this.enable(pos);
|
||
}
|
||
}
|
||
}, {
|
||
key: 'getTargetBounds',
|
||
value: function getTargetBounds() {
|
||
if (typeof this.targetModifier !== 'undefined') {
|
||
if (this.targetModifier === 'visible') {
|
||
if (this.target === document.body) {
|
||
return { top: pageYOffset, left: pageXOffset, height: innerHeight, width: innerWidth };
|
||
} else {
|
||
var bounds = getBounds(this.target);
|
||
|
||
var out = {
|
||
height: bounds.height,
|
||
width: bounds.width,
|
||
top: bounds.top,
|
||
left: bounds.left
|
||
};
|
||
|
||
out.height = Math.min(out.height, bounds.height - (pageYOffset - bounds.top));
|
||
out.height = Math.min(out.height, bounds.height - (bounds.top + bounds.height - (pageYOffset + innerHeight)));
|
||
out.height = Math.min(innerHeight, out.height);
|
||
out.height -= 2;
|
||
|
||
out.width = Math.min(out.width, bounds.width - (pageXOffset - bounds.left));
|
||
out.width = Math.min(out.width, bounds.width - (bounds.left + bounds.width - (pageXOffset + innerWidth)));
|
||
out.width = Math.min(innerWidth, out.width);
|
||
out.width -= 2;
|
||
|
||
if (out.top < pageYOffset) {
|
||
out.top = pageYOffset;
|
||
}
|
||
if (out.left < pageXOffset) {
|
||
out.left = pageXOffset;
|
||
}
|
||
|
||
return out;
|
||
}
|
||
} else if (this.targetModifier === 'scroll-handle') {
|
||
var bounds = undefined;
|
||
var target = this.target;
|
||
if (target === document.body) {
|
||
target = document.documentElement;
|
||
|
||
bounds = {
|
||
left: pageXOffset,
|
||
top: pageYOffset,
|
||
height: innerHeight,
|
||
width: innerWidth
|
||
};
|
||
} else {
|
||
bounds = getBounds(target);
|
||
}
|
||
|
||
var style = getComputedStyle(target);
|
||
|
||
var hasBottomScroll = target.scrollWidth > target.clientWidth || [style.overflow, style.overflowX].indexOf('scroll') >= 0 || this.target !== document.body;
|
||
|
||
var scrollBottom = 0;
|
||
if (hasBottomScroll) {
|
||
scrollBottom = 15;
|
||
}
|
||
|
||
var height = bounds.height - parseFloat(style.borderTopWidth) - parseFloat(style.borderBottomWidth) - scrollBottom;
|
||
|
||
var out = {
|
||
width: 15,
|
||
height: height * 0.975 * (height / target.scrollHeight),
|
||
left: bounds.left + bounds.width - parseFloat(style.borderLeftWidth) - 15
|
||
};
|
||
|
||
var fitAdj = 0;
|
||
if (height < 408 && this.target === document.body) {
|
||
fitAdj = -0.00011 * Math.pow(height, 2) - 0.00727 * height + 22.58;
|
||
}
|
||
|
||
if (this.target !== document.body) {
|
||
out.height = Math.max(out.height, 24);
|
||
}
|
||
|
||
var scrollPercentage = this.target.scrollTop / (target.scrollHeight - height);
|
||
out.top = scrollPercentage * (height - out.height - fitAdj) + bounds.top + parseFloat(style.borderTopWidth);
|
||
|
||
if (this.target === document.body) {
|
||
out.height = Math.max(out.height, 24);
|
||
}
|
||
|
||
return out;
|
||
}
|
||
} else {
|
||
return getBounds(this.target);
|
||
}
|
||
}
|
||
}, {
|
||
key: 'clearCache',
|
||
value: function clearCache() {
|
||
this._cache = {};
|
||
}
|
||
}, {
|
||
key: 'cache',
|
||
value: function cache(k, getter) {
|
||
// More than one module will often need the same DOM info, so
|
||
// we keep a cache which is cleared on each position call
|
||
if (typeof this._cache === 'undefined') {
|
||
this._cache = {};
|
||
}
|
||
|
||
if (typeof this._cache[k] === 'undefined') {
|
||
this._cache[k] = getter.call(this);
|
||
}
|
||
|
||
return this._cache[k];
|
||
}
|
||
}, {
|
||
key: 'enable',
|
||
value: function enable() {
|
||
var _this3 = this;
|
||
|
||
var pos = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
|
||
|
||
if (!(this.options.addTargetClasses === false)) {
|
||
addClass(this.target, this.getClass('enabled'));
|
||
}
|
||
addClass(this.element, this.getClass('enabled'));
|
||
this.enabled = true;
|
||
|
||
this.scrollParents.forEach(function (parent) {
|
||
if (parent !== _this3.target.ownerDocument) {
|
||
parent.addEventListener('scroll', _this3.position);
|
||
}
|
||
});
|
||
|
||
if (pos) {
|
||
this.position();
|
||
}
|
||
}
|
||
}, {
|
||
key: 'disable',
|
||
value: function disable() {
|
||
var _this4 = this;
|
||
|
||
removeClass(this.target, this.getClass('enabled'));
|
||
removeClass(this.element, this.getClass('enabled'));
|
||
this.enabled = false;
|
||
|
||
if (typeof this.scrollParents !== 'undefined') {
|
||
this.scrollParents.forEach(function (parent) {
|
||
parent.removeEventListener('scroll', _this4.position);
|
||
});
|
||
}
|
||
}
|
||
}, {
|
||
key: 'destroy',
|
||
value: function destroy() {
|
||
var _this5 = this;
|
||
|
||
this.disable();
|
||
|
||
tethers.forEach(function (tether, i) {
|
||
if (tether === _this5) {
|
||
tethers.splice(i, 1);
|
||
}
|
||
});
|
||
|
||
// Remove any elements we were using for convenience from the DOM
|
||
if (tethers.length === 0) {
|
||
removeUtilElements();
|
||
}
|
||
}
|
||
}, {
|
||
key: 'updateAttachClasses',
|
||
value: function updateAttachClasses(elementAttach, targetAttach) {
|
||
var _this6 = this;
|
||
|
||
elementAttach = elementAttach || this.attachment;
|
||
targetAttach = targetAttach || this.targetAttachment;
|
||
var sides = ['left', 'top', 'bottom', 'right', 'middle', 'center'];
|
||
|
||
if (typeof this._addAttachClasses !== 'undefined' && this._addAttachClasses.length) {
|
||
// updateAttachClasses can be called more than once in a position call, so
|
||
// we need to clean up after ourselves such that when the last defer gets
|
||
// ran it doesn't add any extra classes from previous calls.
|
||
this._addAttachClasses.splice(0, this._addAttachClasses.length);
|
||
}
|
||
|
||
if (typeof this._addAttachClasses === 'undefined') {
|
||
this._addAttachClasses = [];
|
||
}
|
||
var add = this._addAttachClasses;
|
||
|
||
if (elementAttach.top) {
|
||
add.push(this.getClass('element-attached') + '-' + elementAttach.top);
|
||
}
|
||
if (elementAttach.left) {
|
||
add.push(this.getClass('element-attached') + '-' + elementAttach.left);
|
||
}
|
||
if (targetAttach.top) {
|
||
add.push(this.getClass('target-attached') + '-' + targetAttach.top);
|
||
}
|
||
if (targetAttach.left) {
|
||
add.push(this.getClass('target-attached') + '-' + targetAttach.left);
|
||
}
|
||
|
||
var all = [];
|
||
sides.forEach(function (side) {
|
||
all.push(_this6.getClass('element-attached') + '-' + side);
|
||
all.push(_this6.getClass('target-attached') + '-' + side);
|
||
});
|
||
|
||
defer(function () {
|
||
if (!(typeof _this6._addAttachClasses !== 'undefined')) {
|
||
return;
|
||
}
|
||
|
||
updateClasses(_this6.element, _this6._addAttachClasses, all);
|
||
if (!(_this6.options.addTargetClasses === false)) {
|
||
updateClasses(_this6.target, _this6._addAttachClasses, all);
|
||
}
|
||
|
||
delete _this6._addAttachClasses;
|
||
});
|
||
}
|
||
}, {
|
||
key: 'position',
|
||
value: function position() {
|
||
var _this7 = this;
|
||
|
||
var flushChanges = arguments.length <= 0 || arguments[0] === undefined ? true : arguments[0];
|
||
|
||
// flushChanges commits the changes immediately, leave true unless you are positioning multiple
|
||
// tethers (in which case call Tether.Utils.flush yourself when you're done)
|
||
|
||
if (!this.enabled) {
|
||
return;
|
||
}
|
||
|
||
this.clearCache();
|
||
|
||
// Turn 'auto' attachments into the appropriate corner or edge
|
||
var targetAttachment = autoToFixedAttachment(this.targetAttachment, this.attachment);
|
||
|
||
this.updateAttachClasses(this.attachment, targetAttachment);
|
||
|
||
var elementPos = this.cache('element-bounds', function () {
|
||
return getBounds(_this7.element);
|
||
});
|
||
|
||
var width = elementPos.width;
|
||
var height = elementPos.height;
|
||
|
||
if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') {
|
||
var _lastSize = this.lastSize;
|
||
|
||
// We cache the height and width to make it possible to position elements that are
|
||
// getting hidden.
|
||
width = _lastSize.width;
|
||
height = _lastSize.height;
|
||
} else {
|
||
this.lastSize = { width: width, height: height };
|
||
}
|
||
|
||
var targetPos = this.cache('target-bounds', function () {
|
||
return _this7.getTargetBounds();
|
||
});
|
||
var targetSize = targetPos;
|
||
|
||
// Get an actual px offset from the attachment
|
||
var offset = offsetToPx(attachmentToOffset(this.attachment), { width: width, height: height });
|
||
var targetOffset = offsetToPx(attachmentToOffset(targetAttachment), targetSize);
|
||
|
||
var manualOffset = offsetToPx(this.offset, { width: width, height: height });
|
||
var manualTargetOffset = offsetToPx(this.targetOffset, targetSize);
|
||
|
||
// Add the manually provided offset
|
||
offset = addOffset(offset, manualOffset);
|
||
targetOffset = addOffset(targetOffset, manualTargetOffset);
|
||
|
||
// It's now our goal to make (element position + offset) == (target position + target offset)
|
||
var left = targetPos.left + targetOffset.left - offset.left;
|
||
var top = targetPos.top + targetOffset.top - offset.top;
|
||
|
||
for (var i = 0; i < TetherBase.modules.length; ++i) {
|
||
var _module2 = TetherBase.modules[i];
|
||
var ret = _module2.position.call(this, {
|
||
left: left,
|
||
top: top,
|
||
targetAttachment: targetAttachment,
|
||
targetPos: targetPos,
|
||
elementPos: elementPos,
|
||
offset: offset,
|
||
targetOffset: targetOffset,
|
||
manualOffset: manualOffset,
|
||
manualTargetOffset: manualTargetOffset,
|
||
scrollbarSize: scrollbarSize,
|
||
attachment: this.attachment
|
||
});
|
||
|
||
if (ret === false) {
|
||
return false;
|
||
} else if (typeof ret === 'undefined' || typeof ret !== 'object') {
|
||
continue;
|
||
} else {
|
||
top = ret.top;
|
||
left = ret.left;
|
||
}
|
||
}
|
||
|
||
// We describe the position three different ways to give the optimizer
|
||
// a chance to decide the best possible way to position the element
|
||
// with the fewest repaints.
|
||
var next = {
|
||
// It's position relative to the page (absolute positioning when
|
||
// the element is a child of the body)
|
||
page: {
|
||
top: top,
|
||
left: left
|
||
},
|
||
|
||
// It's position relative to the viewport (fixed positioning)
|
||
viewport: {
|
||
top: top - pageYOffset,
|
||
bottom: pageYOffset - top - height + innerHeight,
|
||
left: left - pageXOffset,
|
||
right: pageXOffset - left - width + innerWidth
|
||
}
|
||
};
|
||
|
||
var doc = this.target.ownerDocument;
|
||
var win = doc.defaultView;
|
||
|
||
var scrollbarSize = undefined;
|
||
if (win.innerHeight > doc.documentElement.clientHeight) {
|
||
scrollbarSize = this.cache('scrollbar-size', getScrollBarSize);
|
||
next.viewport.bottom -= scrollbarSize.height;
|
||
}
|
||
|
||
if (win.innerWidth > doc.documentElement.clientWidth) {
|
||
scrollbarSize = this.cache('scrollbar-size', getScrollBarSize);
|
||
next.viewport.right -= scrollbarSize.width;
|
||
}
|
||
|
||
if (['', 'static'].indexOf(doc.body.style.position) === -1 || ['', 'static'].indexOf(doc.body.parentElement.style.position) === -1) {
|
||
// Absolute positioning in the body will be relative to the page, not the 'initial containing block'
|
||
next.page.bottom = doc.body.scrollHeight - top - height;
|
||
next.page.right = doc.body.scrollWidth - left - width;
|
||
}
|
||
|
||
if (typeof this.options.optimizations !== 'undefined' && this.options.optimizations.moveElement !== false && !(typeof this.targetModifier !== 'undefined')) {
|
||
(function () {
|
||
var offsetParent = _this7.cache('target-offsetparent', function () {
|
||
return getOffsetParent(_this7.target);
|
||
});
|
||
var offsetPosition = _this7.cache('target-offsetparent-bounds', function () {
|
||
return getBounds(offsetParent);
|
||
});
|
||
var offsetParentStyle = getComputedStyle(offsetParent);
|
||
var offsetParentSize = offsetPosition;
|
||
|
||
var offsetBorder = {};
|
||
['Top', 'Left', 'Bottom', 'Right'].forEach(function (side) {
|
||
offsetBorder[side.toLowerCase()] = parseFloat(offsetParentStyle['border' + side + 'Width']);
|
||
});
|
||
|
||
offsetPosition.right = doc.body.scrollWidth - offsetPosition.left - offsetParentSize.width + offsetBorder.right;
|
||
offsetPosition.bottom = doc.body.scrollHeight - offsetPosition.top - offsetParentSize.height + offsetBorder.bottom;
|
||
|
||
if (next.page.top >= offsetPosition.top + offsetBorder.top && next.page.bottom >= offsetPosition.bottom) {
|
||
if (next.page.left >= offsetPosition.left + offsetBorder.left && next.page.right >= offsetPosition.right) {
|
||
// We're within the visible part of the target's scroll parent
|
||
var scrollTop = offsetParent.scrollTop;
|
||
var scrollLeft = offsetParent.scrollLeft;
|
||
|
||
// It's position relative to the target's offset parent (absolute positioning when
|
||
// the element is moved to be a child of the target's offset parent).
|
||
next.offset = {
|
||
top: next.page.top - offsetPosition.top + scrollTop - offsetBorder.top,
|
||
left: next.page.left - offsetPosition.left + scrollLeft - offsetBorder.left
|
||
};
|
||
}
|
||
}
|
||
})();
|
||
}
|
||
|
||
// We could also travel up the DOM and try each containing context, rather than only
|
||
// looking at the body, but we're gonna get diminishing returns.
|
||
|
||
this.move(next);
|
||
|
||
this.history.unshift(next);
|
||
|
||
if (this.history.length > 3) {
|
||
this.history.pop();
|
||
}
|
||
|
||
if (flushChanges) {
|
||
flush();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// THE ISSUE
|
||
}, {
|
||
key: 'move',
|
||
value: function move(pos) {
|
||
var _this8 = this;
|
||
|
||
if (!(typeof this.element.parentNode !== 'undefined')) {
|
||
return;
|
||
}
|
||
|
||
var same = {};
|
||
|
||
for (var type in pos) {
|
||
same[type] = {};
|
||
|
||
for (var key in pos[type]) {
|
||
var found = false;
|
||
|
||
for (var i = 0; i < this.history.length; ++i) {
|
||
var point = this.history[i];
|
||
if (typeof point[type] !== 'undefined' && !within(point[type][key], pos[type][key])) {
|
||
found = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!found) {
|
||
same[type][key] = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
var css = { top: '', left: '', right: '', bottom: '' };
|
||
|
||
var transcribe = function transcribe(_same, _pos) {
|
||
var hasOptimizations = typeof _this8.options.optimizations !== 'undefined';
|
||
var gpu = hasOptimizations ? _this8.options.optimizations.gpu : null;
|
||
if (gpu !== false) {
|
||
var yPos = undefined,
|
||
xPos = undefined;
|
||
if (_same.top) {
|
||
css.top = 0;
|
||
yPos = _pos.top;
|
||
} else {
|
||
css.bottom = 0;
|
||
yPos = -_pos.bottom;
|
||
}
|
||
|
||
if (_same.left) {
|
||
css.left = 0;
|
||
xPos = _pos.left;
|
||
} else {
|
||
css.right = 0;
|
||
xPos = -_pos.right;
|
||
}
|
||
|
||
if (typeof window.devicePixelRatio === 'number' && devicePixelRatio % 1 === 0) {
|
||
xPos = Math.round(xPos * devicePixelRatio) / devicePixelRatio;
|
||
yPos = Math.round(yPos * devicePixelRatio) / devicePixelRatio;
|
||
}
|
||
|
||
css[transformKey] = 'translateX(' + xPos + 'px) translateY(' + yPos + 'px)';
|
||
|
||
if (transformKey !== 'msTransform') {
|
||
// The Z transform will keep this in the GPU (faster, and prevents artifacts),
|
||
// but IE9 doesn't support 3d transforms and will choke.
|
||
css[transformKey] += " translateZ(0)";
|
||
}
|
||
} else {
|
||
if (_same.top) {
|
||
css.top = _pos.top + 'px';
|
||
} else {
|
||
css.bottom = _pos.bottom + 'px';
|
||
}
|
||
|
||
if (_same.left) {
|
||
css.left = _pos.left + 'px';
|
||
} else {
|
||
css.right = _pos.right + 'px';
|
||
}
|
||
}
|
||
};
|
||
|
||
var moved = false;
|
||
if ((same.page.top || same.page.bottom) && (same.page.left || same.page.right)) {
|
||
css.position = 'absolute';
|
||
transcribe(same.page, pos.page);
|
||
} else if ((same.viewport.top || same.viewport.bottom) && (same.viewport.left || same.viewport.right)) {
|
||
css.position = 'fixed';
|
||
transcribe(same.viewport, pos.viewport);
|
||
} else if (typeof same.offset !== 'undefined' && same.offset.top && same.offset.left) {
|
||
(function () {
|
||
css.position = 'absolute';
|
||
var offsetParent = _this8.cache('target-offsetparent', function () {
|
||
return getOffsetParent(_this8.target);
|
||
});
|
||
|
||
if (getOffsetParent(_this8.element) !== offsetParent) {
|
||
defer(function () {
|
||
_this8.element.parentNode.removeChild(_this8.element);
|
||
offsetParent.appendChild(_this8.element);
|
||
});
|
||
}
|
||
|
||
transcribe(same.offset, pos.offset);
|
||
moved = true;
|
||
})();
|
||
} else {
|
||
css.position = 'absolute';
|
||
transcribe({ top: true, left: true }, pos.page);
|
||
}
|
||
|
||
if (!moved) {
|
||
if (this.options.bodyElement) {
|
||
if (this.element.parentNode !== this.options.bodyElement) {
|
||
this.options.bodyElement.appendChild(this.element);
|
||
}
|
||
} else {
|
||
var isFullscreenElement = function isFullscreenElement(e) {
|
||
var d = e.ownerDocument;
|
||
var fe = d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement || d.msFullscreenElement;
|
||
return fe === e;
|
||
};
|
||
|
||
var offsetParentIsBody = true;
|
||
|
||
var currentNode = this.element.parentNode;
|
||
while (currentNode && currentNode.nodeType === 1 && currentNode.tagName !== 'BODY' && !isFullscreenElement(currentNode)) {
|
||
if (getComputedStyle(currentNode).position !== 'static') {
|
||
offsetParentIsBody = false;
|
||
break;
|
||
}
|
||
|
||
currentNode = currentNode.parentNode;
|
||
}
|
||
|
||
if (!offsetParentIsBody) {
|
||
this.element.parentNode.removeChild(this.element);
|
||
this.element.ownerDocument.body.appendChild(this.element);
|
||
}
|
||
}
|
||
}
|
||
|
||
// Any css change will trigger a repaint, so let's avoid one if nothing changed
|
||
var writeCSS = {};
|
||
var write = false;
|
||
for (var key in css) {
|
||
var val = css[key];
|
||
var elVal = this.element.style[key];
|
||
|
||
if (elVal !== val) {
|
||
write = true;
|
||
writeCSS[key] = val;
|
||
}
|
||
}
|
||
|
||
if (write) {
|
||
defer(function () {
|
||
extend(_this8.element.style, writeCSS);
|
||
_this8.trigger('repositioned');
|
||
});
|
||
}
|
||
}
|
||
}]);
|
||
|
||
return TetherClass;
|
||
})(Evented);
|
||
|
||
TetherClass.modules = [];
|
||
|
||
TetherBase.position = position;
|
||
|
||
var Tether = extend(TetherClass, TetherBase);
|
||
/* globals TetherBase */
|
||
|
||
'use strict';
|
||
|
||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
|
||
|
||
var _TetherBase$Utils = TetherBase.Utils;
|
||
var getBounds = _TetherBase$Utils.getBounds;
|
||
var extend = _TetherBase$Utils.extend;
|
||
var updateClasses = _TetherBase$Utils.updateClasses;
|
||
var defer = _TetherBase$Utils.defer;
|
||
|
||
var BOUNDS_FORMAT = ['left', 'top', 'right', 'bottom'];
|
||
|
||
function getBoundingRect(tether, to) {
|
||
if (to === 'scrollParent') {
|
||
to = tether.scrollParents[0];
|
||
} else if (to === 'window') {
|
||
to = [pageXOffset, pageYOffset, innerWidth + pageXOffset, innerHeight + pageYOffset];
|
||
}
|
||
|
||
if (to === document) {
|
||
to = to.documentElement;
|
||
}
|
||
|
||
if (typeof to.nodeType !== 'undefined') {
|
||
(function () {
|
||
var node = to;
|
||
var size = getBounds(to);
|
||
var pos = size;
|
||
var style = getComputedStyle(to);
|
||
|
||
to = [pos.left, pos.top, size.width + pos.left, size.height + pos.top];
|
||
|
||
// Account any parent Frames scroll offset
|
||
if (node.ownerDocument !== document) {
|
||
var win = node.ownerDocument.defaultView;
|
||
to[0] += win.pageXOffset;
|
||
to[1] += win.pageYOffset;
|
||
to[2] += win.pageXOffset;
|
||
to[3] += win.pageYOffset;
|
||
}
|
||
|
||
BOUNDS_FORMAT.forEach(function (side, i) {
|
||
side = side[0].toUpperCase() + side.substr(1);
|
||
if (side === 'Top' || side === 'Left') {
|
||
to[i] += parseFloat(style['border' + side + 'Width']);
|
||
} else {
|
||
to[i] -= parseFloat(style['border' + side + 'Width']);
|
||
}
|
||
});
|
||
})();
|
||
}
|
||
|
||
return to;
|
||
}
|
||
|
||
TetherBase.modules.push({
|
||
position: function position(_ref) {
|
||
var _this = this;
|
||
|
||
var top = _ref.top;
|
||
var left = _ref.left;
|
||
var targetAttachment = _ref.targetAttachment;
|
||
|
||
if (!this.options.constraints) {
|
||
return true;
|
||
}
|
||
|
||
var _cache = this.cache('element-bounds', function () {
|
||
return getBounds(_this.element);
|
||
});
|
||
|
||
var height = _cache.height;
|
||
var width = _cache.width;
|
||
|
||
if (width === 0 && height === 0 && typeof this.lastSize !== 'undefined') {
|
||
var _lastSize = this.lastSize;
|
||
|
||
// Handle the item getting hidden as a result of our positioning without glitching
|
||
// the classes in and out
|
||
width = _lastSize.width;
|
||
height = _lastSize.height;
|
||
}
|
||
|
||
var targetSize = this.cache('target-bounds', function () {
|
||
return _this.getTargetBounds();
|
||
});
|
||
|
||
var targetHeight = targetSize.height;
|
||
var targetWidth = targetSize.width;
|
||
|
||
var allClasses = [this.getClass('pinned'), this.getClass('out-of-bounds')];
|
||
|
||
this.options.constraints.forEach(function (constraint) {
|
||
var outOfBoundsClass = constraint.outOfBoundsClass;
|
||
var pinnedClass = constraint.pinnedClass;
|
||
|
||
if (outOfBoundsClass) {
|
||
allClasses.push(outOfBoundsClass);
|
||
}
|
||
if (pinnedClass) {
|
||
allClasses.push(pinnedClass);
|
||
}
|
||
});
|
||
|
||
allClasses.forEach(function (cls) {
|
||
['left', 'top', 'right', 'bottom'].forEach(function (side) {
|
||
allClasses.push(cls + '-' + side);
|
||
});
|
||
});
|
||
|
||
var addClasses = [];
|
||
|
||
var tAttachment = extend({}, targetAttachment);
|
||
var eAttachment = extend({}, this.attachment);
|
||
|
||
this.options.constraints.forEach(function (constraint) {
|
||
var to = constraint.to;
|
||
var attachment = constraint.attachment;
|
||
var pin = constraint.pin;
|
||
|
||
if (typeof attachment === 'undefined') {
|
||
attachment = '';
|
||
}
|
||
|
||
var changeAttachX = undefined,
|
||
changeAttachY = undefined;
|
||
if (attachment.indexOf(' ') >= 0) {
|
||
var _attachment$split = attachment.split(' ');
|
||
|
||
var _attachment$split2 = _slicedToArray(_attachment$split, 2);
|
||
|
||
changeAttachY = _attachment$split2[0];
|
||
changeAttachX = _attachment$split2[1];
|
||
} else {
|
||
changeAttachX = changeAttachY = attachment;
|
||
}
|
||
|
||
var bounds = getBoundingRect(_this, to);
|
||
|
||
if (changeAttachY === 'target' || changeAttachY === 'both') {
|
||
if (top < bounds[1] && tAttachment.top === 'top') {
|
||
top += targetHeight;
|
||
tAttachment.top = 'bottom';
|
||
}
|
||
|
||
if (top + height > bounds[3] && tAttachment.top === 'bottom') {
|
||
top -= targetHeight;
|
||
tAttachment.top = 'top';
|
||
}
|
||
}
|
||
|
||
if (changeAttachY === 'together') {
|
||
if (tAttachment.top === 'top') {
|
||
if (eAttachment.top === 'bottom' && top < bounds[1]) {
|
||
top += targetHeight;
|
||
tAttachment.top = 'bottom';
|
||
|
||
top += height;
|
||
eAttachment.top = 'top';
|
||
} else if (eAttachment.top === 'top' && top + height > bounds[3] && top - (height - targetHeight) >= bounds[1]) {
|
||
top -= height - targetHeight;
|
||
tAttachment.top = 'bottom';
|
||
|
||
eAttachment.top = 'bottom';
|
||
}
|
||
}
|
||
|
||
if (tAttachment.top === 'bottom') {
|
||
if (eAttachment.top === 'top' && top + height > bounds[3]) {
|
||
top -= targetHeight;
|
||
tAttachment.top = 'top';
|
||
|
||
top -= height;
|
||
eAttachment.top = 'bottom';
|
||
} else if (eAttachment.top === 'bottom' && top < bounds[1] && top + (height * 2 - targetHeight) <= bounds[3]) {
|
||
top += height - targetHeight;
|
||
tAttachment.top = 'top';
|
||
|
||
eAttachment.top = 'top';
|
||
}
|
||
}
|
||
|
||
if (tAttachment.top === 'middle') {
|
||
if (top + height > bounds[3] && eAttachment.top === 'top') {
|
||
top -= height;
|
||
eAttachment.top = 'bottom';
|
||
} else if (top < bounds[1] && eAttachment.top === 'bottom') {
|
||
top += height;
|
||
eAttachment.top = 'top';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (changeAttachX === 'target' || changeAttachX === 'both') {
|
||
if (left < bounds[0] && tAttachment.left === 'left') {
|
||
left += targetWidth;
|
||
tAttachment.left = 'right';
|
||
}
|
||
|
||
if (left + width > bounds[2] && tAttachment.left === 'right') {
|
||
left -= targetWidth;
|
||
tAttachment.left = 'left';
|
||
}
|
||
}
|
||
|
||
if (changeAttachX === 'together') {
|
||
if (left < bounds[0] && tAttachment.left === 'left') {
|
||
if (eAttachment.left === 'right') {
|
||
left += targetWidth;
|
||
tAttachment.left = 'right';
|
||
|
||
left += width;
|
||
eAttachment.left = 'left';
|
||
} else if (eAttachment.left === 'left') {
|
||
left += targetWidth;
|
||
tAttachment.left = 'right';
|
||
|
||
left -= width;
|
||
eAttachment.left = 'right';
|
||
}
|
||
} else if (left + width > bounds[2] && tAttachment.left === 'right') {
|
||
if (eAttachment.left === 'left') {
|
||
left -= targetWidth;
|
||
tAttachment.left = 'left';
|
||
|
||
left -= width;
|
||
eAttachment.left = 'right';
|
||
} else if (eAttachment.left === 'right') {
|
||
left -= targetWidth;
|
||
tAttachment.left = 'left';
|
||
|
||
left += width;
|
||
eAttachment.left = 'left';
|
||
}
|
||
} else if (tAttachment.left === 'center') {
|
||
if (left + width > bounds[2] && eAttachment.left === 'left') {
|
||
left -= width;
|
||
eAttachment.left = 'right';
|
||
} else if (left < bounds[0] && eAttachment.left === 'right') {
|
||
left += width;
|
||
eAttachment.left = 'left';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (changeAttachY === 'element' || changeAttachY === 'both') {
|
||
if (top < bounds[1] && eAttachment.top === 'bottom') {
|
||
top += height;
|
||
eAttachment.top = 'top';
|
||
}
|
||
|
||
if (top + height > bounds[3] && eAttachment.top === 'top') {
|
||
top -= height;
|
||
eAttachment.top = 'bottom';
|
||
}
|
||
}
|
||
|
||
if (changeAttachX === 'element' || changeAttachX === 'both') {
|
||
if (left < bounds[0]) {
|
||
if (eAttachment.left === 'right') {
|
||
left += width;
|
||
eAttachment.left = 'left';
|
||
} else if (eAttachment.left === 'center') {
|
||
left += width / 2;
|
||
eAttachment.left = 'left';
|
||
}
|
||
}
|
||
|
||
if (left + width > bounds[2]) {
|
||
if (eAttachment.left === 'left') {
|
||
left -= width;
|
||
eAttachment.left = 'right';
|
||
} else if (eAttachment.left === 'center') {
|
||
left -= width / 2;
|
||
eAttachment.left = 'right';
|
||
}
|
||
}
|
||
}
|
||
|
||
if (typeof pin === 'string') {
|
||
pin = pin.split(',').map(function (p) {
|
||
return p.trim();
|
||
});
|
||
} else if (pin === true) {
|
||
pin = ['top', 'left', 'right', 'bottom'];
|
||
}
|
||
|
||
pin = pin || [];
|
||
|
||
var pinned = [];
|
||
var oob = [];
|
||
|
||
if (top < bounds[1]) {
|
||
if (pin.indexOf('top') >= 0) {
|
||
top = bounds[1];
|
||
pinned.push('top');
|
||
} else {
|
||
oob.push('top');
|
||
}
|
||
}
|
||
|
||
if (top + height > bounds[3]) {
|
||
if (pin.indexOf('bottom') >= 0) {
|
||
top = bounds[3] - height;
|
||
pinned.push('bottom');
|
||
} else {
|
||
oob.push('bottom');
|
||
}
|
||
}
|
||
|
||
if (left < bounds[0]) {
|
||
if (pin.indexOf('left') >= 0) {
|
||
left = bounds[0];
|
||
pinned.push('left');
|
||
} else {
|
||
oob.push('left');
|
||
}
|
||
}
|
||
|
||
if (left + width > bounds[2]) {
|
||
if (pin.indexOf('right') >= 0) {
|
||
left = bounds[2] - width;
|
||
pinned.push('right');
|
||
} else {
|
||
oob.push('right');
|
||
}
|
||
}
|
||
|
||
if (pinned.length) {
|
||
(function () {
|
||
var pinnedClass = undefined;
|
||
if (typeof _this.options.pinnedClass !== 'undefined') {
|
||
pinnedClass = _this.options.pinnedClass;
|
||
} else {
|
||
pinnedClass = _this.getClass('pinned');
|
||
}
|
||
|
||
addClasses.push(pinnedClass);
|
||
pinned.forEach(function (side) {
|
||
addClasses.push(pinnedClass + '-' + side);
|
||
});
|
||
})();
|
||
}
|
||
|
||
if (oob.length) {
|
||
(function () {
|
||
var oobClass = undefined;
|
||
if (typeof _this.options.outOfBoundsClass !== 'undefined') {
|
||
oobClass = _this.options.outOfBoundsClass;
|
||
} else {
|
||
oobClass = _this.getClass('out-of-bounds');
|
||
}
|
||
|
||
addClasses.push(oobClass);
|
||
oob.forEach(function (side) {
|
||
addClasses.push(oobClass + '-' + side);
|
||
});
|
||
})();
|
||
}
|
||
|
||
if (pinned.indexOf('left') >= 0 || pinned.indexOf('right') >= 0) {
|
||
eAttachment.left = tAttachment.left = false;
|
||
}
|
||
if (pinned.indexOf('top') >= 0 || pinned.indexOf('bottom') >= 0) {
|
||
eAttachment.top = tAttachment.top = false;
|
||
}
|
||
|
||
if (tAttachment.top !== targetAttachment.top || tAttachment.left !== targetAttachment.left || eAttachment.top !== _this.attachment.top || eAttachment.left !== _this.attachment.left) {
|
||
_this.updateAttachClasses(eAttachment, tAttachment);
|
||
_this.trigger('update', {
|
||
attachment: eAttachment,
|
||
targetAttachment: tAttachment
|
||
});
|
||
}
|
||
});
|
||
|
||
defer(function () {
|
||
if (!(_this.options.addTargetClasses === false)) {
|
||
updateClasses(_this.target, addClasses, allClasses);
|
||
}
|
||
updateClasses(_this.element, addClasses, allClasses);
|
||
});
|
||
|
||
return { top: top, left: left };
|
||
}
|
||
});
|
||
/* globals TetherBase */
|
||
|
||
'use strict';
|
||
|
||
var _TetherBase$Utils = TetherBase.Utils;
|
||
var getBounds = _TetherBase$Utils.getBounds;
|
||
var updateClasses = _TetherBase$Utils.updateClasses;
|
||
var defer = _TetherBase$Utils.defer;
|
||
|
||
TetherBase.modules.push({
|
||
position: function position(_ref) {
|
||
var _this = this;
|
||
|
||
var top = _ref.top;
|
||
var left = _ref.left;
|
||
|
||
var _cache = this.cache('element-bounds', function () {
|
||
return getBounds(_this.element);
|
||
});
|
||
|
||
var height = _cache.height;
|
||
var width = _cache.width;
|
||
|
||
var targetPos = this.getTargetBounds();
|
||
|
||
var bottom = top + height;
|
||
var right = left + width;
|
||
|
||
var abutted = [];
|
||
if (top <= targetPos.bottom && bottom >= targetPos.top) {
|
||
['left', 'right'].forEach(function (side) {
|
||
var targetPosSide = targetPos[side];
|
||
if (targetPosSide === left || targetPosSide === right) {
|
||
abutted.push(side);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (left <= targetPos.right && right >= targetPos.left) {
|
||
['top', 'bottom'].forEach(function (side) {
|
||
var targetPosSide = targetPos[side];
|
||
if (targetPosSide === top || targetPosSide === bottom) {
|
||
abutted.push(side);
|
||
}
|
||
});
|
||
}
|
||
|
||
var allClasses = [];
|
||
var addClasses = [];
|
||
|
||
var sides = ['left', 'top', 'right', 'bottom'];
|
||
allClasses.push(this.getClass('abutted'));
|
||
sides.forEach(function (side) {
|
||
allClasses.push(_this.getClass('abutted') + '-' + side);
|
||
});
|
||
|
||
if (abutted.length) {
|
||
addClasses.push(this.getClass('abutted'));
|
||
}
|
||
|
||
abutted.forEach(function (side) {
|
||
addClasses.push(_this.getClass('abutted') + '-' + side);
|
||
});
|
||
|
||
defer(function () {
|
||
if (!(_this.options.addTargetClasses === false)) {
|
||
updateClasses(_this.target, addClasses, allClasses);
|
||
}
|
||
updateClasses(_this.element, addClasses, allClasses);
|
||
});
|
||
|
||
return true;
|
||
}
|
||
});
|
||
/* globals TetherBase */
|
||
|
||
'use strict';
|
||
|
||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
|
||
|
||
TetherBase.modules.push({
|
||
position: function position(_ref) {
|
||
var top = _ref.top;
|
||
var left = _ref.left;
|
||
|
||
if (!this.options.shift) {
|
||
return;
|
||
}
|
||
|
||
var shift = this.options.shift;
|
||
if (typeof this.options.shift === 'function') {
|
||
shift = this.options.shift.call(this, { top: top, left: left });
|
||
}
|
||
|
||
var shiftTop = undefined,
|
||
shiftLeft = undefined;
|
||
if (typeof shift === 'string') {
|
||
shift = shift.split(' ');
|
||
shift[1] = shift[1] || shift[0];
|
||
|
||
var _shift = shift;
|
||
|
||
var _shift2 = _slicedToArray(_shift, 2);
|
||
|
||
shiftTop = _shift2[0];
|
||
shiftLeft = _shift2[1];
|
||
|
||
shiftTop = parseFloat(shiftTop, 10);
|
||
shiftLeft = parseFloat(shiftLeft, 10);
|
||
} else {
|
||
shiftTop = shift.top;
|
||
shiftLeft = shift.left;
|
||
}
|
||
|
||
top += shiftTop;
|
||
left += shiftLeft;
|
||
|
||
return { top: top, left: left };
|
||
}
|
||
});
|
||
return Tether;
|
||
|
||
}));
|
||
|
||
/*! jQuery UI - v1.12.1 - 2017-03-19
|
||
* http://jqueryui.com
|
||
* Includes: widget.js, position.js, data.js, disable-selection.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/draggable.js, widgets/droppable.js, widgets/resizable.js, widgets/selectable.js, widgets/sortable.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/selectmenu.js, widgets/slider.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js
|
||
* Copyright jQuery Foundation and other contributors; Licensed MIT */
|
||
|
||
(function( factory ) {
|
||
if ( typeof define === "function" && define.amd ) {
|
||
|
||
// AMD. Register as an anonymous module.
|
||
define([ "jquery" ], factory );
|
||
} else {
|
||
|
||
// Browser globals
|
||
factory( jQuery );
|
||
}
|
||
}(function( $ ) {
|
||
|
||
$.ui = $.ui || {};
|
||
|
||
var version = $.ui.version = "1.12.1";
|
||
|
||
|
||
/*!
|
||
* jQuery UI Widget 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Widget
|
||
//>>group: Core
|
||
//>>description: Provides a factory for creating stateful widgets with a common API.
|
||
//>>docs: http://api.jqueryui.com/jQuery.widget/
|
||
//>>demos: http://jqueryui.com/widget/
|
||
|
||
|
||
|
||
var widgetUuid = 0;
|
||
var widgetSlice = Array.prototype.slice;
|
||
|
||
$.cleanData = ( function( orig ) {
|
||
return function( elems ) {
|
||
var events, elem, i;
|
||
for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) {
|
||
try {
|
||
|
||
// Only trigger remove when necessary to save time
|
||
events = $._data( elem, "events" );
|
||
if ( events && events.remove ) {
|
||
$( elem ).triggerHandler( "remove" );
|
||
}
|
||
|
||
// Http://bugs.jquery.com/ticket/8235
|
||
} catch ( e ) {}
|
||
}
|
||
orig( elems );
|
||
};
|
||
} )( $.cleanData );
|
||
|
||
$.widget = function( name, base, prototype ) {
|
||
var existingConstructor, constructor, basePrototype;
|
||
|
||
// ProxiedPrototype allows the provided prototype to remain unmodified
|
||
// so that it can be used as a mixin for multiple widgets (#8876)
|
||
var proxiedPrototype = {};
|
||
|
||
var namespace = name.split( "." )[ 0 ];
|
||
name = name.split( "." )[ 1 ];
|
||
var fullName = namespace + "-" + name;
|
||
|
||
if ( !prototype ) {
|
||
prototype = base;
|
||
base = $.Widget;
|
||
}
|
||
|
||
if ( $.isArray( prototype ) ) {
|
||
prototype = $.extend.apply( null, [ {} ].concat( prototype ) );
|
||
}
|
||
|
||
// Create selector for plugin
|
||
$.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
|
||
return !!$.data( elem, fullName );
|
||
};
|
||
|
||
$[ namespace ] = $[ namespace ] || {};
|
||
existingConstructor = $[ namespace ][ name ];
|
||
constructor = $[ namespace ][ name ] = function( options, element ) {
|
||
|
||
// Allow instantiation without "new" keyword
|
||
if ( !this._createWidget ) {
|
||
return new constructor( options, element );
|
||
}
|
||
|
||
// Allow instantiation without initializing for simple inheritance
|
||
// must use "new" keyword (the code above always passes args)
|
||
if ( arguments.length ) {
|
||
this._createWidget( options, element );
|
||
}
|
||
};
|
||
|
||
// Extend with the existing constructor to carry over any static properties
|
||
$.extend( constructor, existingConstructor, {
|
||
version: prototype.version,
|
||
|
||
// Copy the object used to create the prototype in case we need to
|
||
// redefine the widget later
|
||
_proto: $.extend( {}, prototype ),
|
||
|
||
// Track widgets that inherit from this widget in case this widget is
|
||
// redefined after a widget inherits from it
|
||
_childConstructors: []
|
||
} );
|
||
|
||
basePrototype = new base();
|
||
|
||
// We need to make the options hash a property directly on the new instance
|
||
// otherwise we'll modify the options hash on the prototype that we're
|
||
// inheriting from
|
||
basePrototype.options = $.widget.extend( {}, basePrototype.options );
|
||
$.each( prototype, function( prop, value ) {
|
||
if ( !$.isFunction( value ) ) {
|
||
proxiedPrototype[ prop ] = value;
|
||
return;
|
||
}
|
||
proxiedPrototype[ prop ] = ( function() {
|
||
function _super() {
|
||
return base.prototype[ prop ].apply( this, arguments );
|
||
}
|
||
|
||
function _superApply( args ) {
|
||
return base.prototype[ prop ].apply( this, args );
|
||
}
|
||
|
||
return function() {
|
||
var __super = this._super;
|
||
var __superApply = this._superApply;
|
||
var returnValue;
|
||
|
||
this._super = _super;
|
||
this._superApply = _superApply;
|
||
|
||
returnValue = value.apply( this, arguments );
|
||
|
||
this._super = __super;
|
||
this._superApply = __superApply;
|
||
|
||
return returnValue;
|
||
};
|
||
} )();
|
||
} );
|
||
constructor.prototype = $.widget.extend( basePrototype, {
|
||
|
||
// TODO: remove support for widgetEventPrefix
|
||
// always use the name + a colon as the prefix, e.g., draggable:start
|
||
// don't prefix for widgets that aren't DOM-based
|
||
widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name
|
||
}, proxiedPrototype, {
|
||
constructor: constructor,
|
||
namespace: namespace,
|
||
widgetName: name,
|
||
widgetFullName: fullName
|
||
} );
|
||
|
||
// If this widget is being redefined then we need to find all widgets that
|
||
// are inheriting from it and redefine all of them so that they inherit from
|
||
// the new version of this widget. We're essentially trying to replace one
|
||
// level in the prototype chain.
|
||
if ( existingConstructor ) {
|
||
$.each( existingConstructor._childConstructors, function( i, child ) {
|
||
var childPrototype = child.prototype;
|
||
|
||
// Redefine the child widget using the same prototype that was
|
||
// originally used, but inherit from the new version of the base
|
||
$.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor,
|
||
child._proto );
|
||
} );
|
||
|
||
// Remove the list of existing child constructors from the old constructor
|
||
// so the old child constructors can be garbage collected
|
||
delete existingConstructor._childConstructors;
|
||
} else {
|
||
base._childConstructors.push( constructor );
|
||
}
|
||
|
||
$.widget.bridge( name, constructor );
|
||
|
||
return constructor;
|
||
};
|
||
|
||
$.widget.extend = function( target ) {
|
||
var input = widgetSlice.call( arguments, 1 );
|
||
var inputIndex = 0;
|
||
var inputLength = input.length;
|
||
var key;
|
||
var value;
|
||
|
||
for ( ; inputIndex < inputLength; inputIndex++ ) {
|
||
for ( key in input[ inputIndex ] ) {
|
||
value = input[ inputIndex ][ key ];
|
||
if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
|
||
|
||
// Clone objects
|
||
if ( $.isPlainObject( value ) ) {
|
||
target[ key ] = $.isPlainObject( target[ key ] ) ?
|
||
$.widget.extend( {}, target[ key ], value ) :
|
||
|
||
// Don't extend strings, arrays, etc. with objects
|
||
$.widget.extend( {}, value );
|
||
|
||
// Copy everything else by reference
|
||
} else {
|
||
target[ key ] = value;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return target;
|
||
};
|
||
|
||
$.widget.bridge = function( name, object ) {
|
||
var fullName = object.prototype.widgetFullName || name;
|
||
$.fn[ name ] = function( options ) {
|
||
var isMethodCall = typeof options === "string";
|
||
var args = widgetSlice.call( arguments, 1 );
|
||
var returnValue = this;
|
||
|
||
if ( isMethodCall ) {
|
||
|
||
// If this is an empty collection, we need to have the instance method
|
||
// return undefined instead of the jQuery instance
|
||
if ( !this.length && options === "instance" ) {
|
||
returnValue = undefined;
|
||
} else {
|
||
this.each( function() {
|
||
var methodValue;
|
||
var instance = $.data( this, fullName );
|
||
|
||
if ( options === "instance" ) {
|
||
returnValue = instance;
|
||
return false;
|
||
}
|
||
|
||
if ( !instance ) {
|
||
return $.error( "cannot call methods on " + name +
|
||
" prior to initialization; " +
|
||
"attempted to call method '" + options + "'" );
|
||
}
|
||
|
||
if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) {
|
||
return $.error( "no such method '" + options + "' for " + name +
|
||
" widget instance" );
|
||
}
|
||
|
||
methodValue = instance[ options ].apply( instance, args );
|
||
|
||
if ( methodValue !== instance && methodValue !== undefined ) {
|
||
returnValue = methodValue && methodValue.jquery ?
|
||
returnValue.pushStack( methodValue.get() ) :
|
||
methodValue;
|
||
return false;
|
||
}
|
||
} );
|
||
}
|
||
} else {
|
||
|
||
// Allow multiple hashes to be passed on init
|
||
if ( args.length ) {
|
||
options = $.widget.extend.apply( null, [ options ].concat( args ) );
|
||
}
|
||
|
||
this.each( function() {
|
||
var instance = $.data( this, fullName );
|
||
if ( instance ) {
|
||
instance.option( options || {} );
|
||
if ( instance._init ) {
|
||
instance._init();
|
||
}
|
||
} else {
|
||
$.data( this, fullName, new object( options, this ) );
|
||
}
|
||
} );
|
||
}
|
||
|
||
return returnValue;
|
||
};
|
||
};
|
||
|
||
$.Widget = function( /* options, element */ ) {};
|
||
$.Widget._childConstructors = [];
|
||
|
||
$.Widget.prototype = {
|
||
widgetName: "widget",
|
||
widgetEventPrefix: "",
|
||
defaultElement: "<div>",
|
||
|
||
options: {
|
||
classes: {},
|
||
disabled: false,
|
||
|
||
// Callbacks
|
||
create: null
|
||
},
|
||
|
||
_createWidget: function( options, element ) {
|
||
element = $( element || this.defaultElement || this )[ 0 ];
|
||
this.element = $( element );
|
||
this.uuid = widgetUuid++;
|
||
this.eventNamespace = "." + this.widgetName + this.uuid;
|
||
|
||
this.bindings = $();
|
||
this.hoverable = $();
|
||
this.focusable = $();
|
||
this.classesElementLookup = {};
|
||
|
||
if ( element !== this ) {
|
||
$.data( element, this.widgetFullName, this );
|
||
this._on( true, this.element, {
|
||
remove: function( event ) {
|
||
if ( event.target === element ) {
|
||
this.destroy();
|
||
}
|
||
}
|
||
} );
|
||
this.document = $( element.style ?
|
||
|
||
// Element within the document
|
||
element.ownerDocument :
|
||
|
||
// Element is window or document
|
||
element.document || element );
|
||
this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow );
|
||
}
|
||
|
||
this.options = $.widget.extend( {},
|
||
this.options,
|
||
this._getCreateOptions(),
|
||
options );
|
||
|
||
this._create();
|
||
|
||
if ( this.options.disabled ) {
|
||
this._setOptionDisabled( this.options.disabled );
|
||
}
|
||
|
||
this._trigger( "create", null, this._getCreateEventData() );
|
||
this._init();
|
||
},
|
||
|
||
_getCreateOptions: function() {
|
||
return {};
|
||
},
|
||
|
||
_getCreateEventData: $.noop,
|
||
|
||
_create: $.noop,
|
||
|
||
_init: $.noop,
|
||
|
||
destroy: function() {
|
||
var that = this;
|
||
|
||
this._destroy();
|
||
$.each( this.classesElementLookup, function( key, value ) {
|
||
that._removeClass( value, key );
|
||
} );
|
||
|
||
// We can probably remove the unbind calls in 2.0
|
||
// all event bindings should go through this._on()
|
||
this.element
|
||
.off( this.eventNamespace )
|
||
.removeData( this.widgetFullName );
|
||
this.widget()
|
||
.off( this.eventNamespace )
|
||
.removeAttr( "aria-disabled" );
|
||
|
||
// Clean up events and states
|
||
this.bindings.off( this.eventNamespace );
|
||
},
|
||
|
||
_destroy: $.noop,
|
||
|
||
widget: function() {
|
||
return this.element;
|
||
},
|
||
|
||
option: function( key, value ) {
|
||
var options = key;
|
||
var parts;
|
||
var curOption;
|
||
var i;
|
||
|
||
if ( arguments.length === 0 ) {
|
||
|
||
// Don't return a reference to the internal hash
|
||
return $.widget.extend( {}, this.options );
|
||
}
|
||
|
||
if ( typeof key === "string" ) {
|
||
|
||
// Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
|
||
options = {};
|
||
parts = key.split( "." );
|
||
key = parts.shift();
|
||
if ( parts.length ) {
|
||
curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
|
||
for ( i = 0; i < parts.length - 1; i++ ) {
|
||
curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
|
||
curOption = curOption[ parts[ i ] ];
|
||
}
|
||
key = parts.pop();
|
||
if ( arguments.length === 1 ) {
|
||
return curOption[ key ] === undefined ? null : curOption[ key ];
|
||
}
|
||
curOption[ key ] = value;
|
||
} else {
|
||
if ( arguments.length === 1 ) {
|
||
return this.options[ key ] === undefined ? null : this.options[ key ];
|
||
}
|
||
options[ key ] = value;
|
||
}
|
||
}
|
||
|
||
this._setOptions( options );
|
||
|
||
return this;
|
||
},
|
||
|
||
_setOptions: function( options ) {
|
||
var key;
|
||
|
||
for ( key in options ) {
|
||
this._setOption( key, options[ key ] );
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "classes" ) {
|
||
this._setOptionClasses( value );
|
||
}
|
||
|
||
this.options[ key ] = value;
|
||
|
||
if ( key === "disabled" ) {
|
||
this._setOptionDisabled( value );
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
_setOptionClasses: function( value ) {
|
||
var classKey, elements, currentElements;
|
||
|
||
for ( classKey in value ) {
|
||
currentElements = this.classesElementLookup[ classKey ];
|
||
if ( value[ classKey ] === this.options.classes[ classKey ] ||
|
||
!currentElements ||
|
||
!currentElements.length ) {
|
||
continue;
|
||
}
|
||
|
||
// We are doing this to create a new jQuery object because the _removeClass() call
|
||
// on the next line is going to destroy the reference to the current elements being
|
||
// tracked. We need to save a copy of this collection so that we can add the new classes
|
||
// below.
|
||
elements = $( currentElements.get() );
|
||
this._removeClass( currentElements, classKey );
|
||
|
||
// We don't use _addClass() here, because that uses this.options.classes
|
||
// for generating the string of classes. We want to use the value passed in from
|
||
// _setOption(), this is the new value of the classes option which was passed to
|
||
// _setOption(). We pass this value directly to _classes().
|
||
elements.addClass( this._classes( {
|
||
element: elements,
|
||
keys: classKey,
|
||
classes: value,
|
||
add: true
|
||
} ) );
|
||
}
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value );
|
||
|
||
// If the widget is becoming disabled, then nothing is interactive
|
||
if ( value ) {
|
||
this._removeClass( this.hoverable, null, "ui-state-hover" );
|
||
this._removeClass( this.focusable, null, "ui-state-focus" );
|
||
}
|
||
},
|
||
|
||
enable: function() {
|
||
return this._setOptions( { disabled: false } );
|
||
},
|
||
|
||
disable: function() {
|
||
return this._setOptions( { disabled: true } );
|
||
},
|
||
|
||
_classes: function( options ) {
|
||
var full = [];
|
||
var that = this;
|
||
|
||
options = $.extend( {
|
||
element: this.element,
|
||
classes: this.options.classes || {}
|
||
}, options );
|
||
|
||
function processClassString( classes, checkOption ) {
|
||
var current, i;
|
||
for ( i = 0; i < classes.length; i++ ) {
|
||
current = that.classesElementLookup[ classes[ i ] ] || $();
|
||
if ( options.add ) {
|
||
current = $( $.unique( current.get().concat( options.element.get() ) ) );
|
||
} else {
|
||
current = $( current.not( options.element ).get() );
|
||
}
|
||
that.classesElementLookup[ classes[ i ] ] = current;
|
||
full.push( classes[ i ] );
|
||
if ( checkOption && options.classes[ classes[ i ] ] ) {
|
||
full.push( options.classes[ classes[ i ] ] );
|
||
}
|
||
}
|
||
}
|
||
|
||
this._on( options.element, {
|
||
"remove": "_untrackClassesElement"
|
||
} );
|
||
|
||
if ( options.keys ) {
|
||
processClassString( options.keys.match( /\S+/g ) || [], true );
|
||
}
|
||
if ( options.extra ) {
|
||
processClassString( options.extra.match( /\S+/g ) || [] );
|
||
}
|
||
|
||
return full.join( " " );
|
||
},
|
||
|
||
_untrackClassesElement: function( event ) {
|
||
var that = this;
|
||
$.each( that.classesElementLookup, function( key, value ) {
|
||
if ( $.inArray( event.target, value ) !== -1 ) {
|
||
that.classesElementLookup[ key ] = $( value.not( event.target ).get() );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_removeClass: function( element, keys, extra ) {
|
||
return this._toggleClass( element, keys, extra, false );
|
||
},
|
||
|
||
_addClass: function( element, keys, extra ) {
|
||
return this._toggleClass( element, keys, extra, true );
|
||
},
|
||
|
||
_toggleClass: function( element, keys, extra, add ) {
|
||
add = ( typeof add === "boolean" ) ? add : extra;
|
||
var shift = ( typeof element === "string" || element === null ),
|
||
options = {
|
||
extra: shift ? keys : extra,
|
||
keys: shift ? element : keys,
|
||
element: shift ? this.element : element,
|
||
add: add
|
||
};
|
||
options.element.toggleClass( this._classes( options ), add );
|
||
return this;
|
||
},
|
||
|
||
_on: function( suppressDisabledCheck, element, handlers ) {
|
||
var delegateElement;
|
||
var instance = this;
|
||
|
||
// No suppressDisabledCheck flag, shuffle arguments
|
||
if ( typeof suppressDisabledCheck !== "boolean" ) {
|
||
handlers = element;
|
||
element = suppressDisabledCheck;
|
||
suppressDisabledCheck = false;
|
||
}
|
||
|
||
// No element argument, shuffle and use this.element
|
||
if ( !handlers ) {
|
||
handlers = element;
|
||
element = this.element;
|
||
delegateElement = this.widget();
|
||
} else {
|
||
element = delegateElement = $( element );
|
||
this.bindings = this.bindings.add( element );
|
||
}
|
||
|
||
$.each( handlers, function( event, handler ) {
|
||
function handlerProxy() {
|
||
|
||
// Allow widgets to customize the disabled handling
|
||
// - disabled as an array instead of boolean
|
||
// - disabled class as method for disabling individual parts
|
||
if ( !suppressDisabledCheck &&
|
||
( instance.options.disabled === true ||
|
||
$( this ).hasClass( "ui-state-disabled" ) ) ) {
|
||
return;
|
||
}
|
||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||
.apply( instance, arguments );
|
||
}
|
||
|
||
// Copy the guid so direct unbinding works
|
||
if ( typeof handler !== "string" ) {
|
||
handlerProxy.guid = handler.guid =
|
||
handler.guid || handlerProxy.guid || $.guid++;
|
||
}
|
||
|
||
var match = event.match( /^([\w:-]*)\s*(.*)$/ );
|
||
var eventName = match[ 1 ] + instance.eventNamespace;
|
||
var selector = match[ 2 ];
|
||
|
||
if ( selector ) {
|
||
delegateElement.on( eventName, selector, handlerProxy );
|
||
} else {
|
||
element.on( eventName, handlerProxy );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_off: function( element, eventName ) {
|
||
eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) +
|
||
this.eventNamespace;
|
||
element.off( eventName ).off( eventName );
|
||
|
||
// Clear the stack to avoid memory leaks (#10056)
|
||
this.bindings = $( this.bindings.not( element ).get() );
|
||
this.focusable = $( this.focusable.not( element ).get() );
|
||
this.hoverable = $( this.hoverable.not( element ).get() );
|
||
},
|
||
|
||
_delay: function( handler, delay ) {
|
||
function handlerProxy() {
|
||
return ( typeof handler === "string" ? instance[ handler ] : handler )
|
||
.apply( instance, arguments );
|
||
}
|
||
var instance = this;
|
||
return setTimeout( handlerProxy, delay || 0 );
|
||
},
|
||
|
||
_hoverable: function( element ) {
|
||
this.hoverable = this.hoverable.add( element );
|
||
this._on( element, {
|
||
mouseenter: function( event ) {
|
||
this._addClass( $( event.currentTarget ), null, "ui-state-hover" );
|
||
},
|
||
mouseleave: function( event ) {
|
||
this._removeClass( $( event.currentTarget ), null, "ui-state-hover" );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_focusable: function( element ) {
|
||
this.focusable = this.focusable.add( element );
|
||
this._on( element, {
|
||
focusin: function( event ) {
|
||
this._addClass( $( event.currentTarget ), null, "ui-state-focus" );
|
||
},
|
||
focusout: function( event ) {
|
||
this._removeClass( $( event.currentTarget ), null, "ui-state-focus" );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_trigger: function( type, event, data ) {
|
||
var prop, orig;
|
||
var callback = this.options[ type ];
|
||
|
||
data = data || {};
|
||
event = $.Event( event );
|
||
event.type = ( type === this.widgetEventPrefix ?
|
||
type :
|
||
this.widgetEventPrefix + type ).toLowerCase();
|
||
|
||
// The original event may come from any element
|
||
// so we need to reset the target on the new event
|
||
event.target = this.element[ 0 ];
|
||
|
||
// Copy original event properties over to the new event
|
||
orig = event.originalEvent;
|
||
if ( orig ) {
|
||
for ( prop in orig ) {
|
||
if ( !( prop in event ) ) {
|
||
event[ prop ] = orig[ prop ];
|
||
}
|
||
}
|
||
}
|
||
|
||
this.element.trigger( event, data );
|
||
return !( $.isFunction( callback ) &&
|
||
callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false ||
|
||
event.isDefaultPrevented() );
|
||
}
|
||
};
|
||
|
||
$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
|
||
$.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
|
||
if ( typeof options === "string" ) {
|
||
options = { effect: options };
|
||
}
|
||
|
||
var hasOptions;
|
||
var effectName = !options ?
|
||
method :
|
||
options === true || typeof options === "number" ?
|
||
defaultEffect :
|
||
options.effect || defaultEffect;
|
||
|
||
options = options || {};
|
||
if ( typeof options === "number" ) {
|
||
options = { duration: options };
|
||
}
|
||
|
||
hasOptions = !$.isEmptyObject( options );
|
||
options.complete = callback;
|
||
|
||
if ( options.delay ) {
|
||
element.delay( options.delay );
|
||
}
|
||
|
||
if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
|
||
element[ method ]( options );
|
||
} else if ( effectName !== method && element[ effectName ] ) {
|
||
element[ effectName ]( options.duration, options.easing, callback );
|
||
} else {
|
||
element.queue( function( next ) {
|
||
$( this )[ method ]();
|
||
if ( callback ) {
|
||
callback.call( element[ 0 ] );
|
||
}
|
||
next();
|
||
} );
|
||
}
|
||
};
|
||
} );
|
||
|
||
var widget = $.widget;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Position 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*
|
||
* http://api.jqueryui.com/position/
|
||
*/
|
||
|
||
//>>label: Position
|
||
//>>group: Core
|
||
//>>description: Positions elements relative to other elements.
|
||
//>>docs: http://api.jqueryui.com/position/
|
||
//>>demos: http://jqueryui.com/position/
|
||
|
||
|
||
( function() {
|
||
var cachedScrollbarWidth,
|
||
max = Math.max,
|
||
abs = Math.abs,
|
||
rhorizontal = /left|center|right/,
|
||
rvertical = /top|center|bottom/,
|
||
roffset = /[\+\-]\d+(\.[\d]+)?%?/,
|
||
rposition = /^\w+/,
|
||
rpercent = /%$/,
|
||
_position = $.fn.position;
|
||
|
||
function getOffsets( offsets, width, height ) {
|
||
return [
|
||
parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
|
||
parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
|
||
];
|
||
}
|
||
|
||
function parseCss( element, property ) {
|
||
return parseInt( $.css( element, property ), 10 ) || 0;
|
||
}
|
||
|
||
function getDimensions( elem ) {
|
||
var raw = elem[ 0 ];
|
||
if ( raw.nodeType === 9 ) {
|
||
return {
|
||
width: elem.width(),
|
||
height: elem.height(),
|
||
offset: { top: 0, left: 0 }
|
||
};
|
||
}
|
||
if ( $.isWindow( raw ) ) {
|
||
return {
|
||
width: elem.width(),
|
||
height: elem.height(),
|
||
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
|
||
};
|
||
}
|
||
if ( raw.preventDefault ) {
|
||
return {
|
||
width: 0,
|
||
height: 0,
|
||
offset: { top: raw.pageY, left: raw.pageX }
|
||
};
|
||
}
|
||
return {
|
||
width: elem.outerWidth(),
|
||
height: elem.outerHeight(),
|
||
offset: elem.offset()
|
||
};
|
||
}
|
||
|
||
$.position = {
|
||
scrollbarWidth: function() {
|
||
if ( cachedScrollbarWidth !== undefined ) {
|
||
return cachedScrollbarWidth;
|
||
}
|
||
var w1, w2,
|
||
div = $( "<div " +
|
||
"style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
|
||
"<div style='height:100px;width:auto;'></div></div>" ),
|
||
innerDiv = div.children()[ 0 ];
|
||
|
||
$( "body" ).append( div );
|
||
w1 = innerDiv.offsetWidth;
|
||
div.css( "overflow", "scroll" );
|
||
|
||
w2 = innerDiv.offsetWidth;
|
||
|
||
if ( w1 === w2 ) {
|
||
w2 = div[ 0 ].clientWidth;
|
||
}
|
||
|
||
div.remove();
|
||
|
||
return ( cachedScrollbarWidth = w1 - w2 );
|
||
},
|
||
getScrollInfo: function( within ) {
|
||
var overflowX = within.isWindow || within.isDocument ? "" :
|
||
within.element.css( "overflow-x" ),
|
||
overflowY = within.isWindow || within.isDocument ? "" :
|
||
within.element.css( "overflow-y" ),
|
||
hasOverflowX = overflowX === "scroll" ||
|
||
( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
|
||
hasOverflowY = overflowY === "scroll" ||
|
||
( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
|
||
return {
|
||
width: hasOverflowY ? $.position.scrollbarWidth() : 0,
|
||
height: hasOverflowX ? $.position.scrollbarWidth() : 0
|
||
};
|
||
},
|
||
getWithinInfo: function( element ) {
|
||
var withinElement = $( element || window ),
|
||
isWindow = $.isWindow( withinElement[ 0 ] ),
|
||
isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
|
||
hasOffset = !isWindow && !isDocument;
|
||
return {
|
||
element: withinElement,
|
||
isWindow: isWindow,
|
||
isDocument: isDocument,
|
||
offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
|
||
scrollLeft: withinElement.scrollLeft(),
|
||
scrollTop: withinElement.scrollTop(),
|
||
width: withinElement.outerWidth(),
|
||
height: withinElement.outerHeight()
|
||
};
|
||
}
|
||
};
|
||
|
||
$.fn.position = function( options ) {
|
||
if ( !options || !options.of ) {
|
||
return _position.apply( this, arguments );
|
||
}
|
||
|
||
// Make a copy, we don't want to modify arguments
|
||
options = $.extend( {}, options );
|
||
|
||
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
|
||
target = $( options.of ),
|
||
within = $.position.getWithinInfo( options.within ),
|
||
scrollInfo = $.position.getScrollInfo( within ),
|
||
collision = ( options.collision || "flip" ).split( " " ),
|
||
offsets = {};
|
||
|
||
dimensions = getDimensions( target );
|
||
if ( target[ 0 ].preventDefault ) {
|
||
|
||
// Force left top to allow flipping
|
||
options.at = "left top";
|
||
}
|
||
targetWidth = dimensions.width;
|
||
targetHeight = dimensions.height;
|
||
targetOffset = dimensions.offset;
|
||
|
||
// Clone to reuse original targetOffset later
|
||
basePosition = $.extend( {}, targetOffset );
|
||
|
||
// Force my and at to have valid horizontal and vertical positions
|
||
// if a value is missing or invalid, it will be converted to center
|
||
$.each( [ "my", "at" ], function() {
|
||
var pos = ( options[ this ] || "" ).split( " " ),
|
||
horizontalOffset,
|
||
verticalOffset;
|
||
|
||
if ( pos.length === 1 ) {
|
||
pos = rhorizontal.test( pos[ 0 ] ) ?
|
||
pos.concat( [ "center" ] ) :
|
||
rvertical.test( pos[ 0 ] ) ?
|
||
[ "center" ].concat( pos ) :
|
||
[ "center", "center" ];
|
||
}
|
||
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
|
||
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
|
||
|
||
// Calculate offsets
|
||
horizontalOffset = roffset.exec( pos[ 0 ] );
|
||
verticalOffset = roffset.exec( pos[ 1 ] );
|
||
offsets[ this ] = [
|
||
horizontalOffset ? horizontalOffset[ 0 ] : 0,
|
||
verticalOffset ? verticalOffset[ 0 ] : 0
|
||
];
|
||
|
||
// Reduce to just the positions without the offsets
|
||
options[ this ] = [
|
||
rposition.exec( pos[ 0 ] )[ 0 ],
|
||
rposition.exec( pos[ 1 ] )[ 0 ]
|
||
];
|
||
} );
|
||
|
||
// Normalize collision option
|
||
if ( collision.length === 1 ) {
|
||
collision[ 1 ] = collision[ 0 ];
|
||
}
|
||
|
||
if ( options.at[ 0 ] === "right" ) {
|
||
basePosition.left += targetWidth;
|
||
} else if ( options.at[ 0 ] === "center" ) {
|
||
basePosition.left += targetWidth / 2;
|
||
}
|
||
|
||
if ( options.at[ 1 ] === "bottom" ) {
|
||
basePosition.top += targetHeight;
|
||
} else if ( options.at[ 1 ] === "center" ) {
|
||
basePosition.top += targetHeight / 2;
|
||
}
|
||
|
||
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
|
||
basePosition.left += atOffset[ 0 ];
|
||
basePosition.top += atOffset[ 1 ];
|
||
|
||
return this.each( function() {
|
||
var collisionPosition, using,
|
||
elem = $( this ),
|
||
elemWidth = elem.outerWidth(),
|
||
elemHeight = elem.outerHeight(),
|
||
marginLeft = parseCss( this, "marginLeft" ),
|
||
marginTop = parseCss( this, "marginTop" ),
|
||
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
|
||
scrollInfo.width,
|
||
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
|
||
scrollInfo.height,
|
||
position = $.extend( {}, basePosition ),
|
||
myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
|
||
|
||
if ( options.my[ 0 ] === "right" ) {
|
||
position.left -= elemWidth;
|
||
} else if ( options.my[ 0 ] === "center" ) {
|
||
position.left -= elemWidth / 2;
|
||
}
|
||
|
||
if ( options.my[ 1 ] === "bottom" ) {
|
||
position.top -= elemHeight;
|
||
} else if ( options.my[ 1 ] === "center" ) {
|
||
position.top -= elemHeight / 2;
|
||
}
|
||
|
||
position.left += myOffset[ 0 ];
|
||
position.top += myOffset[ 1 ];
|
||
|
||
collisionPosition = {
|
||
marginLeft: marginLeft,
|
||
marginTop: marginTop
|
||
};
|
||
|
||
$.each( [ "left", "top" ], function( i, dir ) {
|
||
if ( $.ui.position[ collision[ i ] ] ) {
|
||
$.ui.position[ collision[ i ] ][ dir ]( position, {
|
||
targetWidth: targetWidth,
|
||
targetHeight: targetHeight,
|
||
elemWidth: elemWidth,
|
||
elemHeight: elemHeight,
|
||
collisionPosition: collisionPosition,
|
||
collisionWidth: collisionWidth,
|
||
collisionHeight: collisionHeight,
|
||
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
|
||
my: options.my,
|
||
at: options.at,
|
||
within: within,
|
||
elem: elem
|
||
} );
|
||
}
|
||
} );
|
||
|
||
if ( options.using ) {
|
||
|
||
// Adds feedback as second argument to using callback, if present
|
||
using = function( props ) {
|
||
var left = targetOffset.left - position.left,
|
||
right = left + targetWidth - elemWidth,
|
||
top = targetOffset.top - position.top,
|
||
bottom = top + targetHeight - elemHeight,
|
||
feedback = {
|
||
target: {
|
||
element: target,
|
||
left: targetOffset.left,
|
||
top: targetOffset.top,
|
||
width: targetWidth,
|
||
height: targetHeight
|
||
},
|
||
element: {
|
||
element: elem,
|
||
left: position.left,
|
||
top: position.top,
|
||
width: elemWidth,
|
||
height: elemHeight
|
||
},
|
||
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
|
||
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
|
||
};
|
||
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
|
||
feedback.horizontal = "center";
|
||
}
|
||
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
|
||
feedback.vertical = "middle";
|
||
}
|
||
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
|
||
feedback.important = "horizontal";
|
||
} else {
|
||
feedback.important = "vertical";
|
||
}
|
||
options.using.call( this, props, feedback );
|
||
};
|
||
}
|
||
|
||
elem.offset( $.extend( position, { using: using } ) );
|
||
} );
|
||
};
|
||
|
||
$.ui.position = {
|
||
fit: {
|
||
left: function( position, data ) {
|
||
var within = data.within,
|
||
withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
|
||
outerWidth = within.width,
|
||
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
|
||
overLeft = withinOffset - collisionPosLeft,
|
||
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
|
||
newOverRight;
|
||
|
||
// Element is wider than within
|
||
if ( data.collisionWidth > outerWidth ) {
|
||
|
||
// Element is initially over the left side of within
|
||
if ( overLeft > 0 && overRight <= 0 ) {
|
||
newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
|
||
withinOffset;
|
||
position.left += overLeft - newOverRight;
|
||
|
||
// Element is initially over right side of within
|
||
} else if ( overRight > 0 && overLeft <= 0 ) {
|
||
position.left = withinOffset;
|
||
|
||
// Element is initially over both left and right sides of within
|
||
} else {
|
||
if ( overLeft > overRight ) {
|
||
position.left = withinOffset + outerWidth - data.collisionWidth;
|
||
} else {
|
||
position.left = withinOffset;
|
||
}
|
||
}
|
||
|
||
// Too far left -> align with left edge
|
||
} else if ( overLeft > 0 ) {
|
||
position.left += overLeft;
|
||
|
||
// Too far right -> align with right edge
|
||
} else if ( overRight > 0 ) {
|
||
position.left -= overRight;
|
||
|
||
// Adjust based on position and margin
|
||
} else {
|
||
position.left = max( position.left - collisionPosLeft, position.left );
|
||
}
|
||
},
|
||
top: function( position, data ) {
|
||
var within = data.within,
|
||
withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
|
||
outerHeight = data.within.height,
|
||
collisionPosTop = position.top - data.collisionPosition.marginTop,
|
||
overTop = withinOffset - collisionPosTop,
|
||
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
|
||
newOverBottom;
|
||
|
||
// Element is taller than within
|
||
if ( data.collisionHeight > outerHeight ) {
|
||
|
||
// Element is initially over the top of within
|
||
if ( overTop > 0 && overBottom <= 0 ) {
|
||
newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
|
||
withinOffset;
|
||
position.top += overTop - newOverBottom;
|
||
|
||
// Element is initially over bottom of within
|
||
} else if ( overBottom > 0 && overTop <= 0 ) {
|
||
position.top = withinOffset;
|
||
|
||
// Element is initially over both top and bottom of within
|
||
} else {
|
||
if ( overTop > overBottom ) {
|
||
position.top = withinOffset + outerHeight - data.collisionHeight;
|
||
} else {
|
||
position.top = withinOffset;
|
||
}
|
||
}
|
||
|
||
// Too far up -> align with top
|
||
} else if ( overTop > 0 ) {
|
||
position.top += overTop;
|
||
|
||
// Too far down -> align with bottom edge
|
||
} else if ( overBottom > 0 ) {
|
||
position.top -= overBottom;
|
||
|
||
// Adjust based on position and margin
|
||
} else {
|
||
position.top = max( position.top - collisionPosTop, position.top );
|
||
}
|
||
}
|
||
},
|
||
flip: {
|
||
left: function( position, data ) {
|
||
var within = data.within,
|
||
withinOffset = within.offset.left + within.scrollLeft,
|
||
outerWidth = within.width,
|
||
offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
|
||
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
|
||
overLeft = collisionPosLeft - offsetLeft,
|
||
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
|
||
myOffset = data.my[ 0 ] === "left" ?
|
||
-data.elemWidth :
|
||
data.my[ 0 ] === "right" ?
|
||
data.elemWidth :
|
||
0,
|
||
atOffset = data.at[ 0 ] === "left" ?
|
||
data.targetWidth :
|
||
data.at[ 0 ] === "right" ?
|
||
-data.targetWidth :
|
||
0,
|
||
offset = -2 * data.offset[ 0 ],
|
||
newOverRight,
|
||
newOverLeft;
|
||
|
||
if ( overLeft < 0 ) {
|
||
newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
|
||
outerWidth - withinOffset;
|
||
if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
|
||
position.left += myOffset + atOffset + offset;
|
||
}
|
||
} else if ( overRight > 0 ) {
|
||
newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
|
||
atOffset + offset - offsetLeft;
|
||
if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
|
||
position.left += myOffset + atOffset + offset;
|
||
}
|
||
}
|
||
},
|
||
top: function( position, data ) {
|
||
var within = data.within,
|
||
withinOffset = within.offset.top + within.scrollTop,
|
||
outerHeight = within.height,
|
||
offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
|
||
collisionPosTop = position.top - data.collisionPosition.marginTop,
|
||
overTop = collisionPosTop - offsetTop,
|
||
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
|
||
top = data.my[ 1 ] === "top",
|
||
myOffset = top ?
|
||
-data.elemHeight :
|
||
data.my[ 1 ] === "bottom" ?
|
||
data.elemHeight :
|
||
0,
|
||
atOffset = data.at[ 1 ] === "top" ?
|
||
data.targetHeight :
|
||
data.at[ 1 ] === "bottom" ?
|
||
-data.targetHeight :
|
||
0,
|
||
offset = -2 * data.offset[ 1 ],
|
||
newOverTop,
|
||
newOverBottom;
|
||
if ( overTop < 0 ) {
|
||
newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
|
||
outerHeight - withinOffset;
|
||
if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
|
||
position.top += myOffset + atOffset + offset;
|
||
}
|
||
} else if ( overBottom > 0 ) {
|
||
newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
|
||
offset - offsetTop;
|
||
if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
|
||
position.top += myOffset + atOffset + offset;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
flipfit: {
|
||
left: function() {
|
||
$.ui.position.flip.left.apply( this, arguments );
|
||
$.ui.position.fit.left.apply( this, arguments );
|
||
},
|
||
top: function() {
|
||
$.ui.position.flip.top.apply( this, arguments );
|
||
$.ui.position.fit.top.apply( this, arguments );
|
||
}
|
||
}
|
||
};
|
||
|
||
} )();
|
||
|
||
var position = $.ui.position;
|
||
|
||
|
||
/*!
|
||
* jQuery UI :data 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: :data Selector
|
||
//>>group: Core
|
||
//>>description: Selects elements which have data stored under the specified key.
|
||
//>>docs: http://api.jqueryui.com/data-selector/
|
||
|
||
|
||
var data = $.extend( $.expr[ ":" ], {
|
||
data: $.expr.createPseudo ?
|
||
$.expr.createPseudo( function( dataName ) {
|
||
return function( elem ) {
|
||
return !!$.data( elem, dataName );
|
||
};
|
||
} ) :
|
||
|
||
// Support: jQuery <1.8
|
||
function( elem, i, match ) {
|
||
return !!$.data( elem, match[ 3 ] );
|
||
}
|
||
} );
|
||
|
||
/*!
|
||
* jQuery UI Disable Selection 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: disableSelection
|
||
//>>group: Core
|
||
//>>description: Disable selection of text content within the set of matched elements.
|
||
//>>docs: http://api.jqueryui.com/disableSelection/
|
||
|
||
// This file is deprecated
|
||
|
||
|
||
var disableSelection = $.fn.extend( {
|
||
disableSelection: ( function() {
|
||
var eventType = "onselectstart" in document.createElement( "div" ) ?
|
||
"selectstart" :
|
||
"mousedown";
|
||
|
||
return function() {
|
||
return this.on( eventType + ".ui-disableSelection", function( event ) {
|
||
event.preventDefault();
|
||
} );
|
||
};
|
||
} )(),
|
||
|
||
enableSelection: function() {
|
||
return this.off( ".ui-disableSelection" );
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Focusable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: :focusable Selector
|
||
//>>group: Core
|
||
//>>description: Selects elements which can be focused.
|
||
//>>docs: http://api.jqueryui.com/focusable-selector/
|
||
|
||
|
||
|
||
// Selectors
|
||
$.ui.focusable = function( element, hasTabindex ) {
|
||
var map, mapName, img, focusableIfVisible, fieldset,
|
||
nodeName = element.nodeName.toLowerCase();
|
||
|
||
if ( "area" === nodeName ) {
|
||
map = element.parentNode;
|
||
mapName = map.name;
|
||
if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
|
||
return false;
|
||
}
|
||
img = $( "img[usemap='#" + mapName + "']" );
|
||
return img.length > 0 && img.is( ":visible" );
|
||
}
|
||
|
||
if ( /^(input|select|textarea|button|object)$/.test( nodeName ) ) {
|
||
focusableIfVisible = !element.disabled;
|
||
|
||
if ( focusableIfVisible ) {
|
||
|
||
// Form controls within a disabled fieldset are disabled.
|
||
// However, controls within the fieldset's legend do not get disabled.
|
||
// Since controls generally aren't placed inside legends, we skip
|
||
// this portion of the check.
|
||
fieldset = $( element ).closest( "fieldset" )[ 0 ];
|
||
if ( fieldset ) {
|
||
focusableIfVisible = !fieldset.disabled;
|
||
}
|
||
}
|
||
} else if ( "a" === nodeName ) {
|
||
focusableIfVisible = element.href || hasTabindex;
|
||
} else {
|
||
focusableIfVisible = hasTabindex;
|
||
}
|
||
|
||
return focusableIfVisible && $( element ).is( ":visible" ) && visible( $( element ) );
|
||
};
|
||
|
||
// Support: IE 8 only
|
||
// IE 8 doesn't resolve inherit to visible/hidden for computed values
|
||
function visible( element ) {
|
||
var visibility = element.css( "visibility" );
|
||
while ( visibility === "inherit" ) {
|
||
element = element.parent();
|
||
visibility = element.css( "visibility" );
|
||
}
|
||
return visibility !== "hidden";
|
||
}
|
||
|
||
$.extend( $.expr[ ":" ], {
|
||
focusable: function( element ) {
|
||
return $.ui.focusable( element, $.attr( element, "tabindex" ) != null );
|
||
}
|
||
} );
|
||
|
||
var focusable = $.ui.focusable;
|
||
|
||
|
||
|
||
|
||
// Support: IE8 Only
|
||
// IE8 does not support the form attribute and when it is supplied. It overwrites the form prop
|
||
// with a string, so we need to find the proper form.
|
||
var form = $.fn.form = function() {
|
||
return typeof this[ 0 ].form === "string" ? this.closest( "form" ) : $( this[ 0 ].form );
|
||
};
|
||
|
||
|
||
/*!
|
||
* jQuery UI Form Reset Mixin 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Form Reset Mixin
|
||
//>>group: Core
|
||
//>>description: Refresh input widgets when their form is reset
|
||
//>>docs: http://api.jqueryui.com/form-reset-mixin/
|
||
|
||
|
||
|
||
var formResetMixin = $.ui.formResetMixin = {
|
||
_formResetHandler: function() {
|
||
var form = $( this );
|
||
|
||
// Wait for the form reset to actually happen before refreshing
|
||
setTimeout( function() {
|
||
var instances = form.data( "ui-form-reset-instances" );
|
||
$.each( instances, function() {
|
||
this.refresh();
|
||
} );
|
||
} );
|
||
},
|
||
|
||
_bindFormResetHandler: function() {
|
||
this.form = this.element.form();
|
||
if ( !this.form.length ) {
|
||
return;
|
||
}
|
||
|
||
var instances = this.form.data( "ui-form-reset-instances" ) || [];
|
||
if ( !instances.length ) {
|
||
|
||
// We don't use _on() here because we use a single event handler per form
|
||
this.form.on( "reset.ui-form-reset", this._formResetHandler );
|
||
}
|
||
instances.push( this );
|
||
this.form.data( "ui-form-reset-instances", instances );
|
||
},
|
||
|
||
_unbindFormResetHandler: function() {
|
||
if ( !this.form.length ) {
|
||
return;
|
||
}
|
||
|
||
var instances = this.form.data( "ui-form-reset-instances" );
|
||
instances.splice( $.inArray( this, instances ), 1 );
|
||
if ( instances.length ) {
|
||
this.form.data( "ui-form-reset-instances", instances );
|
||
} else {
|
||
this.form
|
||
.removeData( "ui-form-reset-instances" )
|
||
.off( "reset.ui-form-reset" );
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
/*!
|
||
* jQuery UI Support for jQuery core 1.7.x 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*
|
||
*/
|
||
|
||
//>>label: jQuery 1.7 Support
|
||
//>>group: Core
|
||
//>>description: Support version 1.7.x of jQuery core
|
||
|
||
|
||
|
||
// Support: jQuery 1.7 only
|
||
// Not a great way to check versions, but since we only support 1.7+ and only
|
||
// need to detect <1.8, this is a simple check that should suffice. Checking
|
||
// for "1.7." would be a bit safer, but the version string is 1.7, not 1.7.0
|
||
// and we'll never reach 1.70.0 (if we do, we certainly won't be supporting
|
||
// 1.7 anymore). See #11197 for why we're not using feature detection.
|
||
if ( $.fn.jquery.substring( 0, 3 ) === "1.7" ) {
|
||
|
||
// Setters for .innerWidth(), .innerHeight(), .outerWidth(), .outerHeight()
|
||
// Unlike jQuery Core 1.8+, these only support numeric values to set the
|
||
// dimensions in pixels
|
||
$.each( [ "Width", "Height" ], function( i, name ) {
|
||
var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
|
||
type = name.toLowerCase(),
|
||
orig = {
|
||
innerWidth: $.fn.innerWidth,
|
||
innerHeight: $.fn.innerHeight,
|
||
outerWidth: $.fn.outerWidth,
|
||
outerHeight: $.fn.outerHeight
|
||
};
|
||
|
||
function reduce( elem, size, border, margin ) {
|
||
$.each( side, function() {
|
||
size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
|
||
if ( border ) {
|
||
size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
|
||
}
|
||
if ( margin ) {
|
||
size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
|
||
}
|
||
} );
|
||
return size;
|
||
}
|
||
|
||
$.fn[ "inner" + name ] = function( size ) {
|
||
if ( size === undefined ) {
|
||
return orig[ "inner" + name ].call( this );
|
||
}
|
||
|
||
return this.each( function() {
|
||
$( this ).css( type, reduce( this, size ) + "px" );
|
||
} );
|
||
};
|
||
|
||
$.fn[ "outer" + name ] = function( size, margin ) {
|
||
if ( typeof size !== "number" ) {
|
||
return orig[ "outer" + name ].call( this, size );
|
||
}
|
||
|
||
return this.each( function() {
|
||
$( this ).css( type, reduce( this, size, true, margin ) + "px" );
|
||
} );
|
||
};
|
||
} );
|
||
|
||
$.fn.addBack = function( selector ) {
|
||
return this.add( selector == null ?
|
||
this.prevObject : this.prevObject.filter( selector )
|
||
);
|
||
};
|
||
}
|
||
|
||
;
|
||
/*!
|
||
* jQuery UI Keycode 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Keycode
|
||
//>>group: Core
|
||
//>>description: Provide keycodes as keynames
|
||
//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/
|
||
|
||
|
||
var keycode = $.ui.keyCode = {
|
||
BACKSPACE: 8,
|
||
COMMA: 188,
|
||
DELETE: 46,
|
||
DOWN: 40,
|
||
END: 35,
|
||
ENTER: 13,
|
||
ESCAPE: 27,
|
||
HOME: 36,
|
||
LEFT: 37,
|
||
PAGE_DOWN: 34,
|
||
PAGE_UP: 33,
|
||
PERIOD: 190,
|
||
RIGHT: 39,
|
||
SPACE: 32,
|
||
TAB: 9,
|
||
UP: 38
|
||
};
|
||
|
||
|
||
|
||
|
||
// Internal use only
|
||
var escapeSelector = $.ui.escapeSelector = ( function() {
|
||
var selectorEscape = /([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;
|
||
return function( selector ) {
|
||
return selector.replace( selectorEscape, "\\$1" );
|
||
};
|
||
} )();
|
||
|
||
|
||
/*!
|
||
* jQuery UI Labels 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: labels
|
||
//>>group: Core
|
||
//>>description: Find all the labels associated with a given input
|
||
//>>docs: http://api.jqueryui.com/labels/
|
||
|
||
|
||
|
||
var labels = $.fn.labels = function() {
|
||
var ancestor, selector, id, labels, ancestors;
|
||
|
||
// Check control.labels first
|
||
if ( this[ 0 ].labels && this[ 0 ].labels.length ) {
|
||
return this.pushStack( this[ 0 ].labels );
|
||
}
|
||
|
||
// Support: IE <= 11, FF <= 37, Android <= 2.3 only
|
||
// Above browsers do not support control.labels. Everything below is to support them
|
||
// as well as document fragments. control.labels does not work on document fragments
|
||
labels = this.eq( 0 ).parents( "label" );
|
||
|
||
// Look for the label based on the id
|
||
id = this.attr( "id" );
|
||
if ( id ) {
|
||
|
||
// We don't search against the document in case the element
|
||
// is disconnected from the DOM
|
||
ancestor = this.eq( 0 ).parents().last();
|
||
|
||
// Get a full set of top level ancestors
|
||
ancestors = ancestor.add( ancestor.length ? ancestor.siblings() : this.siblings() );
|
||
|
||
// Create a selector for the label based on the id
|
||
selector = "label[for='" + $.ui.escapeSelector( id ) + "']";
|
||
|
||
labels = labels.add( ancestors.find( selector ).addBack( selector ) );
|
||
|
||
}
|
||
|
||
// Return whatever we have found for labels
|
||
return this.pushStack( labels );
|
||
};
|
||
|
||
|
||
/*!
|
||
* jQuery UI Scroll Parent 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: scrollParent
|
||
//>>group: Core
|
||
//>>description: Get the closest ancestor element that is scrollable.
|
||
//>>docs: http://api.jqueryui.com/scrollParent/
|
||
|
||
|
||
|
||
var scrollParent = $.fn.scrollParent = function( includeHidden ) {
|
||
var position = this.css( "position" ),
|
||
excludeStaticParent = position === "absolute",
|
||
overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
|
||
scrollParent = this.parents().filter( function() {
|
||
var parent = $( this );
|
||
if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
|
||
return false;
|
||
}
|
||
return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) +
|
||
parent.css( "overflow-x" ) );
|
||
} ).eq( 0 );
|
||
|
||
return position === "fixed" || !scrollParent.length ?
|
||
$( this[ 0 ].ownerDocument || document ) :
|
||
scrollParent;
|
||
};
|
||
|
||
|
||
/*!
|
||
* jQuery UI Tabbable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: :tabbable Selector
|
||
//>>group: Core
|
||
//>>description: Selects elements which can be tabbed to.
|
||
//>>docs: http://api.jqueryui.com/tabbable-selector/
|
||
|
||
|
||
|
||
var tabbable = $.extend( $.expr[ ":" ], {
|
||
tabbable: function( element ) {
|
||
var tabIndex = $.attr( element, "tabindex" ),
|
||
hasTabindex = tabIndex != null;
|
||
return ( !hasTabindex || tabIndex >= 0 ) && $.ui.focusable( element, hasTabindex );
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Unique ID 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: uniqueId
|
||
//>>group: Core
|
||
//>>description: Functions to generate and remove uniqueId's
|
||
//>>docs: http://api.jqueryui.com/uniqueId/
|
||
|
||
|
||
|
||
var uniqueId = $.fn.extend( {
|
||
uniqueId: ( function() {
|
||
var uuid = 0;
|
||
|
||
return function() {
|
||
return this.each( function() {
|
||
if ( !this.id ) {
|
||
this.id = "ui-id-" + ( ++uuid );
|
||
}
|
||
} );
|
||
};
|
||
} )(),
|
||
|
||
removeUniqueId: function() {
|
||
return this.each( function() {
|
||
if ( /^ui-id-\d+$/.test( this.id ) ) {
|
||
$( this ).removeAttr( "id" );
|
||
}
|
||
} );
|
||
}
|
||
} );
|
||
|
||
|
||
|
||
|
||
// This file is deprecated
|
||
var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
|
||
|
||
/*!
|
||
* jQuery UI Mouse 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Mouse
|
||
//>>group: Widgets
|
||
//>>description: Abstracts mouse-based interactions to assist in creating certain widgets.
|
||
//>>docs: http://api.jqueryui.com/mouse/
|
||
|
||
|
||
|
||
var mouseHandled = false;
|
||
$( document ).on( "mouseup", function() {
|
||
mouseHandled = false;
|
||
} );
|
||
|
||
var widgetsMouse = $.widget( "ui.mouse", {
|
||
version: "1.12.1",
|
||
options: {
|
||
cancel: "input, textarea, button, select, option",
|
||
distance: 1,
|
||
delay: 0
|
||
},
|
||
_mouseInit: function() {
|
||
var that = this;
|
||
|
||
this.element
|
||
.on( "mousedown." + this.widgetName, function( event ) {
|
||
return that._mouseDown( event );
|
||
} )
|
||
.on( "click." + this.widgetName, function( event ) {
|
||
if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) {
|
||
$.removeData( event.target, that.widgetName + ".preventClickEvent" );
|
||
event.stopImmediatePropagation();
|
||
return false;
|
||
}
|
||
} );
|
||
|
||
this.started = false;
|
||
},
|
||
|
||
// TODO: make sure destroying one instance of mouse doesn't mess with
|
||
// other instances of mouse
|
||
_mouseDestroy: function() {
|
||
this.element.off( "." + this.widgetName );
|
||
if ( this._mouseMoveDelegate ) {
|
||
this.document
|
||
.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
|
||
.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
|
||
}
|
||
},
|
||
|
||
_mouseDown: function( event ) {
|
||
|
||
// don't let more than one widget handle mouseStart
|
||
if ( mouseHandled ) {
|
||
return;
|
||
}
|
||
|
||
this._mouseMoved = false;
|
||
|
||
// We may have missed mouseup (out of window)
|
||
( this._mouseStarted && this._mouseUp( event ) );
|
||
|
||
this._mouseDownEvent = event;
|
||
|
||
var that = this,
|
||
btnIsLeft = ( event.which === 1 ),
|
||
|
||
// event.target.nodeName works around a bug in IE 8 with
|
||
// disabled inputs (#7620)
|
||
elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ?
|
||
$( event.target ).closest( this.options.cancel ).length : false );
|
||
if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) {
|
||
return true;
|
||
}
|
||
|
||
this.mouseDelayMet = !this.options.delay;
|
||
if ( !this.mouseDelayMet ) {
|
||
this._mouseDelayTimer = setTimeout( function() {
|
||
that.mouseDelayMet = true;
|
||
}, this.options.delay );
|
||
}
|
||
|
||
if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
|
||
this._mouseStarted = ( this._mouseStart( event ) !== false );
|
||
if ( !this._mouseStarted ) {
|
||
event.preventDefault();
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// Click event may never have fired (Gecko & Opera)
|
||
if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) {
|
||
$.removeData( event.target, this.widgetName + ".preventClickEvent" );
|
||
}
|
||
|
||
// These delegates are required to keep context
|
||
this._mouseMoveDelegate = function( event ) {
|
||
return that._mouseMove( event );
|
||
};
|
||
this._mouseUpDelegate = function( event ) {
|
||
return that._mouseUp( event );
|
||
};
|
||
|
||
this.document
|
||
.on( "mousemove." + this.widgetName, this._mouseMoveDelegate )
|
||
.on( "mouseup." + this.widgetName, this._mouseUpDelegate );
|
||
|
||
event.preventDefault();
|
||
|
||
mouseHandled = true;
|
||
return true;
|
||
},
|
||
|
||
_mouseMove: function( event ) {
|
||
|
||
// Only check for mouseups outside the document if you've moved inside the document
|
||
// at least once. This prevents the firing of mouseup in the case of IE<9, which will
|
||
// fire a mousemove event if content is placed under the cursor. See #7778
|
||
// Support: IE <9
|
||
if ( this._mouseMoved ) {
|
||
|
||
// IE mouseup check - mouseup happened when mouse was out of window
|
||
if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) &&
|
||
!event.button ) {
|
||
return this._mouseUp( event );
|
||
|
||
// Iframe mouseup check - mouseup occurred in another document
|
||
} else if ( !event.which ) {
|
||
|
||
// Support: Safari <=8 - 9
|
||
// Safari sets which to 0 if you press any of the following keys
|
||
// during a drag (#14461)
|
||
if ( event.originalEvent.altKey || event.originalEvent.ctrlKey ||
|
||
event.originalEvent.metaKey || event.originalEvent.shiftKey ) {
|
||
this.ignoreMissingWhich = true;
|
||
} else if ( !this.ignoreMissingWhich ) {
|
||
return this._mouseUp( event );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( event.which || event.button ) {
|
||
this._mouseMoved = true;
|
||
}
|
||
|
||
if ( this._mouseStarted ) {
|
||
this._mouseDrag( event );
|
||
return event.preventDefault();
|
||
}
|
||
|
||
if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) {
|
||
this._mouseStarted =
|
||
( this._mouseStart( this._mouseDownEvent, event ) !== false );
|
||
( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) );
|
||
}
|
||
|
||
return !this._mouseStarted;
|
||
},
|
||
|
||
_mouseUp: function( event ) {
|
||
this.document
|
||
.off( "mousemove." + this.widgetName, this._mouseMoveDelegate )
|
||
.off( "mouseup." + this.widgetName, this._mouseUpDelegate );
|
||
|
||
if ( this._mouseStarted ) {
|
||
this._mouseStarted = false;
|
||
|
||
if ( event.target === this._mouseDownEvent.target ) {
|
||
$.data( event.target, this.widgetName + ".preventClickEvent", true );
|
||
}
|
||
|
||
this._mouseStop( event );
|
||
}
|
||
|
||
if ( this._mouseDelayTimer ) {
|
||
clearTimeout( this._mouseDelayTimer );
|
||
delete this._mouseDelayTimer;
|
||
}
|
||
|
||
this.ignoreMissingWhich = false;
|
||
mouseHandled = false;
|
||
event.preventDefault();
|
||
},
|
||
|
||
_mouseDistanceMet: function( event ) {
|
||
return ( Math.max(
|
||
Math.abs( this._mouseDownEvent.pageX - event.pageX ),
|
||
Math.abs( this._mouseDownEvent.pageY - event.pageY )
|
||
) >= this.options.distance
|
||
);
|
||
},
|
||
|
||
_mouseDelayMet: function( /* event */ ) {
|
||
return this.mouseDelayMet;
|
||
},
|
||
|
||
// These are placeholder methods, to be overriden by extending plugin
|
||
_mouseStart: function( /* event */ ) {},
|
||
_mouseDrag: function( /* event */ ) {},
|
||
_mouseStop: function( /* event */ ) {},
|
||
_mouseCapture: function( /* event */ ) { return true; }
|
||
} );
|
||
|
||
|
||
|
||
|
||
// $.ui.plugin is deprecated. Use $.widget() extensions instead.
|
||
var plugin = $.ui.plugin = {
|
||
add: function( module, option, set ) {
|
||
var i,
|
||
proto = $.ui[ module ].prototype;
|
||
for ( i in set ) {
|
||
proto.plugins[ i ] = proto.plugins[ i ] || [];
|
||
proto.plugins[ i ].push( [ option, set[ i ] ] );
|
||
}
|
||
},
|
||
call: function( instance, name, args, allowDisconnected ) {
|
||
var i,
|
||
set = instance.plugins[ name ];
|
||
|
||
if ( !set ) {
|
||
return;
|
||
}
|
||
|
||
if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode ||
|
||
instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
|
||
return;
|
||
}
|
||
|
||
for ( i = 0; i < set.length; i++ ) {
|
||
if ( instance.options[ set[ i ][ 0 ] ] ) {
|
||
set[ i ][ 1 ].apply( instance.element, args );
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
|
||
|
||
var safeActiveElement = $.ui.safeActiveElement = function( document ) {
|
||
var activeElement;
|
||
|
||
// Support: IE 9 only
|
||
// IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
|
||
try {
|
||
activeElement = document.activeElement;
|
||
} catch ( error ) {
|
||
activeElement = document.body;
|
||
}
|
||
|
||
// Support: IE 9 - 11 only
|
||
// IE may return null instead of an element
|
||
// Interestingly, this only seems to occur when NOT in an iframe
|
||
if ( !activeElement ) {
|
||
activeElement = document.body;
|
||
}
|
||
|
||
// Support: IE 11 only
|
||
// IE11 returns a seemingly empty object in some cases when accessing
|
||
// document.activeElement from an <iframe>
|
||
if ( !activeElement.nodeName ) {
|
||
activeElement = document.body;
|
||
}
|
||
|
||
return activeElement;
|
||
};
|
||
|
||
|
||
|
||
var safeBlur = $.ui.safeBlur = function( element ) {
|
||
|
||
// Support: IE9 - 10 only
|
||
// If the <body> is blurred, IE will switch windows, see #9420
|
||
if ( element && element.nodeName.toLowerCase() !== "body" ) {
|
||
$( element ).trigger( "blur" );
|
||
}
|
||
};
|
||
|
||
|
||
/*!
|
||
* jQuery UI Draggable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Draggable
|
||
//>>group: Interactions
|
||
//>>description: Enables dragging functionality for any element.
|
||
//>>docs: http://api.jqueryui.com/draggable/
|
||
//>>demos: http://jqueryui.com/draggable/
|
||
//>>css.structure: ../../themes/base/draggable.css
|
||
|
||
|
||
|
||
$.widget( "ui.draggable", $.ui.mouse, {
|
||
version: "1.12.1",
|
||
widgetEventPrefix: "drag",
|
||
options: {
|
||
addClasses: true,
|
||
appendTo: "parent",
|
||
axis: false,
|
||
connectToSortable: false,
|
||
containment: false,
|
||
cursor: "auto",
|
||
cursorAt: false,
|
||
grid: false,
|
||
handle: false,
|
||
helper: "original",
|
||
iframeFix: false,
|
||
opacity: false,
|
||
refreshPositions: false,
|
||
revert: false,
|
||
revertDuration: 500,
|
||
scope: "default",
|
||
scroll: true,
|
||
scrollSensitivity: 20,
|
||
scrollSpeed: 20,
|
||
snap: false,
|
||
snapMode: "both",
|
||
snapTolerance: 20,
|
||
stack: false,
|
||
zIndex: false,
|
||
|
||
// Callbacks
|
||
drag: null,
|
||
start: null,
|
||
stop: null
|
||
},
|
||
_create: function() {
|
||
|
||
if ( this.options.helper === "original" ) {
|
||
this._setPositionRelative();
|
||
}
|
||
if ( this.options.addClasses ) {
|
||
this._addClass( "ui-draggable" );
|
||
}
|
||
this._setHandleClassName();
|
||
|
||
this._mouseInit();
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
this._super( key, value );
|
||
if ( key === "handle" ) {
|
||
this._removeHandleClassName();
|
||
this._setHandleClassName();
|
||
}
|
||
},
|
||
|
||
_destroy: function() {
|
||
if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
|
||
this.destroyOnClear = true;
|
||
return;
|
||
}
|
||
this._removeHandleClassName();
|
||
this._mouseDestroy();
|
||
},
|
||
|
||
_mouseCapture: function( event ) {
|
||
var o = this.options;
|
||
|
||
// Among others, prevent a drag on a resizable-handle
|
||
if ( this.helper || o.disabled ||
|
||
$( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
|
||
return false;
|
||
}
|
||
|
||
//Quit if we're not on a valid handle
|
||
this.handle = this._getHandle( event );
|
||
if ( !this.handle ) {
|
||
return false;
|
||
}
|
||
|
||
this._blurActiveElement( event );
|
||
|
||
this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
|
||
|
||
return true;
|
||
|
||
},
|
||
|
||
_blockFrames: function( selector ) {
|
||
this.iframeBlocks = this.document.find( selector ).map( function() {
|
||
var iframe = $( this );
|
||
|
||
return $( "<div>" )
|
||
.css( "position", "absolute" )
|
||
.appendTo( iframe.parent() )
|
||
.outerWidth( iframe.outerWidth() )
|
||
.outerHeight( iframe.outerHeight() )
|
||
.offset( iframe.offset() )[ 0 ];
|
||
} );
|
||
},
|
||
|
||
_unblockFrames: function() {
|
||
if ( this.iframeBlocks ) {
|
||
this.iframeBlocks.remove();
|
||
delete this.iframeBlocks;
|
||
}
|
||
},
|
||
|
||
_blurActiveElement: function( event ) {
|
||
var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
|
||
target = $( event.target );
|
||
|
||
// Don't blur if the event occurred on an element that is within
|
||
// the currently focused element
|
||
// See #10527, #12472
|
||
if ( target.closest( activeElement ).length ) {
|
||
return;
|
||
}
|
||
|
||
// Blur any element that currently has focus, see #4261
|
||
$.ui.safeBlur( activeElement );
|
||
},
|
||
|
||
_mouseStart: function( event ) {
|
||
|
||
var o = this.options;
|
||
|
||
//Create and append the visible helper
|
||
this.helper = this._createHelper( event );
|
||
|
||
this._addClass( this.helper, "ui-draggable-dragging" );
|
||
|
||
//Cache the helper size
|
||
this._cacheHelperProportions();
|
||
|
||
//If ddmanager is used for droppables, set the global draggable
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.current = this;
|
||
}
|
||
|
||
/*
|
||
* - Position generation -
|
||
* This block generates everything position related - it's the core of draggables.
|
||
*/
|
||
|
||
//Cache the margins of the original element
|
||
this._cacheMargins();
|
||
|
||
//Store the helper's css position
|
||
this.cssPosition = this.helper.css( "position" );
|
||
this.scrollParent = this.helper.scrollParent( true );
|
||
this.offsetParent = this.helper.offsetParent();
|
||
this.hasFixedAncestor = this.helper.parents().filter( function() {
|
||
return $( this ).css( "position" ) === "fixed";
|
||
} ).length > 0;
|
||
|
||
//The element's absolute position on the page minus margins
|
||
this.positionAbs = this.element.offset();
|
||
this._refreshOffsets( event );
|
||
|
||
//Generate the original position
|
||
this.originalPosition = this.position = this._generatePosition( event, false );
|
||
this.originalPageX = event.pageX;
|
||
this.originalPageY = event.pageY;
|
||
|
||
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
|
||
( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
|
||
|
||
//Set a containment if given in the options
|
||
this._setContainment();
|
||
|
||
//Trigger event + callbacks
|
||
if ( this._trigger( "start", event ) === false ) {
|
||
this._clear();
|
||
return false;
|
||
}
|
||
|
||
//Recache the helper size
|
||
this._cacheHelperProportions();
|
||
|
||
//Prepare the droppable offsets
|
||
if ( $.ui.ddmanager && !o.dropBehaviour ) {
|
||
$.ui.ddmanager.prepareOffsets( this, event );
|
||
}
|
||
|
||
// Execute the drag once - this causes the helper not to be visible before getting its
|
||
// correct position
|
||
this._mouseDrag( event, true );
|
||
|
||
// If the ddmanager is used for droppables, inform the manager that dragging has started
|
||
// (see #5003)
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.dragStart( this, event );
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
_refreshOffsets: function( event ) {
|
||
this.offset = {
|
||
top: this.positionAbs.top - this.margins.top,
|
||
left: this.positionAbs.left - this.margins.left,
|
||
scroll: false,
|
||
parent: this._getParentOffset(),
|
||
relative: this._getRelativeOffset()
|
||
};
|
||
|
||
this.offset.click = {
|
||
left: event.pageX - this.offset.left,
|
||
top: event.pageY - this.offset.top
|
||
};
|
||
},
|
||
|
||
_mouseDrag: function( event, noPropagation ) {
|
||
|
||
// reset any necessary cached properties (see #5009)
|
||
if ( this.hasFixedAncestor ) {
|
||
this.offset.parent = this._getParentOffset();
|
||
}
|
||
|
||
//Compute the helpers position
|
||
this.position = this._generatePosition( event, true );
|
||
this.positionAbs = this._convertPositionTo( "absolute" );
|
||
|
||
//Call plugins and callbacks and use the resulting position if something is returned
|
||
if ( !noPropagation ) {
|
||
var ui = this._uiHash();
|
||
if ( this._trigger( "drag", event, ui ) === false ) {
|
||
this._mouseUp( new $.Event( "mouseup", event ) );
|
||
return false;
|
||
}
|
||
this.position = ui.position;
|
||
}
|
||
|
||
this.helper[ 0 ].style.left = this.position.left + "px";
|
||
this.helper[ 0 ].style.top = this.position.top + "px";
|
||
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.drag( this, event );
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
_mouseStop: function( event ) {
|
||
|
||
//If we are using droppables, inform the manager about the drop
|
||
var that = this,
|
||
dropped = false;
|
||
if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
|
||
dropped = $.ui.ddmanager.drop( this, event );
|
||
}
|
||
|
||
//if a drop comes from outside (a sortable)
|
||
if ( this.dropped ) {
|
||
dropped = this.dropped;
|
||
this.dropped = false;
|
||
}
|
||
|
||
if ( ( this.options.revert === "invalid" && !dropped ) ||
|
||
( this.options.revert === "valid" && dropped ) ||
|
||
this.options.revert === true || ( $.isFunction( this.options.revert ) &&
|
||
this.options.revert.call( this.element, dropped ) )
|
||
) {
|
||
$( this.helper ).animate(
|
||
this.originalPosition,
|
||
parseInt( this.options.revertDuration, 10 ),
|
||
function() {
|
||
if ( that._trigger( "stop", event ) !== false ) {
|
||
that._clear();
|
||
}
|
||
}
|
||
);
|
||
} else {
|
||
if ( this._trigger( "stop", event ) !== false ) {
|
||
this._clear();
|
||
}
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
_mouseUp: function( event ) {
|
||
this._unblockFrames();
|
||
|
||
// If the ddmanager is used for droppables, inform the manager that dragging has stopped
|
||
// (see #5003)
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.dragStop( this, event );
|
||
}
|
||
|
||
// Only need to focus if the event occurred on the draggable itself, see #10527
|
||
if ( this.handleElement.is( event.target ) ) {
|
||
|
||
// The interaction is over; whether or not the click resulted in a drag,
|
||
// focus the element
|
||
this.element.trigger( "focus" );
|
||
}
|
||
|
||
return $.ui.mouse.prototype._mouseUp.call( this, event );
|
||
},
|
||
|
||
cancel: function() {
|
||
|
||
if ( this.helper.is( ".ui-draggable-dragging" ) ) {
|
||
this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
|
||
} else {
|
||
this._clear();
|
||
}
|
||
|
||
return this;
|
||
|
||
},
|
||
|
||
_getHandle: function( event ) {
|
||
return this.options.handle ?
|
||
!!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
|
||
true;
|
||
},
|
||
|
||
_setHandleClassName: function() {
|
||
this.handleElement = this.options.handle ?
|
||
this.element.find( this.options.handle ) : this.element;
|
||
this._addClass( this.handleElement, "ui-draggable-handle" );
|
||
},
|
||
|
||
_removeHandleClassName: function() {
|
||
this._removeClass( this.handleElement, "ui-draggable-handle" );
|
||
},
|
||
|
||
_createHelper: function( event ) {
|
||
|
||
var o = this.options,
|
||
helperIsFunction = $.isFunction( o.helper ),
|
||
helper = helperIsFunction ?
|
||
$( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
|
||
( o.helper === "clone" ?
|
||
this.element.clone().removeAttr( "id" ) :
|
||
this.element );
|
||
|
||
if ( !helper.parents( "body" ).length ) {
|
||
helper.appendTo( ( o.appendTo === "parent" ?
|
||
this.element[ 0 ].parentNode :
|
||
o.appendTo ) );
|
||
}
|
||
|
||
// Http://bugs.jqueryui.com/ticket/9446
|
||
// a helper function can return the original element
|
||
// which wouldn't have been set to relative in _create
|
||
if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
|
||
this._setPositionRelative();
|
||
}
|
||
|
||
if ( helper[ 0 ] !== this.element[ 0 ] &&
|
||
!( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
|
||
helper.css( "position", "absolute" );
|
||
}
|
||
|
||
return helper;
|
||
|
||
},
|
||
|
||
_setPositionRelative: function() {
|
||
if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
|
||
this.element[ 0 ].style.position = "relative";
|
||
}
|
||
},
|
||
|
||
_adjustOffsetFromHelper: function( obj ) {
|
||
if ( typeof obj === "string" ) {
|
||
obj = obj.split( " " );
|
||
}
|
||
if ( $.isArray( obj ) ) {
|
||
obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
|
||
}
|
||
if ( "left" in obj ) {
|
||
this.offset.click.left = obj.left + this.margins.left;
|
||
}
|
||
if ( "right" in obj ) {
|
||
this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
|
||
}
|
||
if ( "top" in obj ) {
|
||
this.offset.click.top = obj.top + this.margins.top;
|
||
}
|
||
if ( "bottom" in obj ) {
|
||
this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
|
||
}
|
||
},
|
||
|
||
_isRootNode: function( element ) {
|
||
return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
|
||
},
|
||
|
||
_getParentOffset: function() {
|
||
|
||
//Get the offsetParent and cache its position
|
||
var po = this.offsetParent.offset(),
|
||
document = this.document[ 0 ];
|
||
|
||
// This is a special case where we need to modify a offset calculated on start, since the
|
||
// following happened:
|
||
// 1. The position of the helper is absolute, so it's position is calculated based on the
|
||
// next positioned parent
|
||
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
|
||
// the document, which means that the scroll is included in the initial calculation of the
|
||
// offset of the parent, and never recalculated upon drag
|
||
if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
|
||
$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
|
||
po.left += this.scrollParent.scrollLeft();
|
||
po.top += this.scrollParent.scrollTop();
|
||
}
|
||
|
||
if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
|
||
po = { top: 0, left: 0 };
|
||
}
|
||
|
||
return {
|
||
top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
|
||
left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
|
||
};
|
||
|
||
},
|
||
|
||
_getRelativeOffset: function() {
|
||
if ( this.cssPosition !== "relative" ) {
|
||
return { top: 0, left: 0 };
|
||
}
|
||
|
||
var p = this.element.position(),
|
||
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
|
||
|
||
return {
|
||
top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
|
||
( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
|
||
left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
|
||
( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
|
||
};
|
||
|
||
},
|
||
|
||
_cacheMargins: function() {
|
||
this.margins = {
|
||
left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
|
||
top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
|
||
right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
|
||
bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
|
||
};
|
||
},
|
||
|
||
_cacheHelperProportions: function() {
|
||
this.helperProportions = {
|
||
width: this.helper.outerWidth(),
|
||
height: this.helper.outerHeight()
|
||
};
|
||
},
|
||
|
||
_setContainment: function() {
|
||
|
||
var isUserScrollable, c, ce,
|
||
o = this.options,
|
||
document = this.document[ 0 ];
|
||
|
||
this.relativeContainer = null;
|
||
|
||
if ( !o.containment ) {
|
||
this.containment = null;
|
||
return;
|
||
}
|
||
|
||
if ( o.containment === "window" ) {
|
||
this.containment = [
|
||
$( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
|
||
$( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
|
||
$( window ).scrollLeft() + $( window ).width() -
|
||
this.helperProportions.width - this.margins.left,
|
||
$( window ).scrollTop() +
|
||
( $( window ).height() || document.body.parentNode.scrollHeight ) -
|
||
this.helperProportions.height - this.margins.top
|
||
];
|
||
return;
|
||
}
|
||
|
||
if ( o.containment === "document" ) {
|
||
this.containment = [
|
||
0,
|
||
0,
|
||
$( document ).width() - this.helperProportions.width - this.margins.left,
|
||
( $( document ).height() || document.body.parentNode.scrollHeight ) -
|
||
this.helperProportions.height - this.margins.top
|
||
];
|
||
return;
|
||
}
|
||
|
||
if ( o.containment.constructor === Array ) {
|
||
this.containment = o.containment;
|
||
return;
|
||
}
|
||
|
||
if ( o.containment === "parent" ) {
|
||
o.containment = this.helper[ 0 ].parentNode;
|
||
}
|
||
|
||
c = $( o.containment );
|
||
ce = c[ 0 ];
|
||
|
||
if ( !ce ) {
|
||
return;
|
||
}
|
||
|
||
isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
|
||
|
||
this.containment = [
|
||
( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
|
||
( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
|
||
( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
|
||
( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
|
||
( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
|
||
( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
|
||
( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
|
||
this.helperProportions.width -
|
||
this.margins.left -
|
||
this.margins.right,
|
||
( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
|
||
( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
|
||
( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
|
||
this.helperProportions.height -
|
||
this.margins.top -
|
||
this.margins.bottom
|
||
];
|
||
this.relativeContainer = c;
|
||
},
|
||
|
||
_convertPositionTo: function( d, pos ) {
|
||
|
||
if ( !pos ) {
|
||
pos = this.position;
|
||
}
|
||
|
||
var mod = d === "absolute" ? 1 : -1,
|
||
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
|
||
|
||
return {
|
||
top: (
|
||
|
||
// The absolute mouse position
|
||
pos.top +
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.top * mod +
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.top * mod -
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.offset.scroll.top :
|
||
( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
|
||
),
|
||
left: (
|
||
|
||
// The absolute mouse position
|
||
pos.left +
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.left * mod +
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.left * mod -
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.offset.scroll.left :
|
||
( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
|
||
)
|
||
};
|
||
|
||
},
|
||
|
||
_generatePosition: function( event, constrainPosition ) {
|
||
|
||
var containment, co, top, left,
|
||
o = this.options,
|
||
scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
|
||
pageX = event.pageX,
|
||
pageY = event.pageY;
|
||
|
||
// Cache the scroll
|
||
if ( !scrollIsRootNode || !this.offset.scroll ) {
|
||
this.offset.scroll = {
|
||
top: this.scrollParent.scrollTop(),
|
||
left: this.scrollParent.scrollLeft()
|
||
};
|
||
}
|
||
|
||
/*
|
||
* - Position constraining -
|
||
* Constrain the position to a mix of grid, containment.
|
||
*/
|
||
|
||
// If we are not dragging yet, we won't check for options
|
||
if ( constrainPosition ) {
|
||
if ( this.containment ) {
|
||
if ( this.relativeContainer ) {
|
||
co = this.relativeContainer.offset();
|
||
containment = [
|
||
this.containment[ 0 ] + co.left,
|
||
this.containment[ 1 ] + co.top,
|
||
this.containment[ 2 ] + co.left,
|
||
this.containment[ 3 ] + co.top
|
||
];
|
||
} else {
|
||
containment = this.containment;
|
||
}
|
||
|
||
if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
|
||
pageX = containment[ 0 ] + this.offset.click.left;
|
||
}
|
||
if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
|
||
pageY = containment[ 1 ] + this.offset.click.top;
|
||
}
|
||
if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
|
||
pageX = containment[ 2 ] + this.offset.click.left;
|
||
}
|
||
if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
|
||
pageY = containment[ 3 ] + this.offset.click.top;
|
||
}
|
||
}
|
||
|
||
if ( o.grid ) {
|
||
|
||
//Check for grid elements set to 0 to prevent divide by 0 error causing invalid
|
||
// argument errors in IE (see ticket #6950)
|
||
top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
|
||
this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
|
||
pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
|
||
top - this.offset.click.top > containment[ 3 ] ) ?
|
||
top :
|
||
( ( top - this.offset.click.top >= containment[ 1 ] ) ?
|
||
top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
|
||
|
||
left = o.grid[ 0 ] ? this.originalPageX +
|
||
Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
|
||
this.originalPageX;
|
||
pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
|
||
left - this.offset.click.left > containment[ 2 ] ) ?
|
||
left :
|
||
( ( left - this.offset.click.left >= containment[ 0 ] ) ?
|
||
left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
|
||
}
|
||
|
||
if ( o.axis === "y" ) {
|
||
pageX = this.originalPageX;
|
||
}
|
||
|
||
if ( o.axis === "x" ) {
|
||
pageY = this.originalPageY;
|
||
}
|
||
}
|
||
|
||
return {
|
||
top: (
|
||
|
||
// The absolute mouse position
|
||
pageY -
|
||
|
||
// Click offset (relative to the element)
|
||
this.offset.click.top -
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.top -
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.top +
|
||
( this.cssPosition === "fixed" ?
|
||
-this.offset.scroll.top :
|
||
( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
|
||
),
|
||
left: (
|
||
|
||
// The absolute mouse position
|
||
pageX -
|
||
|
||
// Click offset (relative to the element)
|
||
this.offset.click.left -
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.left -
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.left +
|
||
( this.cssPosition === "fixed" ?
|
||
-this.offset.scroll.left :
|
||
( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
|
||
)
|
||
};
|
||
|
||
},
|
||
|
||
_clear: function() {
|
||
this._removeClass( this.helper, "ui-draggable-dragging" );
|
||
if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
|
||
this.helper.remove();
|
||
}
|
||
this.helper = null;
|
||
this.cancelHelperRemoval = false;
|
||
if ( this.destroyOnClear ) {
|
||
this.destroy();
|
||
}
|
||
},
|
||
|
||
// From now on bulk stuff - mainly helpers
|
||
|
||
_trigger: function( type, event, ui ) {
|
||
ui = ui || this._uiHash();
|
||
$.ui.plugin.call( this, type, [ event, ui, this ], true );
|
||
|
||
// Absolute position and offset (see #6884 ) have to be recalculated after plugins
|
||
if ( /^(drag|start|stop)/.test( type ) ) {
|
||
this.positionAbs = this._convertPositionTo( "absolute" );
|
||
ui.offset = this.positionAbs;
|
||
}
|
||
return $.Widget.prototype._trigger.call( this, type, event, ui );
|
||
},
|
||
|
||
plugins: {},
|
||
|
||
_uiHash: function() {
|
||
return {
|
||
helper: this.helper,
|
||
position: this.position,
|
||
originalPosition: this.originalPosition,
|
||
offset: this.positionAbs
|
||
};
|
||
}
|
||
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "connectToSortable", {
|
||
start: function( event, ui, draggable ) {
|
||
var uiSortable = $.extend( {}, ui, {
|
||
item: draggable.element
|
||
} );
|
||
|
||
draggable.sortables = [];
|
||
$( draggable.options.connectToSortable ).each( function() {
|
||
var sortable = $( this ).sortable( "instance" );
|
||
|
||
if ( sortable && !sortable.options.disabled ) {
|
||
draggable.sortables.push( sortable );
|
||
|
||
// RefreshPositions is called at drag start to refresh the containerCache
|
||
// which is used in drag. This ensures it's initialized and synchronized
|
||
// with any changes that might have happened on the page since initialization.
|
||
sortable.refreshPositions();
|
||
sortable._trigger( "activate", event, uiSortable );
|
||
}
|
||
} );
|
||
},
|
||
stop: function( event, ui, draggable ) {
|
||
var uiSortable = $.extend( {}, ui, {
|
||
item: draggable.element
|
||
} );
|
||
|
||
draggable.cancelHelperRemoval = false;
|
||
|
||
$.each( draggable.sortables, function() {
|
||
var sortable = this;
|
||
|
||
if ( sortable.isOver ) {
|
||
sortable.isOver = 0;
|
||
|
||
// Allow this sortable to handle removing the helper
|
||
draggable.cancelHelperRemoval = true;
|
||
sortable.cancelHelperRemoval = false;
|
||
|
||
// Use _storedCSS To restore properties in the sortable,
|
||
// as this also handles revert (#9675) since the draggable
|
||
// may have modified them in unexpected ways (#8809)
|
||
sortable._storedCSS = {
|
||
position: sortable.placeholder.css( "position" ),
|
||
top: sortable.placeholder.css( "top" ),
|
||
left: sortable.placeholder.css( "left" )
|
||
};
|
||
|
||
sortable._mouseStop( event );
|
||
|
||
// Once drag has ended, the sortable should return to using
|
||
// its original helper, not the shared helper from draggable
|
||
sortable.options.helper = sortable.options._helper;
|
||
} else {
|
||
|
||
// Prevent this Sortable from removing the helper.
|
||
// However, don't set the draggable to remove the helper
|
||
// either as another connected Sortable may yet handle the removal.
|
||
sortable.cancelHelperRemoval = true;
|
||
|
||
sortable._trigger( "deactivate", event, uiSortable );
|
||
}
|
||
} );
|
||
},
|
||
drag: function( event, ui, draggable ) {
|
||
$.each( draggable.sortables, function() {
|
||
var innermostIntersecting = false,
|
||
sortable = this;
|
||
|
||
// Copy over variables that sortable's _intersectsWith uses
|
||
sortable.positionAbs = draggable.positionAbs;
|
||
sortable.helperProportions = draggable.helperProportions;
|
||
sortable.offset.click = draggable.offset.click;
|
||
|
||
if ( sortable._intersectsWith( sortable.containerCache ) ) {
|
||
innermostIntersecting = true;
|
||
|
||
$.each( draggable.sortables, function() {
|
||
|
||
// Copy over variables that sortable's _intersectsWith uses
|
||
this.positionAbs = draggable.positionAbs;
|
||
this.helperProportions = draggable.helperProportions;
|
||
this.offset.click = draggable.offset.click;
|
||
|
||
if ( this !== sortable &&
|
||
this._intersectsWith( this.containerCache ) &&
|
||
$.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
|
||
innermostIntersecting = false;
|
||
}
|
||
|
||
return innermostIntersecting;
|
||
} );
|
||
}
|
||
|
||
if ( innermostIntersecting ) {
|
||
|
||
// If it intersects, we use a little isOver variable and set it once,
|
||
// so that the move-in stuff gets fired only once.
|
||
if ( !sortable.isOver ) {
|
||
sortable.isOver = 1;
|
||
|
||
// Store draggable's parent in case we need to reappend to it later.
|
||
draggable._parent = ui.helper.parent();
|
||
|
||
sortable.currentItem = ui.helper
|
||
.appendTo( sortable.element )
|
||
.data( "ui-sortable-item", true );
|
||
|
||
// Store helper option to later restore it
|
||
sortable.options._helper = sortable.options.helper;
|
||
|
||
sortable.options.helper = function() {
|
||
return ui.helper[ 0 ];
|
||
};
|
||
|
||
// Fire the start events of the sortable with our passed browser event,
|
||
// and our own helper (so it doesn't create a new one)
|
||
event.target = sortable.currentItem[ 0 ];
|
||
sortable._mouseCapture( event, true );
|
||
sortable._mouseStart( event, true, true );
|
||
|
||
// Because the browser event is way off the new appended portlet,
|
||
// modify necessary variables to reflect the changes
|
||
sortable.offset.click.top = draggable.offset.click.top;
|
||
sortable.offset.click.left = draggable.offset.click.left;
|
||
sortable.offset.parent.left -= draggable.offset.parent.left -
|
||
sortable.offset.parent.left;
|
||
sortable.offset.parent.top -= draggable.offset.parent.top -
|
||
sortable.offset.parent.top;
|
||
|
||
draggable._trigger( "toSortable", event );
|
||
|
||
// Inform draggable that the helper is in a valid drop zone,
|
||
// used solely in the revert option to handle "valid/invalid".
|
||
draggable.dropped = sortable.element;
|
||
|
||
// Need to refreshPositions of all sortables in the case that
|
||
// adding to one sortable changes the location of the other sortables (#9675)
|
||
$.each( draggable.sortables, function() {
|
||
this.refreshPositions();
|
||
} );
|
||
|
||
// Hack so receive/update callbacks work (mostly)
|
||
draggable.currentItem = draggable.element;
|
||
sortable.fromOutside = draggable;
|
||
}
|
||
|
||
if ( sortable.currentItem ) {
|
||
sortable._mouseDrag( event );
|
||
|
||
// Copy the sortable's position because the draggable's can potentially reflect
|
||
// a relative position, while sortable is always absolute, which the dragged
|
||
// element has now become. (#8809)
|
||
ui.position = sortable.position;
|
||
}
|
||
} else {
|
||
|
||
// If it doesn't intersect with the sortable, and it intersected before,
|
||
// we fake the drag stop of the sortable, but make sure it doesn't remove
|
||
// the helper by using cancelHelperRemoval.
|
||
if ( sortable.isOver ) {
|
||
|
||
sortable.isOver = 0;
|
||
sortable.cancelHelperRemoval = true;
|
||
|
||
// Calling sortable's mouseStop would trigger a revert,
|
||
// so revert must be temporarily false until after mouseStop is called.
|
||
sortable.options._revert = sortable.options.revert;
|
||
sortable.options.revert = false;
|
||
|
||
sortable._trigger( "out", event, sortable._uiHash( sortable ) );
|
||
sortable._mouseStop( event, true );
|
||
|
||
// Restore sortable behaviors that were modfied
|
||
// when the draggable entered the sortable area (#9481)
|
||
sortable.options.revert = sortable.options._revert;
|
||
sortable.options.helper = sortable.options._helper;
|
||
|
||
if ( sortable.placeholder ) {
|
||
sortable.placeholder.remove();
|
||
}
|
||
|
||
// Restore and recalculate the draggable's offset considering the sortable
|
||
// may have modified them in unexpected ways. (#8809, #10669)
|
||
ui.helper.appendTo( draggable._parent );
|
||
draggable._refreshOffsets( event );
|
||
ui.position = draggable._generatePosition( event, true );
|
||
|
||
draggable._trigger( "fromSortable", event );
|
||
|
||
// Inform draggable that the helper is no longer in a valid drop zone
|
||
draggable.dropped = false;
|
||
|
||
// Need to refreshPositions of all sortables just in case removing
|
||
// from one sortable changes the location of other sortables (#9675)
|
||
$.each( draggable.sortables, function() {
|
||
this.refreshPositions();
|
||
} );
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "cursor", {
|
||
start: function( event, ui, instance ) {
|
||
var t = $( "body" ),
|
||
o = instance.options;
|
||
|
||
if ( t.css( "cursor" ) ) {
|
||
o._cursor = t.css( "cursor" );
|
||
}
|
||
t.css( "cursor", o.cursor );
|
||
},
|
||
stop: function( event, ui, instance ) {
|
||
var o = instance.options;
|
||
if ( o._cursor ) {
|
||
$( "body" ).css( "cursor", o._cursor );
|
||
}
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "opacity", {
|
||
start: function( event, ui, instance ) {
|
||
var t = $( ui.helper ),
|
||
o = instance.options;
|
||
if ( t.css( "opacity" ) ) {
|
||
o._opacity = t.css( "opacity" );
|
||
}
|
||
t.css( "opacity", o.opacity );
|
||
},
|
||
stop: function( event, ui, instance ) {
|
||
var o = instance.options;
|
||
if ( o._opacity ) {
|
||
$( ui.helper ).css( "opacity", o._opacity );
|
||
}
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "scroll", {
|
||
start: function( event, ui, i ) {
|
||
if ( !i.scrollParentNotHidden ) {
|
||
i.scrollParentNotHidden = i.helper.scrollParent( false );
|
||
}
|
||
|
||
if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
|
||
i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
|
||
i.overflowOffset = i.scrollParentNotHidden.offset();
|
||
}
|
||
},
|
||
drag: function( event, ui, i ) {
|
||
|
||
var o = i.options,
|
||
scrolled = false,
|
||
scrollParent = i.scrollParentNotHidden[ 0 ],
|
||
document = i.document[ 0 ];
|
||
|
||
if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
|
||
if ( !o.axis || o.axis !== "x" ) {
|
||
if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
|
||
o.scrollSensitivity ) {
|
||
scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
|
||
} else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
|
||
scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
|
||
}
|
||
}
|
||
|
||
if ( !o.axis || o.axis !== "y" ) {
|
||
if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
|
||
o.scrollSensitivity ) {
|
||
scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
|
||
} else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
|
||
scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( !o.axis || o.axis !== "x" ) {
|
||
if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
|
||
scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
|
||
} else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
|
||
o.scrollSensitivity ) {
|
||
scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
|
||
}
|
||
}
|
||
|
||
if ( !o.axis || o.axis !== "y" ) {
|
||
if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
|
||
scrolled = $( document ).scrollLeft(
|
||
$( document ).scrollLeft() - o.scrollSpeed
|
||
);
|
||
} else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
|
||
o.scrollSensitivity ) {
|
||
scrolled = $( document ).scrollLeft(
|
||
$( document ).scrollLeft() + o.scrollSpeed
|
||
);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
|
||
$.ui.ddmanager.prepareOffsets( i, event );
|
||
}
|
||
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "snap", {
|
||
start: function( event, ui, i ) {
|
||
|
||
var o = i.options;
|
||
|
||
i.snapElements = [];
|
||
|
||
$( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
|
||
.each( function() {
|
||
var $t = $( this ),
|
||
$o = $t.offset();
|
||
if ( this !== i.element[ 0 ] ) {
|
||
i.snapElements.push( {
|
||
item: this,
|
||
width: $t.outerWidth(), height: $t.outerHeight(),
|
||
top: $o.top, left: $o.left
|
||
} );
|
||
}
|
||
} );
|
||
|
||
},
|
||
drag: function( event, ui, inst ) {
|
||
|
||
var ts, bs, ls, rs, l, r, t, b, i, first,
|
||
o = inst.options,
|
||
d = o.snapTolerance,
|
||
x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
|
||
y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
|
||
|
||
for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
|
||
|
||
l = inst.snapElements[ i ].left - inst.margins.left;
|
||
r = l + inst.snapElements[ i ].width;
|
||
t = inst.snapElements[ i ].top - inst.margins.top;
|
||
b = t + inst.snapElements[ i ].height;
|
||
|
||
if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
|
||
!$.contains( inst.snapElements[ i ].item.ownerDocument,
|
||
inst.snapElements[ i ].item ) ) {
|
||
if ( inst.snapElements[ i ].snapping ) {
|
||
( inst.options.snap.release &&
|
||
inst.options.snap.release.call(
|
||
inst.element,
|
||
event,
|
||
$.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
|
||
) );
|
||
}
|
||
inst.snapElements[ i ].snapping = false;
|
||
continue;
|
||
}
|
||
|
||
if ( o.snapMode !== "inner" ) {
|
||
ts = Math.abs( t - y2 ) <= d;
|
||
bs = Math.abs( b - y1 ) <= d;
|
||
ls = Math.abs( l - x2 ) <= d;
|
||
rs = Math.abs( r - x1 ) <= d;
|
||
if ( ts ) {
|
||
ui.position.top = inst._convertPositionTo( "relative", {
|
||
top: t - inst.helperProportions.height,
|
||
left: 0
|
||
} ).top;
|
||
}
|
||
if ( bs ) {
|
||
ui.position.top = inst._convertPositionTo( "relative", {
|
||
top: b,
|
||
left: 0
|
||
} ).top;
|
||
}
|
||
if ( ls ) {
|
||
ui.position.left = inst._convertPositionTo( "relative", {
|
||
top: 0,
|
||
left: l - inst.helperProportions.width
|
||
} ).left;
|
||
}
|
||
if ( rs ) {
|
||
ui.position.left = inst._convertPositionTo( "relative", {
|
||
top: 0,
|
||
left: r
|
||
} ).left;
|
||
}
|
||
}
|
||
|
||
first = ( ts || bs || ls || rs );
|
||
|
||
if ( o.snapMode !== "outer" ) {
|
||
ts = Math.abs( t - y1 ) <= d;
|
||
bs = Math.abs( b - y2 ) <= d;
|
||
ls = Math.abs( l - x1 ) <= d;
|
||
rs = Math.abs( r - x2 ) <= d;
|
||
if ( ts ) {
|
||
ui.position.top = inst._convertPositionTo( "relative", {
|
||
top: t,
|
||
left: 0
|
||
} ).top;
|
||
}
|
||
if ( bs ) {
|
||
ui.position.top = inst._convertPositionTo( "relative", {
|
||
top: b - inst.helperProportions.height,
|
||
left: 0
|
||
} ).top;
|
||
}
|
||
if ( ls ) {
|
||
ui.position.left = inst._convertPositionTo( "relative", {
|
||
top: 0,
|
||
left: l
|
||
} ).left;
|
||
}
|
||
if ( rs ) {
|
||
ui.position.left = inst._convertPositionTo( "relative", {
|
||
top: 0,
|
||
left: r - inst.helperProportions.width
|
||
} ).left;
|
||
}
|
||
}
|
||
|
||
if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
|
||
( inst.options.snap.snap &&
|
||
inst.options.snap.snap.call(
|
||
inst.element,
|
||
event,
|
||
$.extend( inst._uiHash(), {
|
||
snapItem: inst.snapElements[ i ].item
|
||
} ) ) );
|
||
}
|
||
inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
|
||
|
||
}
|
||
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "stack", {
|
||
start: function( event, ui, instance ) {
|
||
var min,
|
||
o = instance.options,
|
||
group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
|
||
return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
|
||
( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
|
||
} );
|
||
|
||
if ( !group.length ) { return; }
|
||
|
||
min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
|
||
$( group ).each( function( i ) {
|
||
$( this ).css( "zIndex", min + i );
|
||
} );
|
||
this.css( "zIndex", ( min + group.length ) );
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "draggable", "zIndex", {
|
||
start: function( event, ui, instance ) {
|
||
var t = $( ui.helper ),
|
||
o = instance.options;
|
||
|
||
if ( t.css( "zIndex" ) ) {
|
||
o._zIndex = t.css( "zIndex" );
|
||
}
|
||
t.css( "zIndex", o.zIndex );
|
||
},
|
||
stop: function( event, ui, instance ) {
|
||
var o = instance.options;
|
||
|
||
if ( o._zIndex ) {
|
||
$( ui.helper ).css( "zIndex", o._zIndex );
|
||
}
|
||
}
|
||
} );
|
||
|
||
var widgetsDraggable = $.ui.draggable;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Droppable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Droppable
|
||
//>>group: Interactions
|
||
//>>description: Enables drop targets for draggable elements.
|
||
//>>docs: http://api.jqueryui.com/droppable/
|
||
//>>demos: http://jqueryui.com/droppable/
|
||
|
||
|
||
|
||
$.widget( "ui.droppable", {
|
||
version: "1.12.1",
|
||
widgetEventPrefix: "drop",
|
||
options: {
|
||
accept: "*",
|
||
addClasses: true,
|
||
greedy: false,
|
||
scope: "default",
|
||
tolerance: "intersect",
|
||
|
||
// Callbacks
|
||
activate: null,
|
||
deactivate: null,
|
||
drop: null,
|
||
out: null,
|
||
over: null
|
||
},
|
||
_create: function() {
|
||
|
||
var proportions,
|
||
o = this.options,
|
||
accept = o.accept;
|
||
|
||
this.isover = false;
|
||
this.isout = true;
|
||
|
||
this.accept = $.isFunction( accept ) ? accept : function( d ) {
|
||
return d.is( accept );
|
||
};
|
||
|
||
this.proportions = function( /* valueToWrite */ ) {
|
||
if ( arguments.length ) {
|
||
|
||
// Store the droppable's proportions
|
||
proportions = arguments[ 0 ];
|
||
} else {
|
||
|
||
// Retrieve or derive the droppable's proportions
|
||
return proportions ?
|
||
proportions :
|
||
proportions = {
|
||
width: this.element[ 0 ].offsetWidth,
|
||
height: this.element[ 0 ].offsetHeight
|
||
};
|
||
}
|
||
};
|
||
|
||
this._addToManager( o.scope );
|
||
|
||
o.addClasses && this._addClass( "ui-droppable" );
|
||
|
||
},
|
||
|
||
_addToManager: function( scope ) {
|
||
|
||
// Add the reference and positions to the manager
|
||
$.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
|
||
$.ui.ddmanager.droppables[ scope ].push( this );
|
||
},
|
||
|
||
_splice: function( drop ) {
|
||
var i = 0;
|
||
for ( ; i < drop.length; i++ ) {
|
||
if ( drop[ i ] === this ) {
|
||
drop.splice( i, 1 );
|
||
}
|
||
}
|
||
},
|
||
|
||
_destroy: function() {
|
||
var drop = $.ui.ddmanager.droppables[ this.options.scope ];
|
||
|
||
this._splice( drop );
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
|
||
if ( key === "accept" ) {
|
||
this.accept = $.isFunction( value ) ? value : function( d ) {
|
||
return d.is( value );
|
||
};
|
||
} else if ( key === "scope" ) {
|
||
var drop = $.ui.ddmanager.droppables[ this.options.scope ];
|
||
|
||
this._splice( drop );
|
||
this._addToManager( value );
|
||
}
|
||
|
||
this._super( key, value );
|
||
},
|
||
|
||
_activate: function( event ) {
|
||
var draggable = $.ui.ddmanager.current;
|
||
|
||
this._addActiveClass();
|
||
if ( draggable ) {
|
||
this._trigger( "activate", event, this.ui( draggable ) );
|
||
}
|
||
},
|
||
|
||
_deactivate: function( event ) {
|
||
var draggable = $.ui.ddmanager.current;
|
||
|
||
this._removeActiveClass();
|
||
if ( draggable ) {
|
||
this._trigger( "deactivate", event, this.ui( draggable ) );
|
||
}
|
||
},
|
||
|
||
_over: function( event ) {
|
||
|
||
var draggable = $.ui.ddmanager.current;
|
||
|
||
// Bail if draggable and droppable are same element
|
||
if ( !draggable || ( draggable.currentItem ||
|
||
draggable.element )[ 0 ] === this.element[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
|
||
draggable.element ) ) ) {
|
||
this._addHoverClass();
|
||
this._trigger( "over", event, this.ui( draggable ) );
|
||
}
|
||
|
||
},
|
||
|
||
_out: function( event ) {
|
||
|
||
var draggable = $.ui.ddmanager.current;
|
||
|
||
// Bail if draggable and droppable are same element
|
||
if ( !draggable || ( draggable.currentItem ||
|
||
draggable.element )[ 0 ] === this.element[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem ||
|
||
draggable.element ) ) ) {
|
||
this._removeHoverClass();
|
||
this._trigger( "out", event, this.ui( draggable ) );
|
||
}
|
||
|
||
},
|
||
|
||
_drop: function( event, custom ) {
|
||
|
||
var draggable = custom || $.ui.ddmanager.current,
|
||
childrenIntersection = false;
|
||
|
||
// Bail if draggable and droppable are same element
|
||
if ( !draggable || ( draggable.currentItem ||
|
||
draggable.element )[ 0 ] === this.element[ 0 ] ) {
|
||
return false;
|
||
}
|
||
|
||
this.element
|
||
.find( ":data(ui-droppable)" )
|
||
.not( ".ui-draggable-dragging" )
|
||
.each( function() {
|
||
var inst = $( this ).droppable( "instance" );
|
||
if (
|
||
inst.options.greedy &&
|
||
!inst.options.disabled &&
|
||
inst.options.scope === draggable.options.scope &&
|
||
inst.accept.call(
|
||
inst.element[ 0 ], ( draggable.currentItem || draggable.element )
|
||
) &&
|
||
intersect(
|
||
draggable,
|
||
$.extend( inst, { offset: inst.element.offset() } ),
|
||
inst.options.tolerance, event
|
||
)
|
||
) {
|
||
childrenIntersection = true;
|
||
return false; }
|
||
} );
|
||
if ( childrenIntersection ) {
|
||
return false;
|
||
}
|
||
|
||
if ( this.accept.call( this.element[ 0 ],
|
||
( draggable.currentItem || draggable.element ) ) ) {
|
||
this._removeActiveClass();
|
||
this._removeHoverClass();
|
||
|
||
this._trigger( "drop", event, this.ui( draggable ) );
|
||
return this.element;
|
||
}
|
||
|
||
return false;
|
||
|
||
},
|
||
|
||
ui: function( c ) {
|
||
return {
|
||
draggable: ( c.currentItem || c.element ),
|
||
helper: c.helper,
|
||
position: c.position,
|
||
offset: c.positionAbs
|
||
};
|
||
},
|
||
|
||
// Extension points just to make backcompat sane and avoid duplicating logic
|
||
// TODO: Remove in 1.13 along with call to it below
|
||
_addHoverClass: function() {
|
||
this._addClass( "ui-droppable-hover" );
|
||
},
|
||
|
||
_removeHoverClass: function() {
|
||
this._removeClass( "ui-droppable-hover" );
|
||
},
|
||
|
||
_addActiveClass: function() {
|
||
this._addClass( "ui-droppable-active" );
|
||
},
|
||
|
||
_removeActiveClass: function() {
|
||
this._removeClass( "ui-droppable-active" );
|
||
}
|
||
} );
|
||
|
||
var intersect = $.ui.intersect = ( function() {
|
||
function isOverAxis( x, reference, size ) {
|
||
return ( x >= reference ) && ( x < ( reference + size ) );
|
||
}
|
||
|
||
return function( draggable, droppable, toleranceMode, event ) {
|
||
|
||
if ( !droppable.offset ) {
|
||
return false;
|
||
}
|
||
|
||
var x1 = ( draggable.positionAbs ||
|
||
draggable.position.absolute ).left + draggable.margins.left,
|
||
y1 = ( draggable.positionAbs ||
|
||
draggable.position.absolute ).top + draggable.margins.top,
|
||
x2 = x1 + draggable.helperProportions.width,
|
||
y2 = y1 + draggable.helperProportions.height,
|
||
l = droppable.offset.left,
|
||
t = droppable.offset.top,
|
||
r = l + droppable.proportions().width,
|
||
b = t + droppable.proportions().height;
|
||
|
||
switch ( toleranceMode ) {
|
||
case "fit":
|
||
return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
|
||
case "intersect":
|
||
return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
|
||
x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
|
||
t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
|
||
y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
|
||
case "pointer":
|
||
return isOverAxis( event.pageY, t, droppable.proportions().height ) &&
|
||
isOverAxis( event.pageX, l, droppable.proportions().width );
|
||
case "touch":
|
||
return (
|
||
( y1 >= t && y1 <= b ) || // Top edge touching
|
||
( y2 >= t && y2 <= b ) || // Bottom edge touching
|
||
( y1 < t && y2 > b ) // Surrounded vertically
|
||
) && (
|
||
( x1 >= l && x1 <= r ) || // Left edge touching
|
||
( x2 >= l && x2 <= r ) || // Right edge touching
|
||
( x1 < l && x2 > r ) // Surrounded horizontally
|
||
);
|
||
default:
|
||
return false;
|
||
}
|
||
};
|
||
} )();
|
||
|
||
/*
|
||
This manager tracks offsets of draggables and droppables
|
||
*/
|
||
$.ui.ddmanager = {
|
||
current: null,
|
||
droppables: { "default": [] },
|
||
prepareOffsets: function( t, event ) {
|
||
|
||
var i, j,
|
||
m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
|
||
type = event ? event.type : null, // workaround for #2317
|
||
list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
|
||
|
||
droppablesLoop: for ( i = 0; i < m.length; i++ ) {
|
||
|
||
// No disabled and non-accepted
|
||
if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ],
|
||
( t.currentItem || t.element ) ) ) ) {
|
||
continue;
|
||
}
|
||
|
||
// Filter out elements in the current dragged item
|
||
for ( j = 0; j < list.length; j++ ) {
|
||
if ( list[ j ] === m[ i ].element[ 0 ] ) {
|
||
m[ i ].proportions().height = 0;
|
||
continue droppablesLoop;
|
||
}
|
||
}
|
||
|
||
m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
|
||
if ( !m[ i ].visible ) {
|
||
continue;
|
||
}
|
||
|
||
// Activate the droppable if used directly from draggables
|
||
if ( type === "mousedown" ) {
|
||
m[ i ]._activate.call( m[ i ], event );
|
||
}
|
||
|
||
m[ i ].offset = m[ i ].element.offset();
|
||
m[ i ].proportions( {
|
||
width: m[ i ].element[ 0 ].offsetWidth,
|
||
height: m[ i ].element[ 0 ].offsetHeight
|
||
} );
|
||
|
||
}
|
||
|
||
},
|
||
drop: function( draggable, event ) {
|
||
|
||
var dropped = false;
|
||
|
||
// Create a copy of the droppables in case the list changes during the drop (#9116)
|
||
$.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
|
||
|
||
if ( !this.options ) {
|
||
return;
|
||
}
|
||
if ( !this.options.disabled && this.visible &&
|
||
intersect( draggable, this, this.options.tolerance, event ) ) {
|
||
dropped = this._drop.call( this, event ) || dropped;
|
||
}
|
||
|
||
if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ],
|
||
( draggable.currentItem || draggable.element ) ) ) {
|
||
this.isout = true;
|
||
this.isover = false;
|
||
this._deactivate.call( this, event );
|
||
}
|
||
|
||
} );
|
||
return dropped;
|
||
|
||
},
|
||
dragStart: function( draggable, event ) {
|
||
|
||
// Listen for scrolling so that if the dragging causes scrolling the position of the
|
||
// droppables can be recalculated (see #5003)
|
||
draggable.element.parentsUntil( "body" ).on( "scroll.droppable", function() {
|
||
if ( !draggable.options.refreshPositions ) {
|
||
$.ui.ddmanager.prepareOffsets( draggable, event );
|
||
}
|
||
} );
|
||
},
|
||
drag: function( draggable, event ) {
|
||
|
||
// If you have a highly dynamic page, you might try this option. It renders positions
|
||
// every time you move the mouse.
|
||
if ( draggable.options.refreshPositions ) {
|
||
$.ui.ddmanager.prepareOffsets( draggable, event );
|
||
}
|
||
|
||
// Run through all droppables and check their positions based on specific tolerance options
|
||
$.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
|
||
|
||
if ( this.options.disabled || this.greedyChild || !this.visible ) {
|
||
return;
|
||
}
|
||
|
||
var parentInstance, scope, parent,
|
||
intersects = intersect( draggable, this, this.options.tolerance, event ),
|
||
c = !intersects && this.isover ?
|
||
"isout" :
|
||
( intersects && !this.isover ? "isover" : null );
|
||
if ( !c ) {
|
||
return;
|
||
}
|
||
|
||
if ( this.options.greedy ) {
|
||
|
||
// find droppable parents with same scope
|
||
scope = this.options.scope;
|
||
parent = this.element.parents( ":data(ui-droppable)" ).filter( function() {
|
||
return $( this ).droppable( "instance" ).options.scope === scope;
|
||
} );
|
||
|
||
if ( parent.length ) {
|
||
parentInstance = $( parent[ 0 ] ).droppable( "instance" );
|
||
parentInstance.greedyChild = ( c === "isover" );
|
||
}
|
||
}
|
||
|
||
// We just moved into a greedy child
|
||
if ( parentInstance && c === "isover" ) {
|
||
parentInstance.isover = false;
|
||
parentInstance.isout = true;
|
||
parentInstance._out.call( parentInstance, event );
|
||
}
|
||
|
||
this[ c ] = true;
|
||
this[ c === "isout" ? "isover" : "isout" ] = false;
|
||
this[ c === "isover" ? "_over" : "_out" ].call( this, event );
|
||
|
||
// We just moved out of a greedy child
|
||
if ( parentInstance && c === "isout" ) {
|
||
parentInstance.isout = false;
|
||
parentInstance.isover = true;
|
||
parentInstance._over.call( parentInstance, event );
|
||
}
|
||
} );
|
||
|
||
},
|
||
dragStop: function( draggable, event ) {
|
||
draggable.element.parentsUntil( "body" ).off( "scroll.droppable" );
|
||
|
||
// Call prepareOffsets one final time since IE does not fire return scroll events when
|
||
// overflow was caused by drag (see #5003)
|
||
if ( !draggable.options.refreshPositions ) {
|
||
$.ui.ddmanager.prepareOffsets( draggable, event );
|
||
}
|
||
}
|
||
};
|
||
|
||
// DEPRECATED
|
||
// TODO: switch return back to widget declaration at top of file when this is removed
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Backcompat for activeClass and hoverClass options
|
||
$.widget( "ui.droppable", $.ui.droppable, {
|
||
options: {
|
||
hoverClass: false,
|
||
activeClass: false
|
||
},
|
||
_addActiveClass: function() {
|
||
this._super();
|
||
if ( this.options.activeClass ) {
|
||
this.element.addClass( this.options.activeClass );
|
||
}
|
||
},
|
||
_removeActiveClass: function() {
|
||
this._super();
|
||
if ( this.options.activeClass ) {
|
||
this.element.removeClass( this.options.activeClass );
|
||
}
|
||
},
|
||
_addHoverClass: function() {
|
||
this._super();
|
||
if ( this.options.hoverClass ) {
|
||
this.element.addClass( this.options.hoverClass );
|
||
}
|
||
},
|
||
_removeHoverClass: function() {
|
||
this._super();
|
||
if ( this.options.hoverClass ) {
|
||
this.element.removeClass( this.options.hoverClass );
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
|
||
var widgetsDroppable = $.ui.droppable;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Resizable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Resizable
|
||
//>>group: Interactions
|
||
//>>description: Enables resize functionality for any element.
|
||
//>>docs: http://api.jqueryui.com/resizable/
|
||
//>>demos: http://jqueryui.com/resizable/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/resizable.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.resizable", $.ui.mouse, {
|
||
version: "1.12.1",
|
||
widgetEventPrefix: "resize",
|
||
options: {
|
||
alsoResize: false,
|
||
animate: false,
|
||
animateDuration: "slow",
|
||
animateEasing: "swing",
|
||
aspectRatio: false,
|
||
autoHide: false,
|
||
classes: {
|
||
"ui-resizable-se": "ui-icon ui-icon-gripsmall-diagonal-se"
|
||
},
|
||
containment: false,
|
||
ghost: false,
|
||
grid: false,
|
||
handles: "e,s,se",
|
||
helper: false,
|
||
maxHeight: null,
|
||
maxWidth: null,
|
||
minHeight: 10,
|
||
minWidth: 10,
|
||
|
||
// See #7960
|
||
zIndex: 90,
|
||
|
||
// Callbacks
|
||
resize: null,
|
||
start: null,
|
||
stop: null
|
||
},
|
||
|
||
_num: function( value ) {
|
||
return parseFloat( value ) || 0;
|
||
},
|
||
|
||
_isNumber: function( value ) {
|
||
return !isNaN( parseFloat( value ) );
|
||
},
|
||
|
||
_hasScroll: function( el, a ) {
|
||
|
||
if ( $( el ).css( "overflow" ) === "hidden" ) {
|
||
return false;
|
||
}
|
||
|
||
var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
|
||
has = false;
|
||
|
||
if ( el[ scroll ] > 0 ) {
|
||
return true;
|
||
}
|
||
|
||
// TODO: determine which cases actually cause this to happen
|
||
// if the element doesn't have the scroll set, see if it's possible to
|
||
// set the scroll
|
||
el[ scroll ] = 1;
|
||
has = ( el[ scroll ] > 0 );
|
||
el[ scroll ] = 0;
|
||
return has;
|
||
},
|
||
|
||
_create: function() {
|
||
|
||
var margins,
|
||
o = this.options,
|
||
that = this;
|
||
this._addClass( "ui-resizable" );
|
||
|
||
$.extend( this, {
|
||
_aspectRatio: !!( o.aspectRatio ),
|
||
aspectRatio: o.aspectRatio,
|
||
originalElement: this.element,
|
||
_proportionallyResizeElements: [],
|
||
_helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
|
||
} );
|
||
|
||
// Wrap the element if it cannot hold child nodes
|
||
if ( this.element[ 0 ].nodeName.match( /^(canvas|textarea|input|select|button|img)$/i ) ) {
|
||
|
||
this.element.wrap(
|
||
$( "<div class='ui-wrapper' style='overflow: hidden;'></div>" ).css( {
|
||
position: this.element.css( "position" ),
|
||
width: this.element.outerWidth(),
|
||
height: this.element.outerHeight(),
|
||
top: this.element.css( "top" ),
|
||
left: this.element.css( "left" )
|
||
} )
|
||
);
|
||
|
||
this.element = this.element.parent().data(
|
||
"ui-resizable", this.element.resizable( "instance" )
|
||
);
|
||
|
||
this.elementIsWrapper = true;
|
||
|
||
margins = {
|
||
marginTop: this.originalElement.css( "marginTop" ),
|
||
marginRight: this.originalElement.css( "marginRight" ),
|
||
marginBottom: this.originalElement.css( "marginBottom" ),
|
||
marginLeft: this.originalElement.css( "marginLeft" )
|
||
};
|
||
|
||
this.element.css( margins );
|
||
this.originalElement.css( "margin", 0 );
|
||
|
||
// support: Safari
|
||
// Prevent Safari textarea resize
|
||
this.originalResizeStyle = this.originalElement.css( "resize" );
|
||
this.originalElement.css( "resize", "none" );
|
||
|
||
this._proportionallyResizeElements.push( this.originalElement.css( {
|
||
position: "static",
|
||
zoom: 1,
|
||
display: "block"
|
||
} ) );
|
||
|
||
// Support: IE9
|
||
// avoid IE jump (hard set the margin)
|
||
this.originalElement.css( margins );
|
||
|
||
this._proportionallyResize();
|
||
}
|
||
|
||
this._setupHandles();
|
||
|
||
if ( o.autoHide ) {
|
||
$( this.element )
|
||
.on( "mouseenter", function() {
|
||
if ( o.disabled ) {
|
||
return;
|
||
}
|
||
that._removeClass( "ui-resizable-autohide" );
|
||
that._handles.show();
|
||
} )
|
||
.on( "mouseleave", function() {
|
||
if ( o.disabled ) {
|
||
return;
|
||
}
|
||
if ( !that.resizing ) {
|
||
that._addClass( "ui-resizable-autohide" );
|
||
that._handles.hide();
|
||
}
|
||
} );
|
||
}
|
||
|
||
this._mouseInit();
|
||
},
|
||
|
||
_destroy: function() {
|
||
|
||
this._mouseDestroy();
|
||
|
||
var wrapper,
|
||
_destroy = function( exp ) {
|
||
$( exp )
|
||
.removeData( "resizable" )
|
||
.removeData( "ui-resizable" )
|
||
.off( ".resizable" )
|
||
.find( ".ui-resizable-handle" )
|
||
.remove();
|
||
};
|
||
|
||
// TODO: Unwrap at same DOM position
|
||
if ( this.elementIsWrapper ) {
|
||
_destroy( this.element );
|
||
wrapper = this.element;
|
||
this.originalElement.css( {
|
||
position: wrapper.css( "position" ),
|
||
width: wrapper.outerWidth(),
|
||
height: wrapper.outerHeight(),
|
||
top: wrapper.css( "top" ),
|
||
left: wrapper.css( "left" )
|
||
} ).insertAfter( wrapper );
|
||
wrapper.remove();
|
||
}
|
||
|
||
this.originalElement.css( "resize", this.originalResizeStyle );
|
||
_destroy( this.originalElement );
|
||
|
||
return this;
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
this._super( key, value );
|
||
|
||
switch ( key ) {
|
||
case "handles":
|
||
this._removeHandles();
|
||
this._setupHandles();
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
},
|
||
|
||
_setupHandles: function() {
|
||
var o = this.options, handle, i, n, hname, axis, that = this;
|
||
this.handles = o.handles ||
|
||
( !$( ".ui-resizable-handle", this.element ).length ?
|
||
"e,s,se" : {
|
||
n: ".ui-resizable-n",
|
||
e: ".ui-resizable-e",
|
||
s: ".ui-resizable-s",
|
||
w: ".ui-resizable-w",
|
||
se: ".ui-resizable-se",
|
||
sw: ".ui-resizable-sw",
|
||
ne: ".ui-resizable-ne",
|
||
nw: ".ui-resizable-nw"
|
||
} );
|
||
|
||
this._handles = $();
|
||
if ( this.handles.constructor === String ) {
|
||
|
||
if ( this.handles === "all" ) {
|
||
this.handles = "n,e,s,w,se,sw,ne,nw";
|
||
}
|
||
|
||
n = this.handles.split( "," );
|
||
this.handles = {};
|
||
|
||
for ( i = 0; i < n.length; i++ ) {
|
||
|
||
handle = $.trim( n[ i ] );
|
||
hname = "ui-resizable-" + handle;
|
||
axis = $( "<div>" );
|
||
this._addClass( axis, "ui-resizable-handle " + hname );
|
||
|
||
axis.css( { zIndex: o.zIndex } );
|
||
|
||
this.handles[ handle ] = ".ui-resizable-" + handle;
|
||
this.element.append( axis );
|
||
}
|
||
|
||
}
|
||
|
||
this._renderAxis = function( target ) {
|
||
|
||
var i, axis, padPos, padWrapper;
|
||
|
||
target = target || this.element;
|
||
|
||
for ( i in this.handles ) {
|
||
|
||
if ( this.handles[ i ].constructor === String ) {
|
||
this.handles[ i ] = this.element.children( this.handles[ i ] ).first().show();
|
||
} else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
|
||
this.handles[ i ] = $( this.handles[ i ] );
|
||
this._on( this.handles[ i ], { "mousedown": that._mouseDown } );
|
||
}
|
||
|
||
if ( this.elementIsWrapper &&
|
||
this.originalElement[ 0 ]
|
||
.nodeName
|
||
.match( /^(textarea|input|select|button)$/i ) ) {
|
||
axis = $( this.handles[ i ], this.element );
|
||
|
||
padWrapper = /sw|ne|nw|se|n|s/.test( i ) ?
|
||
axis.outerHeight() :
|
||
axis.outerWidth();
|
||
|
||
padPos = [ "padding",
|
||
/ne|nw|n/.test( i ) ? "Top" :
|
||
/se|sw|s/.test( i ) ? "Bottom" :
|
||
/^e$/.test( i ) ? "Right" : "Left" ].join( "" );
|
||
|
||
target.css( padPos, padWrapper );
|
||
|
||
this._proportionallyResize();
|
||
}
|
||
|
||
this._handles = this._handles.add( this.handles[ i ] );
|
||
}
|
||
};
|
||
|
||
// TODO: make renderAxis a prototype function
|
||
this._renderAxis( this.element );
|
||
|
||
this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
|
||
this._handles.disableSelection();
|
||
|
||
this._handles.on( "mouseover", function() {
|
||
if ( !that.resizing ) {
|
||
if ( this.className ) {
|
||
axis = this.className.match( /ui-resizable-(se|sw|ne|nw|n|e|s|w)/i );
|
||
}
|
||
that.axis = axis && axis[ 1 ] ? axis[ 1 ] : "se";
|
||
}
|
||
} );
|
||
|
||
if ( o.autoHide ) {
|
||
this._handles.hide();
|
||
this._addClass( "ui-resizable-autohide" );
|
||
}
|
||
},
|
||
|
||
_removeHandles: function() {
|
||
this._handles.remove();
|
||
},
|
||
|
||
_mouseCapture: function( event ) {
|
||
var i, handle,
|
||
capture = false;
|
||
|
||
for ( i in this.handles ) {
|
||
handle = $( this.handles[ i ] )[ 0 ];
|
||
if ( handle === event.target || $.contains( handle, event.target ) ) {
|
||
capture = true;
|
||
}
|
||
}
|
||
|
||
return !this.options.disabled && capture;
|
||
},
|
||
|
||
_mouseStart: function( event ) {
|
||
|
||
var curleft, curtop, cursor,
|
||
o = this.options,
|
||
el = this.element;
|
||
|
||
this.resizing = true;
|
||
|
||
this._renderProxy();
|
||
|
||
curleft = this._num( this.helper.css( "left" ) );
|
||
curtop = this._num( this.helper.css( "top" ) );
|
||
|
||
if ( o.containment ) {
|
||
curleft += $( o.containment ).scrollLeft() || 0;
|
||
curtop += $( o.containment ).scrollTop() || 0;
|
||
}
|
||
|
||
this.offset = this.helper.offset();
|
||
this.position = { left: curleft, top: curtop };
|
||
|
||
this.size = this._helper ? {
|
||
width: this.helper.width(),
|
||
height: this.helper.height()
|
||
} : {
|
||
width: el.width(),
|
||
height: el.height()
|
||
};
|
||
|
||
this.originalSize = this._helper ? {
|
||
width: el.outerWidth(),
|
||
height: el.outerHeight()
|
||
} : {
|
||
width: el.width(),
|
||
height: el.height()
|
||
};
|
||
|
||
this.sizeDiff = {
|
||
width: el.outerWidth() - el.width(),
|
||
height: el.outerHeight() - el.height()
|
||
};
|
||
|
||
this.originalPosition = { left: curleft, top: curtop };
|
||
this.originalMousePosition = { left: event.pageX, top: event.pageY };
|
||
|
||
this.aspectRatio = ( typeof o.aspectRatio === "number" ) ?
|
||
o.aspectRatio :
|
||
( ( this.originalSize.width / this.originalSize.height ) || 1 );
|
||
|
||
cursor = $( ".ui-resizable-" + this.axis ).css( "cursor" );
|
||
$( "body" ).css( "cursor", cursor === "auto" ? this.axis + "-resize" : cursor );
|
||
|
||
this._addClass( "ui-resizable-resizing" );
|
||
this._propagate( "start", event );
|
||
return true;
|
||
},
|
||
|
||
_mouseDrag: function( event ) {
|
||
|
||
var data, props,
|
||
smp = this.originalMousePosition,
|
||
a = this.axis,
|
||
dx = ( event.pageX - smp.left ) || 0,
|
||
dy = ( event.pageY - smp.top ) || 0,
|
||
trigger = this._change[ a ];
|
||
|
||
this._updatePrevProperties();
|
||
|
||
if ( !trigger ) {
|
||
return false;
|
||
}
|
||
|
||
data = trigger.apply( this, [ event, dx, dy ] );
|
||
|
||
this._updateVirtualBoundaries( event.shiftKey );
|
||
if ( this._aspectRatio || event.shiftKey ) {
|
||
data = this._updateRatio( data, event );
|
||
}
|
||
|
||
data = this._respectSize( data, event );
|
||
|
||
this._updateCache( data );
|
||
|
||
this._propagate( "resize", event );
|
||
|
||
props = this._applyChanges();
|
||
|
||
if ( !this._helper && this._proportionallyResizeElements.length ) {
|
||
this._proportionallyResize();
|
||
}
|
||
|
||
if ( !$.isEmptyObject( props ) ) {
|
||
this._updatePrevProperties();
|
||
this._trigger( "resize", event, this.ui() );
|
||
this._applyChanges();
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
_mouseStop: function( event ) {
|
||
|
||
this.resizing = false;
|
||
var pr, ista, soffseth, soffsetw, s, left, top,
|
||
o = this.options, that = this;
|
||
|
||
if ( this._helper ) {
|
||
|
||
pr = this._proportionallyResizeElements;
|
||
ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName );
|
||
soffseth = ista && this._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height;
|
||
soffsetw = ista ? 0 : that.sizeDiff.width;
|
||
|
||
s = {
|
||
width: ( that.helper.width() - soffsetw ),
|
||
height: ( that.helper.height() - soffseth )
|
||
};
|
||
left = ( parseFloat( that.element.css( "left" ) ) +
|
||
( that.position.left - that.originalPosition.left ) ) || null;
|
||
top = ( parseFloat( that.element.css( "top" ) ) +
|
||
( that.position.top - that.originalPosition.top ) ) || null;
|
||
|
||
if ( !o.animate ) {
|
||
this.element.css( $.extend( s, { top: top, left: left } ) );
|
||
}
|
||
|
||
that.helper.height( that.size.height );
|
||
that.helper.width( that.size.width );
|
||
|
||
if ( this._helper && !o.animate ) {
|
||
this._proportionallyResize();
|
||
}
|
||
}
|
||
|
||
$( "body" ).css( "cursor", "auto" );
|
||
|
||
this._removeClass( "ui-resizable-resizing" );
|
||
|
||
this._propagate( "stop", event );
|
||
|
||
if ( this._helper ) {
|
||
this.helper.remove();
|
||
}
|
||
|
||
return false;
|
||
|
||
},
|
||
|
||
_updatePrevProperties: function() {
|
||
this.prevPosition = {
|
||
top: this.position.top,
|
||
left: this.position.left
|
||
};
|
||
this.prevSize = {
|
||
width: this.size.width,
|
||
height: this.size.height
|
||
};
|
||
},
|
||
|
||
_applyChanges: function() {
|
||
var props = {};
|
||
|
||
if ( this.position.top !== this.prevPosition.top ) {
|
||
props.top = this.position.top + "px";
|
||
}
|
||
if ( this.position.left !== this.prevPosition.left ) {
|
||
props.left = this.position.left + "px";
|
||
}
|
||
if ( this.size.width !== this.prevSize.width ) {
|
||
props.width = this.size.width + "px";
|
||
}
|
||
if ( this.size.height !== this.prevSize.height ) {
|
||
props.height = this.size.height + "px";
|
||
}
|
||
|
||
this.helper.css( props );
|
||
|
||
return props;
|
||
},
|
||
|
||
_updateVirtualBoundaries: function( forceAspectRatio ) {
|
||
var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
|
||
o = this.options;
|
||
|
||
b = {
|
||
minWidth: this._isNumber( o.minWidth ) ? o.minWidth : 0,
|
||
maxWidth: this._isNumber( o.maxWidth ) ? o.maxWidth : Infinity,
|
||
minHeight: this._isNumber( o.minHeight ) ? o.minHeight : 0,
|
||
maxHeight: this._isNumber( o.maxHeight ) ? o.maxHeight : Infinity
|
||
};
|
||
|
||
if ( this._aspectRatio || forceAspectRatio ) {
|
||
pMinWidth = b.minHeight * this.aspectRatio;
|
||
pMinHeight = b.minWidth / this.aspectRatio;
|
||
pMaxWidth = b.maxHeight * this.aspectRatio;
|
||
pMaxHeight = b.maxWidth / this.aspectRatio;
|
||
|
||
if ( pMinWidth > b.minWidth ) {
|
||
b.minWidth = pMinWidth;
|
||
}
|
||
if ( pMinHeight > b.minHeight ) {
|
||
b.minHeight = pMinHeight;
|
||
}
|
||
if ( pMaxWidth < b.maxWidth ) {
|
||
b.maxWidth = pMaxWidth;
|
||
}
|
||
if ( pMaxHeight < b.maxHeight ) {
|
||
b.maxHeight = pMaxHeight;
|
||
}
|
||
}
|
||
this._vBoundaries = b;
|
||
},
|
||
|
||
_updateCache: function( data ) {
|
||
this.offset = this.helper.offset();
|
||
if ( this._isNumber( data.left ) ) {
|
||
this.position.left = data.left;
|
||
}
|
||
if ( this._isNumber( data.top ) ) {
|
||
this.position.top = data.top;
|
||
}
|
||
if ( this._isNumber( data.height ) ) {
|
||
this.size.height = data.height;
|
||
}
|
||
if ( this._isNumber( data.width ) ) {
|
||
this.size.width = data.width;
|
||
}
|
||
},
|
||
|
||
_updateRatio: function( data ) {
|
||
|
||
var cpos = this.position,
|
||
csize = this.size,
|
||
a = this.axis;
|
||
|
||
if ( this._isNumber( data.height ) ) {
|
||
data.width = ( data.height * this.aspectRatio );
|
||
} else if ( this._isNumber( data.width ) ) {
|
||
data.height = ( data.width / this.aspectRatio );
|
||
}
|
||
|
||
if ( a === "sw" ) {
|
||
data.left = cpos.left + ( csize.width - data.width );
|
||
data.top = null;
|
||
}
|
||
if ( a === "nw" ) {
|
||
data.top = cpos.top + ( csize.height - data.height );
|
||
data.left = cpos.left + ( csize.width - data.width );
|
||
}
|
||
|
||
return data;
|
||
},
|
||
|
||
_respectSize: function( data ) {
|
||
|
||
var o = this._vBoundaries,
|
||
a = this.axis,
|
||
ismaxw = this._isNumber( data.width ) && o.maxWidth && ( o.maxWidth < data.width ),
|
||
ismaxh = this._isNumber( data.height ) && o.maxHeight && ( o.maxHeight < data.height ),
|
||
isminw = this._isNumber( data.width ) && o.minWidth && ( o.minWidth > data.width ),
|
||
isminh = this._isNumber( data.height ) && o.minHeight && ( o.minHeight > data.height ),
|
||
dw = this.originalPosition.left + this.originalSize.width,
|
||
dh = this.originalPosition.top + this.originalSize.height,
|
||
cw = /sw|nw|w/.test( a ), ch = /nw|ne|n/.test( a );
|
||
if ( isminw ) {
|
||
data.width = o.minWidth;
|
||
}
|
||
if ( isminh ) {
|
||
data.height = o.minHeight;
|
||
}
|
||
if ( ismaxw ) {
|
||
data.width = o.maxWidth;
|
||
}
|
||
if ( ismaxh ) {
|
||
data.height = o.maxHeight;
|
||
}
|
||
|
||
if ( isminw && cw ) {
|
||
data.left = dw - o.minWidth;
|
||
}
|
||
if ( ismaxw && cw ) {
|
||
data.left = dw - o.maxWidth;
|
||
}
|
||
if ( isminh && ch ) {
|
||
data.top = dh - o.minHeight;
|
||
}
|
||
if ( ismaxh && ch ) {
|
||
data.top = dh - o.maxHeight;
|
||
}
|
||
|
||
// Fixing jump error on top/left - bug #2330
|
||
if ( !data.width && !data.height && !data.left && data.top ) {
|
||
data.top = null;
|
||
} else if ( !data.width && !data.height && !data.top && data.left ) {
|
||
data.left = null;
|
||
}
|
||
|
||
return data;
|
||
},
|
||
|
||
_getPaddingPlusBorderDimensions: function( element ) {
|
||
var i = 0,
|
||
widths = [],
|
||
borders = [
|
||
element.css( "borderTopWidth" ),
|
||
element.css( "borderRightWidth" ),
|
||
element.css( "borderBottomWidth" ),
|
||
element.css( "borderLeftWidth" )
|
||
],
|
||
paddings = [
|
||
element.css( "paddingTop" ),
|
||
element.css( "paddingRight" ),
|
||
element.css( "paddingBottom" ),
|
||
element.css( "paddingLeft" )
|
||
];
|
||
|
||
for ( ; i < 4; i++ ) {
|
||
widths[ i ] = ( parseFloat( borders[ i ] ) || 0 );
|
||
widths[ i ] += ( parseFloat( paddings[ i ] ) || 0 );
|
||
}
|
||
|
||
return {
|
||
height: widths[ 0 ] + widths[ 2 ],
|
||
width: widths[ 1 ] + widths[ 3 ]
|
||
};
|
||
},
|
||
|
||
_proportionallyResize: function() {
|
||
|
||
if ( !this._proportionallyResizeElements.length ) {
|
||
return;
|
||
}
|
||
|
||
var prel,
|
||
i = 0,
|
||
element = this.helper || this.element;
|
||
|
||
for ( ; i < this._proportionallyResizeElements.length; i++ ) {
|
||
|
||
prel = this._proportionallyResizeElements[ i ];
|
||
|
||
// TODO: Seems like a bug to cache this.outerDimensions
|
||
// considering that we are in a loop.
|
||
if ( !this.outerDimensions ) {
|
||
this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
|
||
}
|
||
|
||
prel.css( {
|
||
height: ( element.height() - this.outerDimensions.height ) || 0,
|
||
width: ( element.width() - this.outerDimensions.width ) || 0
|
||
} );
|
||
|
||
}
|
||
|
||
},
|
||
|
||
_renderProxy: function() {
|
||
|
||
var el = this.element, o = this.options;
|
||
this.elementOffset = el.offset();
|
||
|
||
if ( this._helper ) {
|
||
|
||
this.helper = this.helper || $( "<div style='overflow:hidden;'></div>" );
|
||
|
||
this._addClass( this.helper, this._helper );
|
||
this.helper.css( {
|
||
width: this.element.outerWidth(),
|
||
height: this.element.outerHeight(),
|
||
position: "absolute",
|
||
left: this.elementOffset.left + "px",
|
||
top: this.elementOffset.top + "px",
|
||
zIndex: ++o.zIndex //TODO: Don't modify option
|
||
} );
|
||
|
||
this.helper
|
||
.appendTo( "body" )
|
||
.disableSelection();
|
||
|
||
} else {
|
||
this.helper = this.element;
|
||
}
|
||
|
||
},
|
||
|
||
_change: {
|
||
e: function( event, dx ) {
|
||
return { width: this.originalSize.width + dx };
|
||
},
|
||
w: function( event, dx ) {
|
||
var cs = this.originalSize, sp = this.originalPosition;
|
||
return { left: sp.left + dx, width: cs.width - dx };
|
||
},
|
||
n: function( event, dx, dy ) {
|
||
var cs = this.originalSize, sp = this.originalPosition;
|
||
return { top: sp.top + dy, height: cs.height - dy };
|
||
},
|
||
s: function( event, dx, dy ) {
|
||
return { height: this.originalSize.height + dy };
|
||
},
|
||
se: function( event, dx, dy ) {
|
||
return $.extend( this._change.s.apply( this, arguments ),
|
||
this._change.e.apply( this, [ event, dx, dy ] ) );
|
||
},
|
||
sw: function( event, dx, dy ) {
|
||
return $.extend( this._change.s.apply( this, arguments ),
|
||
this._change.w.apply( this, [ event, dx, dy ] ) );
|
||
},
|
||
ne: function( event, dx, dy ) {
|
||
return $.extend( this._change.n.apply( this, arguments ),
|
||
this._change.e.apply( this, [ event, dx, dy ] ) );
|
||
},
|
||
nw: function( event, dx, dy ) {
|
||
return $.extend( this._change.n.apply( this, arguments ),
|
||
this._change.w.apply( this, [ event, dx, dy ] ) );
|
||
}
|
||
},
|
||
|
||
_propagate: function( n, event ) {
|
||
$.ui.plugin.call( this, n, [ event, this.ui() ] );
|
||
( n !== "resize" && this._trigger( n, event, this.ui() ) );
|
||
},
|
||
|
||
plugins: {},
|
||
|
||
ui: function() {
|
||
return {
|
||
originalElement: this.originalElement,
|
||
element: this.element,
|
||
helper: this.helper,
|
||
position: this.position,
|
||
size: this.size,
|
||
originalSize: this.originalSize,
|
||
originalPosition: this.originalPosition
|
||
};
|
||
}
|
||
|
||
} );
|
||
|
||
/*
|
||
* Resizable Extensions
|
||
*/
|
||
|
||
$.ui.plugin.add( "resizable", "animate", {
|
||
|
||
stop: function( event ) {
|
||
var that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
pr = that._proportionallyResizeElements,
|
||
ista = pr.length && ( /textarea/i ).test( pr[ 0 ].nodeName ),
|
||
soffseth = ista && that._hasScroll( pr[ 0 ], "left" ) ? 0 : that.sizeDiff.height,
|
||
soffsetw = ista ? 0 : that.sizeDiff.width,
|
||
style = {
|
||
width: ( that.size.width - soffsetw ),
|
||
height: ( that.size.height - soffseth )
|
||
},
|
||
left = ( parseFloat( that.element.css( "left" ) ) +
|
||
( that.position.left - that.originalPosition.left ) ) || null,
|
||
top = ( parseFloat( that.element.css( "top" ) ) +
|
||
( that.position.top - that.originalPosition.top ) ) || null;
|
||
|
||
that.element.animate(
|
||
$.extend( style, top && left ? { top: top, left: left } : {} ), {
|
||
duration: o.animateDuration,
|
||
easing: o.animateEasing,
|
||
step: function() {
|
||
|
||
var data = {
|
||
width: parseFloat( that.element.css( "width" ) ),
|
||
height: parseFloat( that.element.css( "height" ) ),
|
||
top: parseFloat( that.element.css( "top" ) ),
|
||
left: parseFloat( that.element.css( "left" ) )
|
||
};
|
||
|
||
if ( pr && pr.length ) {
|
||
$( pr[ 0 ] ).css( { width: data.width, height: data.height } );
|
||
}
|
||
|
||
// Propagating resize, and updating values for each animation step
|
||
that._updateCache( data );
|
||
that._propagate( "resize", event );
|
||
|
||
}
|
||
}
|
||
);
|
||
}
|
||
|
||
} );
|
||
|
||
$.ui.plugin.add( "resizable", "containment", {
|
||
|
||
start: function() {
|
||
var element, p, co, ch, cw, width, height,
|
||
that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
el = that.element,
|
||
oc = o.containment,
|
||
ce = ( oc instanceof $ ) ?
|
||
oc.get( 0 ) :
|
||
( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;
|
||
|
||
if ( !ce ) {
|
||
return;
|
||
}
|
||
|
||
that.containerElement = $( ce );
|
||
|
||
if ( /document/.test( oc ) || oc === document ) {
|
||
that.containerOffset = {
|
||
left: 0,
|
||
top: 0
|
||
};
|
||
that.containerPosition = {
|
||
left: 0,
|
||
top: 0
|
||
};
|
||
|
||
that.parentData = {
|
||
element: $( document ),
|
||
left: 0,
|
||
top: 0,
|
||
width: $( document ).width(),
|
||
height: $( document ).height() || document.body.parentNode.scrollHeight
|
||
};
|
||
} else {
|
||
element = $( ce );
|
||
p = [];
|
||
$( [ "Top", "Right", "Left", "Bottom" ] ).each( function( i, name ) {
|
||
p[ i ] = that._num( element.css( "padding" + name ) );
|
||
} );
|
||
|
||
that.containerOffset = element.offset();
|
||
that.containerPosition = element.position();
|
||
that.containerSize = {
|
||
height: ( element.innerHeight() - p[ 3 ] ),
|
||
width: ( element.innerWidth() - p[ 1 ] )
|
||
};
|
||
|
||
co = that.containerOffset;
|
||
ch = that.containerSize.height;
|
||
cw = that.containerSize.width;
|
||
width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
|
||
height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;
|
||
|
||
that.parentData = {
|
||
element: ce,
|
||
left: co.left,
|
||
top: co.top,
|
||
width: width,
|
||
height: height
|
||
};
|
||
}
|
||
},
|
||
|
||
resize: function( event ) {
|
||
var woset, hoset, isParent, isOffsetRelative,
|
||
that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
co = that.containerOffset,
|
||
cp = that.position,
|
||
pRatio = that._aspectRatio || event.shiftKey,
|
||
cop = {
|
||
top: 0,
|
||
left: 0
|
||
},
|
||
ce = that.containerElement,
|
||
continueResize = true;
|
||
|
||
if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
|
||
cop = co;
|
||
}
|
||
|
||
if ( cp.left < ( that._helper ? co.left : 0 ) ) {
|
||
that.size.width = that.size.width +
|
||
( that._helper ?
|
||
( that.position.left - co.left ) :
|
||
( that.position.left - cop.left ) );
|
||
|
||
if ( pRatio ) {
|
||
that.size.height = that.size.width / that.aspectRatio;
|
||
continueResize = false;
|
||
}
|
||
that.position.left = o.helper ? co.left : 0;
|
||
}
|
||
|
||
if ( cp.top < ( that._helper ? co.top : 0 ) ) {
|
||
that.size.height = that.size.height +
|
||
( that._helper ?
|
||
( that.position.top - co.top ) :
|
||
that.position.top );
|
||
|
||
if ( pRatio ) {
|
||
that.size.width = that.size.height * that.aspectRatio;
|
||
continueResize = false;
|
||
}
|
||
that.position.top = that._helper ? co.top : 0;
|
||
}
|
||
|
||
isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
|
||
isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );
|
||
|
||
if ( isParent && isOffsetRelative ) {
|
||
that.offset.left = that.parentData.left + that.position.left;
|
||
that.offset.top = that.parentData.top + that.position.top;
|
||
} else {
|
||
that.offset.left = that.element.offset().left;
|
||
that.offset.top = that.element.offset().top;
|
||
}
|
||
|
||
woset = Math.abs( that.sizeDiff.width +
|
||
( that._helper ?
|
||
that.offset.left - cop.left :
|
||
( that.offset.left - co.left ) ) );
|
||
|
||
hoset = Math.abs( that.sizeDiff.height +
|
||
( that._helper ?
|
||
that.offset.top - cop.top :
|
||
( that.offset.top - co.top ) ) );
|
||
|
||
if ( woset + that.size.width >= that.parentData.width ) {
|
||
that.size.width = that.parentData.width - woset;
|
||
if ( pRatio ) {
|
||
that.size.height = that.size.width / that.aspectRatio;
|
||
continueResize = false;
|
||
}
|
||
}
|
||
|
||
if ( hoset + that.size.height >= that.parentData.height ) {
|
||
that.size.height = that.parentData.height - hoset;
|
||
if ( pRatio ) {
|
||
that.size.width = that.size.height * that.aspectRatio;
|
||
continueResize = false;
|
||
}
|
||
}
|
||
|
||
if ( !continueResize ) {
|
||
that.position.left = that.prevPosition.left;
|
||
that.position.top = that.prevPosition.top;
|
||
that.size.width = that.prevSize.width;
|
||
that.size.height = that.prevSize.height;
|
||
}
|
||
},
|
||
|
||
stop: function() {
|
||
var that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
co = that.containerOffset,
|
||
cop = that.containerPosition,
|
||
ce = that.containerElement,
|
||
helper = $( that.helper ),
|
||
ho = helper.offset(),
|
||
w = helper.outerWidth() - that.sizeDiff.width,
|
||
h = helper.outerHeight() - that.sizeDiff.height;
|
||
|
||
if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
|
||
$( this ).css( {
|
||
left: ho.left - cop.left - co.left,
|
||
width: w,
|
||
height: h
|
||
} );
|
||
}
|
||
|
||
if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
|
||
$( this ).css( {
|
||
left: ho.left - cop.left - co.left,
|
||
width: w,
|
||
height: h
|
||
} );
|
||
}
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "resizable", "alsoResize", {
|
||
|
||
start: function() {
|
||
var that = $( this ).resizable( "instance" ),
|
||
o = that.options;
|
||
|
||
$( o.alsoResize ).each( function() {
|
||
var el = $( this );
|
||
el.data( "ui-resizable-alsoresize", {
|
||
width: parseFloat( el.width() ), height: parseFloat( el.height() ),
|
||
left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
|
||
} );
|
||
} );
|
||
},
|
||
|
||
resize: function( event, ui ) {
|
||
var that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
os = that.originalSize,
|
||
op = that.originalPosition,
|
||
delta = {
|
||
height: ( that.size.height - os.height ) || 0,
|
||
width: ( that.size.width - os.width ) || 0,
|
||
top: ( that.position.top - op.top ) || 0,
|
||
left: ( that.position.left - op.left ) || 0
|
||
};
|
||
|
||
$( o.alsoResize ).each( function() {
|
||
var el = $( this ), start = $( this ).data( "ui-resizable-alsoresize" ), style = {},
|
||
css = el.parents( ui.originalElement[ 0 ] ).length ?
|
||
[ "width", "height" ] :
|
||
[ "width", "height", "top", "left" ];
|
||
|
||
$.each( css, function( i, prop ) {
|
||
var sum = ( start[ prop ] || 0 ) + ( delta[ prop ] || 0 );
|
||
if ( sum && sum >= 0 ) {
|
||
style[ prop ] = sum || null;
|
||
}
|
||
} );
|
||
|
||
el.css( style );
|
||
} );
|
||
},
|
||
|
||
stop: function() {
|
||
$( this ).removeData( "ui-resizable-alsoresize" );
|
||
}
|
||
} );
|
||
|
||
$.ui.plugin.add( "resizable", "ghost", {
|
||
|
||
start: function() {
|
||
|
||
var that = $( this ).resizable( "instance" ), cs = that.size;
|
||
|
||
that.ghost = that.originalElement.clone();
|
||
that.ghost.css( {
|
||
opacity: 0.25,
|
||
display: "block",
|
||
position: "relative",
|
||
height: cs.height,
|
||
width: cs.width,
|
||
margin: 0,
|
||
left: 0,
|
||
top: 0
|
||
} );
|
||
|
||
that._addClass( that.ghost, "ui-resizable-ghost" );
|
||
|
||
// DEPRECATED
|
||
// TODO: remove after 1.12
|
||
if ( $.uiBackCompat !== false && typeof that.options.ghost === "string" ) {
|
||
|
||
// Ghost option
|
||
that.ghost.addClass( this.options.ghost );
|
||
}
|
||
|
||
that.ghost.appendTo( that.helper );
|
||
|
||
},
|
||
|
||
resize: function() {
|
||
var that = $( this ).resizable( "instance" );
|
||
if ( that.ghost ) {
|
||
that.ghost.css( {
|
||
position: "relative",
|
||
height: that.size.height,
|
||
width: that.size.width
|
||
} );
|
||
}
|
||
},
|
||
|
||
stop: function() {
|
||
var that = $( this ).resizable( "instance" );
|
||
if ( that.ghost && that.helper ) {
|
||
that.helper.get( 0 ).removeChild( that.ghost.get( 0 ) );
|
||
}
|
||
}
|
||
|
||
} );
|
||
|
||
$.ui.plugin.add( "resizable", "grid", {
|
||
|
||
resize: function() {
|
||
var outerDimensions,
|
||
that = $( this ).resizable( "instance" ),
|
||
o = that.options,
|
||
cs = that.size,
|
||
os = that.originalSize,
|
||
op = that.originalPosition,
|
||
a = that.axis,
|
||
grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
|
||
gridX = ( grid[ 0 ] || 1 ),
|
||
gridY = ( grid[ 1 ] || 1 ),
|
||
ox = Math.round( ( cs.width - os.width ) / gridX ) * gridX,
|
||
oy = Math.round( ( cs.height - os.height ) / gridY ) * gridY,
|
||
newWidth = os.width + ox,
|
||
newHeight = os.height + oy,
|
||
isMaxWidth = o.maxWidth && ( o.maxWidth < newWidth ),
|
||
isMaxHeight = o.maxHeight && ( o.maxHeight < newHeight ),
|
||
isMinWidth = o.minWidth && ( o.minWidth > newWidth ),
|
||
isMinHeight = o.minHeight && ( o.minHeight > newHeight );
|
||
|
||
o.grid = grid;
|
||
|
||
if ( isMinWidth ) {
|
||
newWidth += gridX;
|
||
}
|
||
if ( isMinHeight ) {
|
||
newHeight += gridY;
|
||
}
|
||
if ( isMaxWidth ) {
|
||
newWidth -= gridX;
|
||
}
|
||
if ( isMaxHeight ) {
|
||
newHeight -= gridY;
|
||
}
|
||
|
||
if ( /^(se|s|e)$/.test( a ) ) {
|
||
that.size.width = newWidth;
|
||
that.size.height = newHeight;
|
||
} else if ( /^(ne)$/.test( a ) ) {
|
||
that.size.width = newWidth;
|
||
that.size.height = newHeight;
|
||
that.position.top = op.top - oy;
|
||
} else if ( /^(sw)$/.test( a ) ) {
|
||
that.size.width = newWidth;
|
||
that.size.height = newHeight;
|
||
that.position.left = op.left - ox;
|
||
} else {
|
||
if ( newHeight - gridY <= 0 || newWidth - gridX <= 0 ) {
|
||
outerDimensions = that._getPaddingPlusBorderDimensions( this );
|
||
}
|
||
|
||
if ( newHeight - gridY > 0 ) {
|
||
that.size.height = newHeight;
|
||
that.position.top = op.top - oy;
|
||
} else {
|
||
newHeight = gridY - outerDimensions.height;
|
||
that.size.height = newHeight;
|
||
that.position.top = op.top + os.height - newHeight;
|
||
}
|
||
if ( newWidth - gridX > 0 ) {
|
||
that.size.width = newWidth;
|
||
that.position.left = op.left - ox;
|
||
} else {
|
||
newWidth = gridX - outerDimensions.width;
|
||
that.size.width = newWidth;
|
||
that.position.left = op.left + os.width - newWidth;
|
||
}
|
||
}
|
||
}
|
||
|
||
} );
|
||
|
||
var widgetsResizable = $.ui.resizable;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Selectable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Selectable
|
||
//>>group: Interactions
|
||
//>>description: Allows groups of elements to be selected with the mouse.
|
||
//>>docs: http://api.jqueryui.com/selectable/
|
||
//>>demos: http://jqueryui.com/selectable/
|
||
//>>css.structure: ../../themes/base/selectable.css
|
||
|
||
|
||
|
||
var widgetsSelectable = $.widget( "ui.selectable", $.ui.mouse, {
|
||
version: "1.12.1",
|
||
options: {
|
||
appendTo: "body",
|
||
autoRefresh: true,
|
||
distance: 0,
|
||
filter: "*",
|
||
tolerance: "touch",
|
||
|
||
// Callbacks
|
||
selected: null,
|
||
selecting: null,
|
||
start: null,
|
||
stop: null,
|
||
unselected: null,
|
||
unselecting: null
|
||
},
|
||
_create: function() {
|
||
var that = this;
|
||
|
||
this._addClass( "ui-selectable" );
|
||
|
||
this.dragged = false;
|
||
|
||
// Cache selectee children based on filter
|
||
this.refresh = function() {
|
||
that.elementPos = $( that.element[ 0 ] ).offset();
|
||
that.selectees = $( that.options.filter, that.element[ 0 ] );
|
||
that._addClass( that.selectees, "ui-selectee" );
|
||
that.selectees.each( function() {
|
||
var $this = $( this ),
|
||
selecteeOffset = $this.offset(),
|
||
pos = {
|
||
left: selecteeOffset.left - that.elementPos.left,
|
||
top: selecteeOffset.top - that.elementPos.top
|
||
};
|
||
$.data( this, "selectable-item", {
|
||
element: this,
|
||
$element: $this,
|
||
left: pos.left,
|
||
top: pos.top,
|
||
right: pos.left + $this.outerWidth(),
|
||
bottom: pos.top + $this.outerHeight(),
|
||
startselected: false,
|
||
selected: $this.hasClass( "ui-selected" ),
|
||
selecting: $this.hasClass( "ui-selecting" ),
|
||
unselecting: $this.hasClass( "ui-unselecting" )
|
||
} );
|
||
} );
|
||
};
|
||
this.refresh();
|
||
|
||
this._mouseInit();
|
||
|
||
this.helper = $( "<div>" );
|
||
this._addClass( this.helper, "ui-selectable-helper" );
|
||
},
|
||
|
||
_destroy: function() {
|
||
this.selectees.removeData( "selectable-item" );
|
||
this._mouseDestroy();
|
||
},
|
||
|
||
_mouseStart: function( event ) {
|
||
var that = this,
|
||
options = this.options;
|
||
|
||
this.opos = [ event.pageX, event.pageY ];
|
||
this.elementPos = $( this.element[ 0 ] ).offset();
|
||
|
||
if ( this.options.disabled ) {
|
||
return;
|
||
}
|
||
|
||
this.selectees = $( options.filter, this.element[ 0 ] );
|
||
|
||
this._trigger( "start", event );
|
||
|
||
$( options.appendTo ).append( this.helper );
|
||
|
||
// position helper (lasso)
|
||
this.helper.css( {
|
||
"left": event.pageX,
|
||
"top": event.pageY,
|
||
"width": 0,
|
||
"height": 0
|
||
} );
|
||
|
||
if ( options.autoRefresh ) {
|
||
this.refresh();
|
||
}
|
||
|
||
this.selectees.filter( ".ui-selected" ).each( function() {
|
||
var selectee = $.data( this, "selectable-item" );
|
||
selectee.startselected = true;
|
||
if ( !event.metaKey && !event.ctrlKey ) {
|
||
that._removeClass( selectee.$element, "ui-selected" );
|
||
selectee.selected = false;
|
||
that._addClass( selectee.$element, "ui-unselecting" );
|
||
selectee.unselecting = true;
|
||
|
||
// selectable UNSELECTING callback
|
||
that._trigger( "unselecting", event, {
|
||
unselecting: selectee.element
|
||
} );
|
||
}
|
||
} );
|
||
|
||
$( event.target ).parents().addBack().each( function() {
|
||
var doSelect,
|
||
selectee = $.data( this, "selectable-item" );
|
||
if ( selectee ) {
|
||
doSelect = ( !event.metaKey && !event.ctrlKey ) ||
|
||
!selectee.$element.hasClass( "ui-selected" );
|
||
that._removeClass( selectee.$element, doSelect ? "ui-unselecting" : "ui-selected" )
|
||
._addClass( selectee.$element, doSelect ? "ui-selecting" : "ui-unselecting" );
|
||
selectee.unselecting = !doSelect;
|
||
selectee.selecting = doSelect;
|
||
selectee.selected = doSelect;
|
||
|
||
// selectable (UN)SELECTING callback
|
||
if ( doSelect ) {
|
||
that._trigger( "selecting", event, {
|
||
selecting: selectee.element
|
||
} );
|
||
} else {
|
||
that._trigger( "unselecting", event, {
|
||
unselecting: selectee.element
|
||
} );
|
||
}
|
||
return false;
|
||
}
|
||
} );
|
||
|
||
},
|
||
|
||
_mouseDrag: function( event ) {
|
||
|
||
this.dragged = true;
|
||
|
||
if ( this.options.disabled ) {
|
||
return;
|
||
}
|
||
|
||
var tmp,
|
||
that = this,
|
||
options = this.options,
|
||
x1 = this.opos[ 0 ],
|
||
y1 = this.opos[ 1 ],
|
||
x2 = event.pageX,
|
||
y2 = event.pageY;
|
||
|
||
if ( x1 > x2 ) { tmp = x2; x2 = x1; x1 = tmp; }
|
||
if ( y1 > y2 ) { tmp = y2; y2 = y1; y1 = tmp; }
|
||
this.helper.css( { left: x1, top: y1, width: x2 - x1, height: y2 - y1 } );
|
||
|
||
this.selectees.each( function() {
|
||
var selectee = $.data( this, "selectable-item" ),
|
||
hit = false,
|
||
offset = {};
|
||
|
||
//prevent helper from being selected if appendTo: selectable
|
||
if ( !selectee || selectee.element === that.element[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
offset.left = selectee.left + that.elementPos.left;
|
||
offset.right = selectee.right + that.elementPos.left;
|
||
offset.top = selectee.top + that.elementPos.top;
|
||
offset.bottom = selectee.bottom + that.elementPos.top;
|
||
|
||
if ( options.tolerance === "touch" ) {
|
||
hit = ( !( offset.left > x2 || offset.right < x1 || offset.top > y2 ||
|
||
offset.bottom < y1 ) );
|
||
} else if ( options.tolerance === "fit" ) {
|
||
hit = ( offset.left > x1 && offset.right < x2 && offset.top > y1 &&
|
||
offset.bottom < y2 );
|
||
}
|
||
|
||
if ( hit ) {
|
||
|
||
// SELECT
|
||
if ( selectee.selected ) {
|
||
that._removeClass( selectee.$element, "ui-selected" );
|
||
selectee.selected = false;
|
||
}
|
||
if ( selectee.unselecting ) {
|
||
that._removeClass( selectee.$element, "ui-unselecting" );
|
||
selectee.unselecting = false;
|
||
}
|
||
if ( !selectee.selecting ) {
|
||
that._addClass( selectee.$element, "ui-selecting" );
|
||
selectee.selecting = true;
|
||
|
||
// selectable SELECTING callback
|
||
that._trigger( "selecting", event, {
|
||
selecting: selectee.element
|
||
} );
|
||
}
|
||
} else {
|
||
|
||
// UNSELECT
|
||
if ( selectee.selecting ) {
|
||
if ( ( event.metaKey || event.ctrlKey ) && selectee.startselected ) {
|
||
that._removeClass( selectee.$element, "ui-selecting" );
|
||
selectee.selecting = false;
|
||
that._addClass( selectee.$element, "ui-selected" );
|
||
selectee.selected = true;
|
||
} else {
|
||
that._removeClass( selectee.$element, "ui-selecting" );
|
||
selectee.selecting = false;
|
||
if ( selectee.startselected ) {
|
||
that._addClass( selectee.$element, "ui-unselecting" );
|
||
selectee.unselecting = true;
|
||
}
|
||
|
||
// selectable UNSELECTING callback
|
||
that._trigger( "unselecting", event, {
|
||
unselecting: selectee.element
|
||
} );
|
||
}
|
||
}
|
||
if ( selectee.selected ) {
|
||
if ( !event.metaKey && !event.ctrlKey && !selectee.startselected ) {
|
||
that._removeClass( selectee.$element, "ui-selected" );
|
||
selectee.selected = false;
|
||
|
||
that._addClass( selectee.$element, "ui-unselecting" );
|
||
selectee.unselecting = true;
|
||
|
||
// selectable UNSELECTING callback
|
||
that._trigger( "unselecting", event, {
|
||
unselecting: selectee.element
|
||
} );
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
|
||
return false;
|
||
},
|
||
|
||
_mouseStop: function( event ) {
|
||
var that = this;
|
||
|
||
this.dragged = false;
|
||
|
||
$( ".ui-unselecting", this.element[ 0 ] ).each( function() {
|
||
var selectee = $.data( this, "selectable-item" );
|
||
that._removeClass( selectee.$element, "ui-unselecting" );
|
||
selectee.unselecting = false;
|
||
selectee.startselected = false;
|
||
that._trigger( "unselected", event, {
|
||
unselected: selectee.element
|
||
} );
|
||
} );
|
||
$( ".ui-selecting", this.element[ 0 ] ).each( function() {
|
||
var selectee = $.data( this, "selectable-item" );
|
||
that._removeClass( selectee.$element, "ui-selecting" )
|
||
._addClass( selectee.$element, "ui-selected" );
|
||
selectee.selecting = false;
|
||
selectee.selected = true;
|
||
selectee.startselected = true;
|
||
that._trigger( "selected", event, {
|
||
selected: selectee.element
|
||
} );
|
||
} );
|
||
this._trigger( "stop", event );
|
||
|
||
this.helper.remove();
|
||
|
||
return false;
|
||
}
|
||
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Sortable 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Sortable
|
||
//>>group: Interactions
|
||
//>>description: Enables items in a list to be sorted using the mouse.
|
||
//>>docs: http://api.jqueryui.com/sortable/
|
||
//>>demos: http://jqueryui.com/sortable/
|
||
//>>css.structure: ../../themes/base/sortable.css
|
||
|
||
|
||
|
||
var widgetsSortable = $.widget( "ui.sortable", $.ui.mouse, {
|
||
version: "1.12.1",
|
||
widgetEventPrefix: "sort",
|
||
ready: false,
|
||
options: {
|
||
appendTo: "parent",
|
||
axis: false,
|
||
connectWith: false,
|
||
containment: false,
|
||
cursor: "auto",
|
||
cursorAt: false,
|
||
dropOnEmpty: true,
|
||
forcePlaceholderSize: false,
|
||
forceHelperSize: false,
|
||
grid: false,
|
||
handle: false,
|
||
helper: "original",
|
||
items: "> *",
|
||
opacity: false,
|
||
placeholder: false,
|
||
revert: false,
|
||
scroll: true,
|
||
scrollSensitivity: 20,
|
||
scrollSpeed: 20,
|
||
scope: "default",
|
||
tolerance: "intersect",
|
||
zIndex: 1000,
|
||
|
||
// Callbacks
|
||
activate: null,
|
||
beforeStop: null,
|
||
change: null,
|
||
deactivate: null,
|
||
out: null,
|
||
over: null,
|
||
receive: null,
|
||
remove: null,
|
||
sort: null,
|
||
start: null,
|
||
stop: null,
|
||
update: null
|
||
},
|
||
|
||
_isOverAxis: function( x, reference, size ) {
|
||
return ( x >= reference ) && ( x < ( reference + size ) );
|
||
},
|
||
|
||
_isFloating: function( item ) {
|
||
return ( /left|right/ ).test( item.css( "float" ) ) ||
|
||
( /inline|table-cell/ ).test( item.css( "display" ) );
|
||
},
|
||
|
||
_create: function() {
|
||
this.containerCache = {};
|
||
this._addClass( "ui-sortable" );
|
||
|
||
//Get the items
|
||
this.refresh();
|
||
|
||
//Let's determine the parent's offset
|
||
this.offset = this.element.offset();
|
||
|
||
//Initialize mouse events for interaction
|
||
this._mouseInit();
|
||
|
||
this._setHandleClassName();
|
||
|
||
//We're ready to go
|
||
this.ready = true;
|
||
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
this._super( key, value );
|
||
|
||
if ( key === "handle" ) {
|
||
this._setHandleClassName();
|
||
}
|
||
},
|
||
|
||
_setHandleClassName: function() {
|
||
var that = this;
|
||
this._removeClass( this.element.find( ".ui-sortable-handle" ), "ui-sortable-handle" );
|
||
$.each( this.items, function() {
|
||
that._addClass(
|
||
this.instance.options.handle ?
|
||
this.item.find( this.instance.options.handle ) :
|
||
this.item,
|
||
"ui-sortable-handle"
|
||
);
|
||
} );
|
||
},
|
||
|
||
_destroy: function() {
|
||
this._mouseDestroy();
|
||
|
||
for ( var i = this.items.length - 1; i >= 0; i-- ) {
|
||
this.items[ i ].item.removeData( this.widgetName + "-item" );
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
_mouseCapture: function( event, overrideHandle ) {
|
||
var currentItem = null,
|
||
validHandle = false,
|
||
that = this;
|
||
|
||
if ( this.reverting ) {
|
||
return false;
|
||
}
|
||
|
||
if ( this.options.disabled || this.options.type === "static" ) {
|
||
return false;
|
||
}
|
||
|
||
//We have to refresh the items data once first
|
||
this._refreshItems( event );
|
||
|
||
//Find out if the clicked node (or one of its parents) is a actual item in this.items
|
||
$( event.target ).parents().each( function() {
|
||
if ( $.data( this, that.widgetName + "-item" ) === that ) {
|
||
currentItem = $( this );
|
||
return false;
|
||
}
|
||
} );
|
||
if ( $.data( event.target, that.widgetName + "-item" ) === that ) {
|
||
currentItem = $( event.target );
|
||
}
|
||
|
||
if ( !currentItem ) {
|
||
return false;
|
||
}
|
||
if ( this.options.handle && !overrideHandle ) {
|
||
$( this.options.handle, currentItem ).find( "*" ).addBack().each( function() {
|
||
if ( this === event.target ) {
|
||
validHandle = true;
|
||
}
|
||
} );
|
||
if ( !validHandle ) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
this.currentItem = currentItem;
|
||
this._removeCurrentsFromItems();
|
||
return true;
|
||
|
||
},
|
||
|
||
_mouseStart: function( event, overrideHandle, noActivation ) {
|
||
|
||
var i, body,
|
||
o = this.options;
|
||
|
||
this.currentContainer = this;
|
||
|
||
//We only need to call refreshPositions, because the refreshItems call has been moved to
|
||
// mouseCapture
|
||
this.refreshPositions();
|
||
|
||
//Create and append the visible helper
|
||
this.helper = this._createHelper( event );
|
||
|
||
//Cache the helper size
|
||
this._cacheHelperProportions();
|
||
|
||
/*
|
||
* - Position generation -
|
||
* This block generates everything position related - it's the core of draggables.
|
||
*/
|
||
|
||
//Cache the margins of the original element
|
||
this._cacheMargins();
|
||
|
||
//Get the next scrolling parent
|
||
this.scrollParent = this.helper.scrollParent();
|
||
|
||
//The element's absolute position on the page minus margins
|
||
this.offset = this.currentItem.offset();
|
||
this.offset = {
|
||
top: this.offset.top - this.margins.top,
|
||
left: this.offset.left - this.margins.left
|
||
};
|
||
|
||
$.extend( this.offset, {
|
||
click: { //Where the click happened, relative to the element
|
||
left: event.pageX - this.offset.left,
|
||
top: event.pageY - this.offset.top
|
||
},
|
||
parent: this._getParentOffset(),
|
||
|
||
// This is a relative to absolute position minus the actual position calculation -
|
||
// only used for relative positioned helper
|
||
relative: this._getRelativeOffset()
|
||
} );
|
||
|
||
// Only after we got the offset, we can change the helper's position to absolute
|
||
// TODO: Still need to figure out a way to make relative sorting possible
|
||
this.helper.css( "position", "absolute" );
|
||
this.cssPosition = this.helper.css( "position" );
|
||
|
||
//Generate the original position
|
||
this.originalPosition = this._generatePosition( event );
|
||
this.originalPageX = event.pageX;
|
||
this.originalPageY = event.pageY;
|
||
|
||
//Adjust the mouse offset relative to the helper if "cursorAt" is supplied
|
||
( o.cursorAt && this._adjustOffsetFromHelper( o.cursorAt ) );
|
||
|
||
//Cache the former DOM position
|
||
this.domPosition = {
|
||
prev: this.currentItem.prev()[ 0 ],
|
||
parent: this.currentItem.parent()[ 0 ]
|
||
};
|
||
|
||
// If the helper is not the original, hide the original so it's not playing any role during
|
||
// the drag, won't cause anything bad this way
|
||
if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
|
||
this.currentItem.hide();
|
||
}
|
||
|
||
//Create the placeholder
|
||
this._createPlaceholder();
|
||
|
||
//Set a containment if given in the options
|
||
if ( o.containment ) {
|
||
this._setContainment();
|
||
}
|
||
|
||
if ( o.cursor && o.cursor !== "auto" ) { // cursor option
|
||
body = this.document.find( "body" );
|
||
|
||
// Support: IE
|
||
this.storedCursor = body.css( "cursor" );
|
||
body.css( "cursor", o.cursor );
|
||
|
||
this.storedStylesheet =
|
||
$( "<style>*{ cursor: " + o.cursor + " !important; }</style>" ).appendTo( body );
|
||
}
|
||
|
||
if ( o.opacity ) { // opacity option
|
||
if ( this.helper.css( "opacity" ) ) {
|
||
this._storedOpacity = this.helper.css( "opacity" );
|
||
}
|
||
this.helper.css( "opacity", o.opacity );
|
||
}
|
||
|
||
if ( o.zIndex ) { // zIndex option
|
||
if ( this.helper.css( "zIndex" ) ) {
|
||
this._storedZIndex = this.helper.css( "zIndex" );
|
||
}
|
||
this.helper.css( "zIndex", o.zIndex );
|
||
}
|
||
|
||
//Prepare scrolling
|
||
if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
this.scrollParent[ 0 ].tagName !== "HTML" ) {
|
||
this.overflowOffset = this.scrollParent.offset();
|
||
}
|
||
|
||
//Call callbacks
|
||
this._trigger( "start", event, this._uiHash() );
|
||
|
||
//Recache the helper size
|
||
if ( !this._preserveHelperProportions ) {
|
||
this._cacheHelperProportions();
|
||
}
|
||
|
||
//Post "activate" events to possible containers
|
||
if ( !noActivation ) {
|
||
for ( i = this.containers.length - 1; i >= 0; i-- ) {
|
||
this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
|
||
}
|
||
}
|
||
|
||
//Prepare possible droppables
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.current = this;
|
||
}
|
||
|
||
if ( $.ui.ddmanager && !o.dropBehaviour ) {
|
||
$.ui.ddmanager.prepareOffsets( this, event );
|
||
}
|
||
|
||
this.dragging = true;
|
||
|
||
this._addClass( this.helper, "ui-sortable-helper" );
|
||
|
||
// Execute the drag once - this causes the helper not to be visiblebefore getting its
|
||
// correct position
|
||
this._mouseDrag( event );
|
||
return true;
|
||
|
||
},
|
||
|
||
_mouseDrag: function( event ) {
|
||
var i, item, itemElement, intersection,
|
||
o = this.options,
|
||
scrolled = false;
|
||
|
||
//Compute the helpers position
|
||
this.position = this._generatePosition( event );
|
||
this.positionAbs = this._convertPositionTo( "absolute" );
|
||
|
||
if ( !this.lastPositionAbs ) {
|
||
this.lastPositionAbs = this.positionAbs;
|
||
}
|
||
|
||
//Do scrolling
|
||
if ( this.options.scroll ) {
|
||
if ( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
this.scrollParent[ 0 ].tagName !== "HTML" ) {
|
||
|
||
if ( ( this.overflowOffset.top + this.scrollParent[ 0 ].offsetHeight ) -
|
||
event.pageY < o.scrollSensitivity ) {
|
||
this.scrollParent[ 0 ].scrollTop =
|
||
scrolled = this.scrollParent[ 0 ].scrollTop + o.scrollSpeed;
|
||
} else if ( event.pageY - this.overflowOffset.top < o.scrollSensitivity ) {
|
||
this.scrollParent[ 0 ].scrollTop =
|
||
scrolled = this.scrollParent[ 0 ].scrollTop - o.scrollSpeed;
|
||
}
|
||
|
||
if ( ( this.overflowOffset.left + this.scrollParent[ 0 ].offsetWidth ) -
|
||
event.pageX < o.scrollSensitivity ) {
|
||
this.scrollParent[ 0 ].scrollLeft = scrolled =
|
||
this.scrollParent[ 0 ].scrollLeft + o.scrollSpeed;
|
||
} else if ( event.pageX - this.overflowOffset.left < o.scrollSensitivity ) {
|
||
this.scrollParent[ 0 ].scrollLeft = scrolled =
|
||
this.scrollParent[ 0 ].scrollLeft - o.scrollSpeed;
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( event.pageY - this.document.scrollTop() < o.scrollSensitivity ) {
|
||
scrolled = this.document.scrollTop( this.document.scrollTop() - o.scrollSpeed );
|
||
} else if ( this.window.height() - ( event.pageY - this.document.scrollTop() ) <
|
||
o.scrollSensitivity ) {
|
||
scrolled = this.document.scrollTop( this.document.scrollTop() + o.scrollSpeed );
|
||
}
|
||
|
||
if ( event.pageX - this.document.scrollLeft() < o.scrollSensitivity ) {
|
||
scrolled = this.document.scrollLeft(
|
||
this.document.scrollLeft() - o.scrollSpeed
|
||
);
|
||
} else if ( this.window.width() - ( event.pageX - this.document.scrollLeft() ) <
|
||
o.scrollSensitivity ) {
|
||
scrolled = this.document.scrollLeft(
|
||
this.document.scrollLeft() + o.scrollSpeed
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
|
||
$.ui.ddmanager.prepareOffsets( this, event );
|
||
}
|
||
}
|
||
|
||
//Regenerate the absolute position used for position checks
|
||
this.positionAbs = this._convertPositionTo( "absolute" );
|
||
|
||
//Set the helper position
|
||
if ( !this.options.axis || this.options.axis !== "y" ) {
|
||
this.helper[ 0 ].style.left = this.position.left + "px";
|
||
}
|
||
if ( !this.options.axis || this.options.axis !== "x" ) {
|
||
this.helper[ 0 ].style.top = this.position.top + "px";
|
||
}
|
||
|
||
//Rearrange
|
||
for ( i = this.items.length - 1; i >= 0; i-- ) {
|
||
|
||
//Cache variables and intersection, continue if no intersection
|
||
item = this.items[ i ];
|
||
itemElement = item.item[ 0 ];
|
||
intersection = this._intersectsWithPointer( item );
|
||
if ( !intersection ) {
|
||
continue;
|
||
}
|
||
|
||
// Only put the placeholder inside the current Container, skip all
|
||
// items from other containers. This works because when moving
|
||
// an item from one container to another the
|
||
// currentContainer is switched before the placeholder is moved.
|
||
//
|
||
// Without this, moving items in "sub-sortables" can cause
|
||
// the placeholder to jitter between the outer and inner container.
|
||
if ( item.instance !== this.currentContainer ) {
|
||
continue;
|
||
}
|
||
|
||
// Cannot intersect with itself
|
||
// no useless actions that have been done before
|
||
// no action if the item moved is the parent of the item checked
|
||
if ( itemElement !== this.currentItem[ 0 ] &&
|
||
this.placeholder[ intersection === 1 ? "next" : "prev" ]()[ 0 ] !== itemElement &&
|
||
!$.contains( this.placeholder[ 0 ], itemElement ) &&
|
||
( this.options.type === "semi-dynamic" ?
|
||
!$.contains( this.element[ 0 ], itemElement ) :
|
||
true
|
||
)
|
||
) {
|
||
|
||
this.direction = intersection === 1 ? "down" : "up";
|
||
|
||
if ( this.options.tolerance === "pointer" || this._intersectsWithSides( item ) ) {
|
||
this._rearrange( event, item );
|
||
} else {
|
||
break;
|
||
}
|
||
|
||
this._trigger( "change", event, this._uiHash() );
|
||
break;
|
||
}
|
||
}
|
||
|
||
//Post events to containers
|
||
this._contactContainers( event );
|
||
|
||
//Interconnect with droppables
|
||
if ( $.ui.ddmanager ) {
|
||
$.ui.ddmanager.drag( this, event );
|
||
}
|
||
|
||
//Call callbacks
|
||
this._trigger( "sort", event, this._uiHash() );
|
||
|
||
this.lastPositionAbs = this.positionAbs;
|
||
return false;
|
||
|
||
},
|
||
|
||
_mouseStop: function( event, noPropagation ) {
|
||
|
||
if ( !event ) {
|
||
return;
|
||
}
|
||
|
||
//If we are using droppables, inform the manager about the drop
|
||
if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
|
||
$.ui.ddmanager.drop( this, event );
|
||
}
|
||
|
||
if ( this.options.revert ) {
|
||
var that = this,
|
||
cur = this.placeholder.offset(),
|
||
axis = this.options.axis,
|
||
animation = {};
|
||
|
||
if ( !axis || axis === "x" ) {
|
||
animation.left = cur.left - this.offset.parent.left - this.margins.left +
|
||
( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
|
||
0 :
|
||
this.offsetParent[ 0 ].scrollLeft
|
||
);
|
||
}
|
||
if ( !axis || axis === "y" ) {
|
||
animation.top = cur.top - this.offset.parent.top - this.margins.top +
|
||
( this.offsetParent[ 0 ] === this.document[ 0 ].body ?
|
||
0 :
|
||
this.offsetParent[ 0 ].scrollTop
|
||
);
|
||
}
|
||
this.reverting = true;
|
||
$( this.helper ).animate(
|
||
animation,
|
||
parseInt( this.options.revert, 10 ) || 500,
|
||
function() {
|
||
that._clear( event );
|
||
}
|
||
);
|
||
} else {
|
||
this._clear( event, noPropagation );
|
||
}
|
||
|
||
return false;
|
||
|
||
},
|
||
|
||
cancel: function() {
|
||
|
||
if ( this.dragging ) {
|
||
|
||
this._mouseUp( new $.Event( "mouseup", { target: null } ) );
|
||
|
||
if ( this.options.helper === "original" ) {
|
||
this.currentItem.css( this._storedCSS );
|
||
this._removeClass( this.currentItem, "ui-sortable-helper" );
|
||
} else {
|
||
this.currentItem.show();
|
||
}
|
||
|
||
//Post deactivating events to containers
|
||
for ( var i = this.containers.length - 1; i >= 0; i-- ) {
|
||
this.containers[ i ]._trigger( "deactivate", null, this._uiHash( this ) );
|
||
if ( this.containers[ i ].containerCache.over ) {
|
||
this.containers[ i ]._trigger( "out", null, this._uiHash( this ) );
|
||
this.containers[ i ].containerCache.over = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if ( this.placeholder ) {
|
||
|
||
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
|
||
// it unbinds ALL events from the original node!
|
||
if ( this.placeholder[ 0 ].parentNode ) {
|
||
this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
|
||
}
|
||
if ( this.options.helper !== "original" && this.helper &&
|
||
this.helper[ 0 ].parentNode ) {
|
||
this.helper.remove();
|
||
}
|
||
|
||
$.extend( this, {
|
||
helper: null,
|
||
dragging: false,
|
||
reverting: false,
|
||
_noFinalSort: null
|
||
} );
|
||
|
||
if ( this.domPosition.prev ) {
|
||
$( this.domPosition.prev ).after( this.currentItem );
|
||
} else {
|
||
$( this.domPosition.parent ).prepend( this.currentItem );
|
||
}
|
||
}
|
||
|
||
return this;
|
||
|
||
},
|
||
|
||
serialize: function( o ) {
|
||
|
||
var items = this._getItemsAsjQuery( o && o.connected ),
|
||
str = [];
|
||
o = o || {};
|
||
|
||
$( items ).each( function() {
|
||
var res = ( $( o.item || this ).attr( o.attribute || "id" ) || "" )
|
||
.match( o.expression || ( /(.+)[\-=_](.+)/ ) );
|
||
if ( res ) {
|
||
str.push(
|
||
( o.key || res[ 1 ] + "[]" ) +
|
||
"=" + ( o.key && o.expression ? res[ 1 ] : res[ 2 ] ) );
|
||
}
|
||
} );
|
||
|
||
if ( !str.length && o.key ) {
|
||
str.push( o.key + "=" );
|
||
}
|
||
|
||
return str.join( "&" );
|
||
|
||
},
|
||
|
||
toArray: function( o ) {
|
||
|
||
var items = this._getItemsAsjQuery( o && o.connected ),
|
||
ret = [];
|
||
|
||
o = o || {};
|
||
|
||
items.each( function() {
|
||
ret.push( $( o.item || this ).attr( o.attribute || "id" ) || "" );
|
||
} );
|
||
return ret;
|
||
|
||
},
|
||
|
||
/* Be careful with the following core functions */
|
||
_intersectsWith: function( item ) {
|
||
|
||
var x1 = this.positionAbs.left,
|
||
x2 = x1 + this.helperProportions.width,
|
||
y1 = this.positionAbs.top,
|
||
y2 = y1 + this.helperProportions.height,
|
||
l = item.left,
|
||
r = l + item.width,
|
||
t = item.top,
|
||
b = t + item.height,
|
||
dyClick = this.offset.click.top,
|
||
dxClick = this.offset.click.left,
|
||
isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t &&
|
||
( y1 + dyClick ) < b ),
|
||
isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l &&
|
||
( x1 + dxClick ) < r ),
|
||
isOverElement = isOverElementHeight && isOverElementWidth;
|
||
|
||
if ( this.options.tolerance === "pointer" ||
|
||
this.options.forcePointerForContainers ||
|
||
( this.options.tolerance !== "pointer" &&
|
||
this.helperProportions[ this.floating ? "width" : "height" ] >
|
||
item[ this.floating ? "width" : "height" ] )
|
||
) {
|
||
return isOverElement;
|
||
} else {
|
||
|
||
return ( l < x1 + ( this.helperProportions.width / 2 ) && // Right Half
|
||
x2 - ( this.helperProportions.width / 2 ) < r && // Left Half
|
||
t < y1 + ( this.helperProportions.height / 2 ) && // Bottom Half
|
||
y2 - ( this.helperProportions.height / 2 ) < b ); // Top Half
|
||
|
||
}
|
||
},
|
||
|
||
_intersectsWithPointer: function( item ) {
|
||
var verticalDirection, horizontalDirection,
|
||
isOverElementHeight = ( this.options.axis === "x" ) ||
|
||
this._isOverAxis(
|
||
this.positionAbs.top + this.offset.click.top, item.top, item.height ),
|
||
isOverElementWidth = ( this.options.axis === "y" ) ||
|
||
this._isOverAxis(
|
||
this.positionAbs.left + this.offset.click.left, item.left, item.width ),
|
||
isOverElement = isOverElementHeight && isOverElementWidth;
|
||
|
||
if ( !isOverElement ) {
|
||
return false;
|
||
}
|
||
|
||
verticalDirection = this._getDragVerticalDirection();
|
||
horizontalDirection = this._getDragHorizontalDirection();
|
||
|
||
return this.floating ?
|
||
( ( horizontalDirection === "right" || verticalDirection === "down" ) ? 2 : 1 )
|
||
: ( verticalDirection && ( verticalDirection === "down" ? 2 : 1 ) );
|
||
|
||
},
|
||
|
||
_intersectsWithSides: function( item ) {
|
||
|
||
var isOverBottomHalf = this._isOverAxis( this.positionAbs.top +
|
||
this.offset.click.top, item.top + ( item.height / 2 ), item.height ),
|
||
isOverRightHalf = this._isOverAxis( this.positionAbs.left +
|
||
this.offset.click.left, item.left + ( item.width / 2 ), item.width ),
|
||
verticalDirection = this._getDragVerticalDirection(),
|
||
horizontalDirection = this._getDragHorizontalDirection();
|
||
|
||
if ( this.floating && horizontalDirection ) {
|
||
return ( ( horizontalDirection === "right" && isOverRightHalf ) ||
|
||
( horizontalDirection === "left" && !isOverRightHalf ) );
|
||
} else {
|
||
return verticalDirection && ( ( verticalDirection === "down" && isOverBottomHalf ) ||
|
||
( verticalDirection === "up" && !isOverBottomHalf ) );
|
||
}
|
||
|
||
},
|
||
|
||
_getDragVerticalDirection: function() {
|
||
var delta = this.positionAbs.top - this.lastPositionAbs.top;
|
||
return delta !== 0 && ( delta > 0 ? "down" : "up" );
|
||
},
|
||
|
||
_getDragHorizontalDirection: function() {
|
||
var delta = this.positionAbs.left - this.lastPositionAbs.left;
|
||
return delta !== 0 && ( delta > 0 ? "right" : "left" );
|
||
},
|
||
|
||
refresh: function( event ) {
|
||
this._refreshItems( event );
|
||
this._setHandleClassName();
|
||
this.refreshPositions();
|
||
return this;
|
||
},
|
||
|
||
_connectWith: function() {
|
||
var options = this.options;
|
||
return options.connectWith.constructor === String ?
|
||
[ options.connectWith ] :
|
||
options.connectWith;
|
||
},
|
||
|
||
_getItemsAsjQuery: function( connected ) {
|
||
|
||
var i, j, cur, inst,
|
||
items = [],
|
||
queries = [],
|
||
connectWith = this._connectWith();
|
||
|
||
if ( connectWith && connected ) {
|
||
for ( i = connectWith.length - 1; i >= 0; i-- ) {
|
||
cur = $( connectWith[ i ], this.document[ 0 ] );
|
||
for ( j = cur.length - 1; j >= 0; j-- ) {
|
||
inst = $.data( cur[ j ], this.widgetFullName );
|
||
if ( inst && inst !== this && !inst.options.disabled ) {
|
||
queries.push( [ $.isFunction( inst.options.items ) ?
|
||
inst.options.items.call( inst.element ) :
|
||
$( inst.options.items, inst.element )
|
||
.not( ".ui-sortable-helper" )
|
||
.not( ".ui-sortable-placeholder" ), inst ] );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
queries.push( [ $.isFunction( this.options.items ) ?
|
||
this.options.items
|
||
.call( this.element, null, { options: this.options, item: this.currentItem } ) :
|
||
$( this.options.items, this.element )
|
||
.not( ".ui-sortable-helper" )
|
||
.not( ".ui-sortable-placeholder" ), this ] );
|
||
|
||
function addItems() {
|
||
items.push( this );
|
||
}
|
||
for ( i = queries.length - 1; i >= 0; i-- ) {
|
||
queries[ i ][ 0 ].each( addItems );
|
||
}
|
||
|
||
return $( items );
|
||
|
||
},
|
||
|
||
_removeCurrentsFromItems: function() {
|
||
|
||
var list = this.currentItem.find( ":data(" + this.widgetName + "-item)" );
|
||
|
||
this.items = $.grep( this.items, function( item ) {
|
||
for ( var j = 0; j < list.length; j++ ) {
|
||
if ( list[ j ] === item.item[ 0 ] ) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
} );
|
||
|
||
},
|
||
|
||
_refreshItems: function( event ) {
|
||
|
||
this.items = [];
|
||
this.containers = [ this ];
|
||
|
||
var i, j, cur, inst, targetData, _queries, item, queriesLength,
|
||
items = this.items,
|
||
queries = [ [ $.isFunction( this.options.items ) ?
|
||
this.options.items.call( this.element[ 0 ], event, { item: this.currentItem } ) :
|
||
$( this.options.items, this.element ), this ] ],
|
||
connectWith = this._connectWith();
|
||
|
||
//Shouldn't be run the first time through due to massive slow-down
|
||
if ( connectWith && this.ready ) {
|
||
for ( i = connectWith.length - 1; i >= 0; i-- ) {
|
||
cur = $( connectWith[ i ], this.document[ 0 ] );
|
||
for ( j = cur.length - 1; j >= 0; j-- ) {
|
||
inst = $.data( cur[ j ], this.widgetFullName );
|
||
if ( inst && inst !== this && !inst.options.disabled ) {
|
||
queries.push( [ $.isFunction( inst.options.items ) ?
|
||
inst.options.items
|
||
.call( inst.element[ 0 ], event, { item: this.currentItem } ) :
|
||
$( inst.options.items, inst.element ), inst ] );
|
||
this.containers.push( inst );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for ( i = queries.length - 1; i >= 0; i-- ) {
|
||
targetData = queries[ i ][ 1 ];
|
||
_queries = queries[ i ][ 0 ];
|
||
|
||
for ( j = 0, queriesLength = _queries.length; j < queriesLength; j++ ) {
|
||
item = $( _queries[ j ] );
|
||
|
||
// Data for target checking (mouse manager)
|
||
item.data( this.widgetName + "-item", targetData );
|
||
|
||
items.push( {
|
||
item: item,
|
||
instance: targetData,
|
||
width: 0, height: 0,
|
||
left: 0, top: 0
|
||
} );
|
||
}
|
||
}
|
||
|
||
},
|
||
|
||
refreshPositions: function( fast ) {
|
||
|
||
// Determine whether items are being displayed horizontally
|
||
this.floating = this.items.length ?
|
||
this.options.axis === "x" || this._isFloating( this.items[ 0 ].item ) :
|
||
false;
|
||
|
||
//This has to be redone because due to the item being moved out/into the offsetParent,
|
||
// the offsetParent's position will change
|
||
if ( this.offsetParent && this.helper ) {
|
||
this.offset.parent = this._getParentOffset();
|
||
}
|
||
|
||
var i, item, t, p;
|
||
|
||
for ( i = this.items.length - 1; i >= 0; i-- ) {
|
||
item = this.items[ i ];
|
||
|
||
//We ignore calculating positions of all connected containers when we're not over them
|
||
if ( item.instance !== this.currentContainer && this.currentContainer &&
|
||
item.item[ 0 ] !== this.currentItem[ 0 ] ) {
|
||
continue;
|
||
}
|
||
|
||
t = this.options.toleranceElement ?
|
||
$( this.options.toleranceElement, item.item ) :
|
||
item.item;
|
||
|
||
if ( !fast ) {
|
||
item.width = t.outerWidth();
|
||
item.height = t.outerHeight();
|
||
}
|
||
|
||
p = t.offset();
|
||
item.left = p.left;
|
||
item.top = p.top;
|
||
}
|
||
|
||
if ( this.options.custom && this.options.custom.refreshContainers ) {
|
||
this.options.custom.refreshContainers.call( this );
|
||
} else {
|
||
for ( i = this.containers.length - 1; i >= 0; i-- ) {
|
||
p = this.containers[ i ].element.offset();
|
||
this.containers[ i ].containerCache.left = p.left;
|
||
this.containers[ i ].containerCache.top = p.top;
|
||
this.containers[ i ].containerCache.width =
|
||
this.containers[ i ].element.outerWidth();
|
||
this.containers[ i ].containerCache.height =
|
||
this.containers[ i ].element.outerHeight();
|
||
}
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
_createPlaceholder: function( that ) {
|
||
that = that || this;
|
||
var className,
|
||
o = that.options;
|
||
|
||
if ( !o.placeholder || o.placeholder.constructor === String ) {
|
||
className = o.placeholder;
|
||
o.placeholder = {
|
||
element: function() {
|
||
|
||
var nodeName = that.currentItem[ 0 ].nodeName.toLowerCase(),
|
||
element = $( "<" + nodeName + ">", that.document[ 0 ] );
|
||
|
||
that._addClass( element, "ui-sortable-placeholder",
|
||
className || that.currentItem[ 0 ].className )
|
||
._removeClass( element, "ui-sortable-helper" );
|
||
|
||
if ( nodeName === "tbody" ) {
|
||
that._createTrPlaceholder(
|
||
that.currentItem.find( "tr" ).eq( 0 ),
|
||
$( "<tr>", that.document[ 0 ] ).appendTo( element )
|
||
);
|
||
} else if ( nodeName === "tr" ) {
|
||
that._createTrPlaceholder( that.currentItem, element );
|
||
} else if ( nodeName === "img" ) {
|
||
element.attr( "src", that.currentItem.attr( "src" ) );
|
||
}
|
||
|
||
if ( !className ) {
|
||
element.css( "visibility", "hidden" );
|
||
}
|
||
|
||
return element;
|
||
},
|
||
update: function( container, p ) {
|
||
|
||
// 1. If a className is set as 'placeholder option, we don't force sizes -
|
||
// the class is responsible for that
|
||
// 2. The option 'forcePlaceholderSize can be enabled to force it even if a
|
||
// class name is specified
|
||
if ( className && !o.forcePlaceholderSize ) {
|
||
return;
|
||
}
|
||
|
||
//If the element doesn't have a actual height by itself (without styles coming
|
||
// from a stylesheet), it receives the inline height from the dragged item
|
||
if ( !p.height() ) {
|
||
p.height(
|
||
that.currentItem.innerHeight() -
|
||
parseInt( that.currentItem.css( "paddingTop" ) || 0, 10 ) -
|
||
parseInt( that.currentItem.css( "paddingBottom" ) || 0, 10 ) );
|
||
}
|
||
if ( !p.width() ) {
|
||
p.width(
|
||
that.currentItem.innerWidth() -
|
||
parseInt( that.currentItem.css( "paddingLeft" ) || 0, 10 ) -
|
||
parseInt( that.currentItem.css( "paddingRight" ) || 0, 10 ) );
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
//Create the placeholder
|
||
that.placeholder = $( o.placeholder.element.call( that.element, that.currentItem ) );
|
||
|
||
//Append it after the actual current item
|
||
that.currentItem.after( that.placeholder );
|
||
|
||
//Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
|
||
o.placeholder.update( that, that.placeholder );
|
||
|
||
},
|
||
|
||
_createTrPlaceholder: function( sourceTr, targetTr ) {
|
||
var that = this;
|
||
|
||
sourceTr.children().each( function() {
|
||
$( "<td> </td>", that.document[ 0 ] )
|
||
.attr( "colspan", $( this ).attr( "colspan" ) || 1 )
|
||
.appendTo( targetTr );
|
||
} );
|
||
},
|
||
|
||
_contactContainers: function( event ) {
|
||
var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, cur, nearBottom,
|
||
floating, axis,
|
||
innermostContainer = null,
|
||
innermostIndex = null;
|
||
|
||
// Get innermost container that intersects with item
|
||
for ( i = this.containers.length - 1; i >= 0; i-- ) {
|
||
|
||
// Never consider a container that's located within the item itself
|
||
if ( $.contains( this.currentItem[ 0 ], this.containers[ i ].element[ 0 ] ) ) {
|
||
continue;
|
||
}
|
||
|
||
if ( this._intersectsWith( this.containers[ i ].containerCache ) ) {
|
||
|
||
// If we've already found a container and it's more "inner" than this, then continue
|
||
if ( innermostContainer &&
|
||
$.contains(
|
||
this.containers[ i ].element[ 0 ],
|
||
innermostContainer.element[ 0 ] ) ) {
|
||
continue;
|
||
}
|
||
|
||
innermostContainer = this.containers[ i ];
|
||
innermostIndex = i;
|
||
|
||
} else {
|
||
|
||
// container doesn't intersect. trigger "out" event if necessary
|
||
if ( this.containers[ i ].containerCache.over ) {
|
||
this.containers[ i ]._trigger( "out", event, this._uiHash( this ) );
|
||
this.containers[ i ].containerCache.over = 0;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
// If no intersecting containers found, return
|
||
if ( !innermostContainer ) {
|
||
return;
|
||
}
|
||
|
||
// Move the item into the container if it's not there already
|
||
if ( this.containers.length === 1 ) {
|
||
if ( !this.containers[ innermostIndex ].containerCache.over ) {
|
||
this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
|
||
this.containers[ innermostIndex ].containerCache.over = 1;
|
||
}
|
||
} else {
|
||
|
||
// When entering a new container, we will find the item with the least distance and
|
||
// append our item near it
|
||
dist = 10000;
|
||
itemWithLeastDistance = null;
|
||
floating = innermostContainer.floating || this._isFloating( this.currentItem );
|
||
posProperty = floating ? "left" : "top";
|
||
sizeProperty = floating ? "width" : "height";
|
||
axis = floating ? "pageX" : "pageY";
|
||
|
||
for ( j = this.items.length - 1; j >= 0; j-- ) {
|
||
if ( !$.contains(
|
||
this.containers[ innermostIndex ].element[ 0 ], this.items[ j ].item[ 0 ] )
|
||
) {
|
||
continue;
|
||
}
|
||
if ( this.items[ j ].item[ 0 ] === this.currentItem[ 0 ] ) {
|
||
continue;
|
||
}
|
||
|
||
cur = this.items[ j ].item.offset()[ posProperty ];
|
||
nearBottom = false;
|
||
if ( event[ axis ] - cur > this.items[ j ][ sizeProperty ] / 2 ) {
|
||
nearBottom = true;
|
||
}
|
||
|
||
if ( Math.abs( event[ axis ] - cur ) < dist ) {
|
||
dist = Math.abs( event[ axis ] - cur );
|
||
itemWithLeastDistance = this.items[ j ];
|
||
this.direction = nearBottom ? "up" : "down";
|
||
}
|
||
}
|
||
|
||
//Check if dropOnEmpty is enabled
|
||
if ( !itemWithLeastDistance && !this.options.dropOnEmpty ) {
|
||
return;
|
||
}
|
||
|
||
if ( this.currentContainer === this.containers[ innermostIndex ] ) {
|
||
if ( !this.currentContainer.containerCache.over ) {
|
||
this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash() );
|
||
this.currentContainer.containerCache.over = 1;
|
||
}
|
||
return;
|
||
}
|
||
|
||
itemWithLeastDistance ?
|
||
this._rearrange( event, itemWithLeastDistance, null, true ) :
|
||
this._rearrange( event, null, this.containers[ innermostIndex ].element, true );
|
||
this._trigger( "change", event, this._uiHash() );
|
||
this.containers[ innermostIndex ]._trigger( "change", event, this._uiHash( this ) );
|
||
this.currentContainer = this.containers[ innermostIndex ];
|
||
|
||
//Update the placeholder
|
||
this.options.placeholder.update( this.currentContainer, this.placeholder );
|
||
|
||
this.containers[ innermostIndex ]._trigger( "over", event, this._uiHash( this ) );
|
||
this.containers[ innermostIndex ].containerCache.over = 1;
|
||
}
|
||
|
||
},
|
||
|
||
_createHelper: function( event ) {
|
||
|
||
var o = this.options,
|
||
helper = $.isFunction( o.helper ) ?
|
||
$( o.helper.apply( this.element[ 0 ], [ event, this.currentItem ] ) ) :
|
||
( o.helper === "clone" ? this.currentItem.clone() : this.currentItem );
|
||
|
||
//Add the helper to the DOM if that didn't happen already
|
||
if ( !helper.parents( "body" ).length ) {
|
||
$( o.appendTo !== "parent" ?
|
||
o.appendTo :
|
||
this.currentItem[ 0 ].parentNode )[ 0 ].appendChild( helper[ 0 ] );
|
||
}
|
||
|
||
if ( helper[ 0 ] === this.currentItem[ 0 ] ) {
|
||
this._storedCSS = {
|
||
width: this.currentItem[ 0 ].style.width,
|
||
height: this.currentItem[ 0 ].style.height,
|
||
position: this.currentItem.css( "position" ),
|
||
top: this.currentItem.css( "top" ),
|
||
left: this.currentItem.css( "left" )
|
||
};
|
||
}
|
||
|
||
if ( !helper[ 0 ].style.width || o.forceHelperSize ) {
|
||
helper.width( this.currentItem.width() );
|
||
}
|
||
if ( !helper[ 0 ].style.height || o.forceHelperSize ) {
|
||
helper.height( this.currentItem.height() );
|
||
}
|
||
|
||
return helper;
|
||
|
||
},
|
||
|
||
_adjustOffsetFromHelper: function( obj ) {
|
||
if ( typeof obj === "string" ) {
|
||
obj = obj.split( " " );
|
||
}
|
||
if ( $.isArray( obj ) ) {
|
||
obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
|
||
}
|
||
if ( "left" in obj ) {
|
||
this.offset.click.left = obj.left + this.margins.left;
|
||
}
|
||
if ( "right" in obj ) {
|
||
this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
|
||
}
|
||
if ( "top" in obj ) {
|
||
this.offset.click.top = obj.top + this.margins.top;
|
||
}
|
||
if ( "bottom" in obj ) {
|
||
this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
|
||
}
|
||
},
|
||
|
||
_getParentOffset: function() {
|
||
|
||
//Get the offsetParent and cache its position
|
||
this.offsetParent = this.helper.offsetParent();
|
||
var po = this.offsetParent.offset();
|
||
|
||
// This is a special case where we need to modify a offset calculated on start, since the
|
||
// following happened:
|
||
// 1. The position of the helper is absolute, so it's position is calculated based on the
|
||
// next positioned parent
|
||
// 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
|
||
// the document, which means that the scroll is included in the initial calculation of the
|
||
// offset of the parent, and never recalculated upon drag
|
||
if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
|
||
po.left += this.scrollParent.scrollLeft();
|
||
po.top += this.scrollParent.scrollTop();
|
||
}
|
||
|
||
// This needs to be actually done for all browsers, since pageX/pageY includes this
|
||
// information with an ugly IE fix
|
||
if ( this.offsetParent[ 0 ] === this.document[ 0 ].body ||
|
||
( this.offsetParent[ 0 ].tagName &&
|
||
this.offsetParent[ 0 ].tagName.toLowerCase() === "html" && $.ui.ie ) ) {
|
||
po = { top: 0, left: 0 };
|
||
}
|
||
|
||
return {
|
||
top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
|
||
left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
|
||
};
|
||
|
||
},
|
||
|
||
_getRelativeOffset: function() {
|
||
|
||
if ( this.cssPosition === "relative" ) {
|
||
var p = this.currentItem.position();
|
||
return {
|
||
top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
|
||
this.scrollParent.scrollTop(),
|
||
left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
|
||
this.scrollParent.scrollLeft()
|
||
};
|
||
} else {
|
||
return { top: 0, left: 0 };
|
||
}
|
||
|
||
},
|
||
|
||
_cacheMargins: function() {
|
||
this.margins = {
|
||
left: ( parseInt( this.currentItem.css( "marginLeft" ), 10 ) || 0 ),
|
||
top: ( parseInt( this.currentItem.css( "marginTop" ), 10 ) || 0 )
|
||
};
|
||
},
|
||
|
||
_cacheHelperProportions: function() {
|
||
this.helperProportions = {
|
||
width: this.helper.outerWidth(),
|
||
height: this.helper.outerHeight()
|
||
};
|
||
},
|
||
|
||
_setContainment: function() {
|
||
|
||
var ce, co, over,
|
||
o = this.options;
|
||
if ( o.containment === "parent" ) {
|
||
o.containment = this.helper[ 0 ].parentNode;
|
||
}
|
||
if ( o.containment === "document" || o.containment === "window" ) {
|
||
this.containment = [
|
||
0 - this.offset.relative.left - this.offset.parent.left,
|
||
0 - this.offset.relative.top - this.offset.parent.top,
|
||
o.containment === "document" ?
|
||
this.document.width() :
|
||
this.window.width() - this.helperProportions.width - this.margins.left,
|
||
( o.containment === "document" ?
|
||
( this.document.height() || document.body.parentNode.scrollHeight ) :
|
||
this.window.height() || this.document[ 0 ].body.parentNode.scrollHeight
|
||
) - this.helperProportions.height - this.margins.top
|
||
];
|
||
}
|
||
|
||
if ( !( /^(document|window|parent)$/ ).test( o.containment ) ) {
|
||
ce = $( o.containment )[ 0 ];
|
||
co = $( o.containment ).offset();
|
||
over = ( $( ce ).css( "overflow" ) !== "hidden" );
|
||
|
||
this.containment = [
|
||
co.left + ( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) +
|
||
( parseInt( $( ce ).css( "paddingLeft" ), 10 ) || 0 ) - this.margins.left,
|
||
co.top + ( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) +
|
||
( parseInt( $( ce ).css( "paddingTop" ), 10 ) || 0 ) - this.margins.top,
|
||
co.left + ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
|
||
( parseInt( $( ce ).css( "borderLeftWidth" ), 10 ) || 0 ) -
|
||
( parseInt( $( ce ).css( "paddingRight" ), 10 ) || 0 ) -
|
||
this.helperProportions.width - this.margins.left,
|
||
co.top + ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
|
||
( parseInt( $( ce ).css( "borderTopWidth" ), 10 ) || 0 ) -
|
||
( parseInt( $( ce ).css( "paddingBottom" ), 10 ) || 0 ) -
|
||
this.helperProportions.height - this.margins.top
|
||
];
|
||
}
|
||
|
||
},
|
||
|
||
_convertPositionTo: function( d, pos ) {
|
||
|
||
if ( !pos ) {
|
||
pos = this.position;
|
||
}
|
||
var mod = d === "absolute" ? 1 : -1,
|
||
scroll = this.cssPosition === "absolute" &&
|
||
!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
|
||
this.offsetParent :
|
||
this.scrollParent,
|
||
scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
|
||
|
||
return {
|
||
top: (
|
||
|
||
// The absolute mouse position
|
||
pos.top +
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.top * mod +
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.top * mod -
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.scrollParent.scrollTop() :
|
||
( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod )
|
||
),
|
||
left: (
|
||
|
||
// The absolute mouse position
|
||
pos.left +
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.left * mod +
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.left * mod -
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 :
|
||
scroll.scrollLeft() ) * mod )
|
||
)
|
||
};
|
||
|
||
},
|
||
|
||
_generatePosition: function( event ) {
|
||
|
||
var top, left,
|
||
o = this.options,
|
||
pageX = event.pageX,
|
||
pageY = event.pageY,
|
||
scroll = this.cssPosition === "absolute" &&
|
||
!( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
$.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ?
|
||
this.offsetParent :
|
||
this.scrollParent,
|
||
scrollIsRootNode = ( /(html|body)/i ).test( scroll[ 0 ].tagName );
|
||
|
||
// This is another very weird special case that only happens for relative elements:
|
||
// 1. If the css position is relative
|
||
// 2. and the scroll parent is the document or similar to the offset parent
|
||
// we have to refresh the relative offset during the scroll so there are no jumps
|
||
if ( this.cssPosition === "relative" && !( this.scrollParent[ 0 ] !== this.document[ 0 ] &&
|
||
this.scrollParent[ 0 ] !== this.offsetParent[ 0 ] ) ) {
|
||
this.offset.relative = this._getRelativeOffset();
|
||
}
|
||
|
||
/*
|
||
* - Position constraining -
|
||
* Constrain the position to a mix of grid, containment.
|
||
*/
|
||
|
||
if ( this.originalPosition ) { //If we are not dragging yet, we won't check for options
|
||
|
||
if ( this.containment ) {
|
||
if ( event.pageX - this.offset.click.left < this.containment[ 0 ] ) {
|
||
pageX = this.containment[ 0 ] + this.offset.click.left;
|
||
}
|
||
if ( event.pageY - this.offset.click.top < this.containment[ 1 ] ) {
|
||
pageY = this.containment[ 1 ] + this.offset.click.top;
|
||
}
|
||
if ( event.pageX - this.offset.click.left > this.containment[ 2 ] ) {
|
||
pageX = this.containment[ 2 ] + this.offset.click.left;
|
||
}
|
||
if ( event.pageY - this.offset.click.top > this.containment[ 3 ] ) {
|
||
pageY = this.containment[ 3 ] + this.offset.click.top;
|
||
}
|
||
}
|
||
|
||
if ( o.grid ) {
|
||
top = this.originalPageY + Math.round( ( pageY - this.originalPageY ) /
|
||
o.grid[ 1 ] ) * o.grid[ 1 ];
|
||
pageY = this.containment ?
|
||
( ( top - this.offset.click.top >= this.containment[ 1 ] &&
|
||
top - this.offset.click.top <= this.containment[ 3 ] ) ?
|
||
top :
|
||
( ( top - this.offset.click.top >= this.containment[ 1 ] ) ?
|
||
top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) :
|
||
top;
|
||
|
||
left = this.originalPageX + Math.round( ( pageX - this.originalPageX ) /
|
||
o.grid[ 0 ] ) * o.grid[ 0 ];
|
||
pageX = this.containment ?
|
||
( ( left - this.offset.click.left >= this.containment[ 0 ] &&
|
||
left - this.offset.click.left <= this.containment[ 2 ] ) ?
|
||
left :
|
||
( ( left - this.offset.click.left >= this.containment[ 0 ] ) ?
|
||
left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) :
|
||
left;
|
||
}
|
||
|
||
}
|
||
|
||
return {
|
||
top: (
|
||
|
||
// The absolute mouse position
|
||
pageY -
|
||
|
||
// Click offset (relative to the element)
|
||
this.offset.click.top -
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.top -
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.top +
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.scrollParent.scrollTop() :
|
||
( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) )
|
||
),
|
||
left: (
|
||
|
||
// The absolute mouse position
|
||
pageX -
|
||
|
||
// Click offset (relative to the element)
|
||
this.offset.click.left -
|
||
|
||
// Only for relative positioned nodes: Relative offset from element to offset parent
|
||
this.offset.relative.left -
|
||
|
||
// The offsetParent's offset without borders (offset + border)
|
||
this.offset.parent.left +
|
||
( ( this.cssPosition === "fixed" ?
|
||
-this.scrollParent.scrollLeft() :
|
||
scrollIsRootNode ? 0 : scroll.scrollLeft() ) )
|
||
)
|
||
};
|
||
|
||
},
|
||
|
||
_rearrange: function( event, i, a, hardRefresh ) {
|
||
|
||
a ? a[ 0 ].appendChild( this.placeholder[ 0 ] ) :
|
||
i.item[ 0 ].parentNode.insertBefore( this.placeholder[ 0 ],
|
||
( this.direction === "down" ? i.item[ 0 ] : i.item[ 0 ].nextSibling ) );
|
||
|
||
//Various things done here to improve the performance:
|
||
// 1. we create a setTimeout, that calls refreshPositions
|
||
// 2. on the instance, we have a counter variable, that get's higher after every append
|
||
// 3. on the local scope, we copy the counter variable, and check in the timeout,
|
||
// if it's still the same
|
||
// 4. this lets only the last addition to the timeout stack through
|
||
this.counter = this.counter ? ++this.counter : 1;
|
||
var counter = this.counter;
|
||
|
||
this._delay( function() {
|
||
if ( counter === this.counter ) {
|
||
|
||
//Precompute after each DOM insertion, NOT on mousemove
|
||
this.refreshPositions( !hardRefresh );
|
||
}
|
||
} );
|
||
|
||
},
|
||
|
||
_clear: function( event, noPropagation ) {
|
||
|
||
this.reverting = false;
|
||
|
||
// We delay all events that have to be triggered to after the point where the placeholder
|
||
// has been removed and everything else normalized again
|
||
var i,
|
||
delayedTriggers = [];
|
||
|
||
// We first have to update the dom position of the actual currentItem
|
||
// Note: don't do it if the current item is already removed (by a user), or it gets
|
||
// reappended (see #4088)
|
||
if ( !this._noFinalSort && this.currentItem.parent().length ) {
|
||
this.placeholder.before( this.currentItem );
|
||
}
|
||
this._noFinalSort = null;
|
||
|
||
if ( this.helper[ 0 ] === this.currentItem[ 0 ] ) {
|
||
for ( i in this._storedCSS ) {
|
||
if ( this._storedCSS[ i ] === "auto" || this._storedCSS[ i ] === "static" ) {
|
||
this._storedCSS[ i ] = "";
|
||
}
|
||
}
|
||
this.currentItem.css( this._storedCSS );
|
||
this._removeClass( this.currentItem, "ui-sortable-helper" );
|
||
} else {
|
||
this.currentItem.show();
|
||
}
|
||
|
||
if ( this.fromOutside && !noPropagation ) {
|
||
delayedTriggers.push( function( event ) {
|
||
this._trigger( "receive", event, this._uiHash( this.fromOutside ) );
|
||
} );
|
||
}
|
||
if ( ( this.fromOutside ||
|
||
this.domPosition.prev !==
|
||
this.currentItem.prev().not( ".ui-sortable-helper" )[ 0 ] ||
|
||
this.domPosition.parent !== this.currentItem.parent()[ 0 ] ) && !noPropagation ) {
|
||
|
||
// Trigger update callback if the DOM position has changed
|
||
delayedTriggers.push( function( event ) {
|
||
this._trigger( "update", event, this._uiHash() );
|
||
} );
|
||
}
|
||
|
||
// Check if the items Container has Changed and trigger appropriate
|
||
// events.
|
||
if ( this !== this.currentContainer ) {
|
||
if ( !noPropagation ) {
|
||
delayedTriggers.push( function( event ) {
|
||
this._trigger( "remove", event, this._uiHash() );
|
||
} );
|
||
delayedTriggers.push( ( function( c ) {
|
||
return function( event ) {
|
||
c._trigger( "receive", event, this._uiHash( this ) );
|
||
};
|
||
} ).call( this, this.currentContainer ) );
|
||
delayedTriggers.push( ( function( c ) {
|
||
return function( event ) {
|
||
c._trigger( "update", event, this._uiHash( this ) );
|
||
};
|
||
} ).call( this, this.currentContainer ) );
|
||
}
|
||
}
|
||
|
||
//Post events to containers
|
||
function delayEvent( type, instance, container ) {
|
||
return function( event ) {
|
||
container._trigger( type, event, instance._uiHash( instance ) );
|
||
};
|
||
}
|
||
for ( i = this.containers.length - 1; i >= 0; i-- ) {
|
||
if ( !noPropagation ) {
|
||
delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
|
||
}
|
||
if ( this.containers[ i ].containerCache.over ) {
|
||
delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
|
||
this.containers[ i ].containerCache.over = 0;
|
||
}
|
||
}
|
||
|
||
//Do what was originally in plugins
|
||
if ( this.storedCursor ) {
|
||
this.document.find( "body" ).css( "cursor", this.storedCursor );
|
||
this.storedStylesheet.remove();
|
||
}
|
||
if ( this._storedOpacity ) {
|
||
this.helper.css( "opacity", this._storedOpacity );
|
||
}
|
||
if ( this._storedZIndex ) {
|
||
this.helper.css( "zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex );
|
||
}
|
||
|
||
this.dragging = false;
|
||
|
||
if ( !noPropagation ) {
|
||
this._trigger( "beforeStop", event, this._uiHash() );
|
||
}
|
||
|
||
//$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately,
|
||
// it unbinds ALL events from the original node!
|
||
this.placeholder[ 0 ].parentNode.removeChild( this.placeholder[ 0 ] );
|
||
|
||
if ( !this.cancelHelperRemoval ) {
|
||
if ( this.helper[ 0 ] !== this.currentItem[ 0 ] ) {
|
||
this.helper.remove();
|
||
}
|
||
this.helper = null;
|
||
}
|
||
|
||
if ( !noPropagation ) {
|
||
for ( i = 0; i < delayedTriggers.length; i++ ) {
|
||
|
||
// Trigger all delayed events
|
||
delayedTriggers[ i ].call( this, event );
|
||
}
|
||
this._trigger( "stop", event, this._uiHash() );
|
||
}
|
||
|
||
this.fromOutside = false;
|
||
return !this.cancelHelperRemoval;
|
||
|
||
},
|
||
|
||
_trigger: function() {
|
||
if ( $.Widget.prototype._trigger.apply( this, arguments ) === false ) {
|
||
this.cancel();
|
||
}
|
||
},
|
||
|
||
_uiHash: function( _inst ) {
|
||
var inst = _inst || this;
|
||
return {
|
||
helper: inst.helper,
|
||
placeholder: inst.placeholder || $( [] ),
|
||
position: inst.position,
|
||
originalPosition: inst.originalPosition,
|
||
offset: inst.positionAbs,
|
||
item: inst.currentItem,
|
||
sender: _inst ? _inst.element : null
|
||
};
|
||
}
|
||
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Accordion 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Accordion
|
||
//>>group: Widgets
|
||
// jscs:disable maximumLineLength
|
||
//>>description: Displays collapsible content panels for presenting information in a limited amount of space.
|
||
// jscs:enable maximumLineLength
|
||
//>>docs: http://api.jqueryui.com/accordion/
|
||
//>>demos: http://jqueryui.com/accordion/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/accordion.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
var widgetsAccordion = $.widget( "ui.accordion", {
|
||
version: "1.12.1",
|
||
options: {
|
||
active: 0,
|
||
animate: {},
|
||
classes: {
|
||
"ui-accordion-header": "ui-corner-top",
|
||
"ui-accordion-header-collapsed": "ui-corner-all",
|
||
"ui-accordion-content": "ui-corner-bottom"
|
||
},
|
||
collapsible: false,
|
||
event: "click",
|
||
header: "> li > :first-child, > :not(li):even",
|
||
heightStyle: "auto",
|
||
icons: {
|
||
activeHeader: "ui-icon-triangle-1-s",
|
||
header: "ui-icon-triangle-1-e"
|
||
},
|
||
|
||
// Callbacks
|
||
activate: null,
|
||
beforeActivate: null
|
||
},
|
||
|
||
hideProps: {
|
||
borderTopWidth: "hide",
|
||
borderBottomWidth: "hide",
|
||
paddingTop: "hide",
|
||
paddingBottom: "hide",
|
||
height: "hide"
|
||
},
|
||
|
||
showProps: {
|
||
borderTopWidth: "show",
|
||
borderBottomWidth: "show",
|
||
paddingTop: "show",
|
||
paddingBottom: "show",
|
||
height: "show"
|
||
},
|
||
|
||
_create: function() {
|
||
var options = this.options;
|
||
|
||
this.prevShow = this.prevHide = $();
|
||
this._addClass( "ui-accordion", "ui-widget ui-helper-reset" );
|
||
this.element.attr( "role", "tablist" );
|
||
|
||
// Don't allow collapsible: false and active: false / null
|
||
if ( !options.collapsible && ( options.active === false || options.active == null ) ) {
|
||
options.active = 0;
|
||
}
|
||
|
||
this._processPanels();
|
||
|
||
// handle negative values
|
||
if ( options.active < 0 ) {
|
||
options.active += this.headers.length;
|
||
}
|
||
this._refresh();
|
||
},
|
||
|
||
_getCreateEventData: function() {
|
||
return {
|
||
header: this.active,
|
||
panel: !this.active.length ? $() : this.active.next()
|
||
};
|
||
},
|
||
|
||
_createIcons: function() {
|
||
var icon, children,
|
||
icons = this.options.icons;
|
||
|
||
if ( icons ) {
|
||
icon = $( "<span>" );
|
||
this._addClass( icon, "ui-accordion-header-icon", "ui-icon " + icons.header );
|
||
icon.prependTo( this.headers );
|
||
children = this.active.children( ".ui-accordion-header-icon" );
|
||
this._removeClass( children, icons.header )
|
||
._addClass( children, null, icons.activeHeader )
|
||
._addClass( this.headers, "ui-accordion-icons" );
|
||
}
|
||
},
|
||
|
||
_destroyIcons: function() {
|
||
this._removeClass( this.headers, "ui-accordion-icons" );
|
||
this.headers.children( ".ui-accordion-header-icon" ).remove();
|
||
},
|
||
|
||
_destroy: function() {
|
||
var contents;
|
||
|
||
// Clean up main element
|
||
this.element.removeAttr( "role" );
|
||
|
||
// Clean up headers
|
||
this.headers
|
||
.removeAttr( "role aria-expanded aria-selected aria-controls tabIndex" )
|
||
.removeUniqueId();
|
||
|
||
this._destroyIcons();
|
||
|
||
// Clean up content panels
|
||
contents = this.headers.next()
|
||
.css( "display", "" )
|
||
.removeAttr( "role aria-hidden aria-labelledby" )
|
||
.removeUniqueId();
|
||
|
||
if ( this.options.heightStyle !== "content" ) {
|
||
contents.css( "height", "" );
|
||
}
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "active" ) {
|
||
|
||
// _activate() will handle invalid values and update this.options
|
||
this._activate( value );
|
||
return;
|
||
}
|
||
|
||
if ( key === "event" ) {
|
||
if ( this.options.event ) {
|
||
this._off( this.headers, this.options.event );
|
||
}
|
||
this._setupEvents( value );
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
// Setting collapsible: false while collapsed; open first panel
|
||
if ( key === "collapsible" && !value && this.options.active === false ) {
|
||
this._activate( 0 );
|
||
}
|
||
|
||
if ( key === "icons" ) {
|
||
this._destroyIcons();
|
||
if ( value ) {
|
||
this._createIcons();
|
||
}
|
||
}
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this.element.attr( "aria-disabled", value );
|
||
|
||
// Support: IE8 Only
|
||
// #5332 / #6059 - opacity doesn't cascade to positioned elements in IE
|
||
// so we need to add the disabled class to the headers and panels
|
||
this._toggleClass( null, "ui-state-disabled", !!value );
|
||
this._toggleClass( this.headers.add( this.headers.next() ), null, "ui-state-disabled",
|
||
!!value );
|
||
},
|
||
|
||
_keydown: function( event ) {
|
||
if ( event.altKey || event.ctrlKey ) {
|
||
return;
|
||
}
|
||
|
||
var keyCode = $.ui.keyCode,
|
||
length = this.headers.length,
|
||
currentIndex = this.headers.index( event.target ),
|
||
toFocus = false;
|
||
|
||
switch ( event.keyCode ) {
|
||
case keyCode.RIGHT:
|
||
case keyCode.DOWN:
|
||
toFocus = this.headers[ ( currentIndex + 1 ) % length ];
|
||
break;
|
||
case keyCode.LEFT:
|
||
case keyCode.UP:
|
||
toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
|
||
break;
|
||
case keyCode.SPACE:
|
||
case keyCode.ENTER:
|
||
this._eventHandler( event );
|
||
break;
|
||
case keyCode.HOME:
|
||
toFocus = this.headers[ 0 ];
|
||
break;
|
||
case keyCode.END:
|
||
toFocus = this.headers[ length - 1 ];
|
||
break;
|
||
}
|
||
|
||
if ( toFocus ) {
|
||
$( event.target ).attr( "tabIndex", -1 );
|
||
$( toFocus ).attr( "tabIndex", 0 );
|
||
$( toFocus ).trigger( "focus" );
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
|
||
_panelKeyDown: function( event ) {
|
||
if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
|
||
$( event.currentTarget ).prev().trigger( "focus" );
|
||
}
|
||
},
|
||
|
||
refresh: function() {
|
||
var options = this.options;
|
||
this._processPanels();
|
||
|
||
// Was collapsed or no panel
|
||
if ( ( options.active === false && options.collapsible === true ) ||
|
||
!this.headers.length ) {
|
||
options.active = false;
|
||
this.active = $();
|
||
|
||
// active false only when collapsible is true
|
||
} else if ( options.active === false ) {
|
||
this._activate( 0 );
|
||
|
||
// was active, but active panel is gone
|
||
} else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
|
||
|
||
// all remaining panel are disabled
|
||
if ( this.headers.length === this.headers.find( ".ui-state-disabled" ).length ) {
|
||
options.active = false;
|
||
this.active = $();
|
||
|
||
// activate previous panel
|
||
} else {
|
||
this._activate( Math.max( 0, options.active - 1 ) );
|
||
}
|
||
|
||
// was active, active panel still exists
|
||
} else {
|
||
|
||
// make sure active index is correct
|
||
options.active = this.headers.index( this.active );
|
||
}
|
||
|
||
this._destroyIcons();
|
||
|
||
this._refresh();
|
||
},
|
||
|
||
_processPanels: function() {
|
||
var prevHeaders = this.headers,
|
||
prevPanels = this.panels;
|
||
|
||
this.headers = this.element.find( this.options.header );
|
||
this._addClass( this.headers, "ui-accordion-header ui-accordion-header-collapsed",
|
||
"ui-state-default" );
|
||
|
||
this.panels = this.headers.next().filter( ":not(.ui-accordion-content-active)" ).hide();
|
||
this._addClass( this.panels, "ui-accordion-content", "ui-helper-reset ui-widget-content" );
|
||
|
||
// Avoid memory leaks (#10056)
|
||
if ( prevPanels ) {
|
||
this._off( prevHeaders.not( this.headers ) );
|
||
this._off( prevPanels.not( this.panels ) );
|
||
}
|
||
},
|
||
|
||
_refresh: function() {
|
||
var maxHeight,
|
||
options = this.options,
|
||
heightStyle = options.heightStyle,
|
||
parent = this.element.parent();
|
||
|
||
this.active = this._findActive( options.active );
|
||
this._addClass( this.active, "ui-accordion-header-active", "ui-state-active" )
|
||
._removeClass( this.active, "ui-accordion-header-collapsed" );
|
||
this._addClass( this.active.next(), "ui-accordion-content-active" );
|
||
this.active.next().show();
|
||
|
||
this.headers
|
||
.attr( "role", "tab" )
|
||
.each( function() {
|
||
var header = $( this ),
|
||
headerId = header.uniqueId().attr( "id" ),
|
||
panel = header.next(),
|
||
panelId = panel.uniqueId().attr( "id" );
|
||
header.attr( "aria-controls", panelId );
|
||
panel.attr( "aria-labelledby", headerId );
|
||
} )
|
||
.next()
|
||
.attr( "role", "tabpanel" );
|
||
|
||
this.headers
|
||
.not( this.active )
|
||
.attr( {
|
||
"aria-selected": "false",
|
||
"aria-expanded": "false",
|
||
tabIndex: -1
|
||
} )
|
||
.next()
|
||
.attr( {
|
||
"aria-hidden": "true"
|
||
} )
|
||
.hide();
|
||
|
||
// Make sure at least one header is in the tab order
|
||
if ( !this.active.length ) {
|
||
this.headers.eq( 0 ).attr( "tabIndex", 0 );
|
||
} else {
|
||
this.active.attr( {
|
||
"aria-selected": "true",
|
||
"aria-expanded": "true",
|
||
tabIndex: 0
|
||
} )
|
||
.next()
|
||
.attr( {
|
||
"aria-hidden": "false"
|
||
} );
|
||
}
|
||
|
||
this._createIcons();
|
||
|
||
this._setupEvents( options.event );
|
||
|
||
if ( heightStyle === "fill" ) {
|
||
maxHeight = parent.height();
|
||
this.element.siblings( ":visible" ).each( function() {
|
||
var elem = $( this ),
|
||
position = elem.css( "position" );
|
||
|
||
if ( position === "absolute" || position === "fixed" ) {
|
||
return;
|
||
}
|
||
maxHeight -= elem.outerHeight( true );
|
||
} );
|
||
|
||
this.headers.each( function() {
|
||
maxHeight -= $( this ).outerHeight( true );
|
||
} );
|
||
|
||
this.headers.next()
|
||
.each( function() {
|
||
$( this ).height( Math.max( 0, maxHeight -
|
||
$( this ).innerHeight() + $( this ).height() ) );
|
||
} )
|
||
.css( "overflow", "auto" );
|
||
} else if ( heightStyle === "auto" ) {
|
||
maxHeight = 0;
|
||
this.headers.next()
|
||
.each( function() {
|
||
var isVisible = $( this ).is( ":visible" );
|
||
if ( !isVisible ) {
|
||
$( this ).show();
|
||
}
|
||
maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
|
||
if ( !isVisible ) {
|
||
$( this ).hide();
|
||
}
|
||
} )
|
||
.height( maxHeight );
|
||
}
|
||
},
|
||
|
||
_activate: function( index ) {
|
||
var active = this._findActive( index )[ 0 ];
|
||
|
||
// Trying to activate the already active panel
|
||
if ( active === this.active[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
// Trying to collapse, simulate a click on the currently active header
|
||
active = active || this.active[ 0 ];
|
||
|
||
this._eventHandler( {
|
||
target: active,
|
||
currentTarget: active,
|
||
preventDefault: $.noop
|
||
} );
|
||
},
|
||
|
||
_findActive: function( selector ) {
|
||
return typeof selector === "number" ? this.headers.eq( selector ) : $();
|
||
},
|
||
|
||
_setupEvents: function( event ) {
|
||
var events = {
|
||
keydown: "_keydown"
|
||
};
|
||
if ( event ) {
|
||
$.each( event.split( " " ), function( index, eventName ) {
|
||
events[ eventName ] = "_eventHandler";
|
||
} );
|
||
}
|
||
|
||
this._off( this.headers.add( this.headers.next() ) );
|
||
this._on( this.headers, events );
|
||
this._on( this.headers.next(), { keydown: "_panelKeyDown" } );
|
||
this._hoverable( this.headers );
|
||
this._focusable( this.headers );
|
||
},
|
||
|
||
_eventHandler: function( event ) {
|
||
var activeChildren, clickedChildren,
|
||
options = this.options,
|
||
active = this.active,
|
||
clicked = $( event.currentTarget ),
|
||
clickedIsActive = clicked[ 0 ] === active[ 0 ],
|
||
collapsing = clickedIsActive && options.collapsible,
|
||
toShow = collapsing ? $() : clicked.next(),
|
||
toHide = active.next(),
|
||
eventData = {
|
||
oldHeader: active,
|
||
oldPanel: toHide,
|
||
newHeader: collapsing ? $() : clicked,
|
||
newPanel: toShow
|
||
};
|
||
|
||
event.preventDefault();
|
||
|
||
if (
|
||
|
||
// click on active header, but not collapsible
|
||
( clickedIsActive && !options.collapsible ) ||
|
||
|
||
// allow canceling activation
|
||
( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
|
||
return;
|
||
}
|
||
|
||
options.active = collapsing ? false : this.headers.index( clicked );
|
||
|
||
// When the call to ._toggle() comes after the class changes
|
||
// it causes a very odd bug in IE 8 (see #6720)
|
||
this.active = clickedIsActive ? $() : clicked;
|
||
this._toggle( eventData );
|
||
|
||
// Switch classes
|
||
// corner classes on the previously active header stay after the animation
|
||
this._removeClass( active, "ui-accordion-header-active", "ui-state-active" );
|
||
if ( options.icons ) {
|
||
activeChildren = active.children( ".ui-accordion-header-icon" );
|
||
this._removeClass( activeChildren, null, options.icons.activeHeader )
|
||
._addClass( activeChildren, null, options.icons.header );
|
||
}
|
||
|
||
if ( !clickedIsActive ) {
|
||
this._removeClass( clicked, "ui-accordion-header-collapsed" )
|
||
._addClass( clicked, "ui-accordion-header-active", "ui-state-active" );
|
||
if ( options.icons ) {
|
||
clickedChildren = clicked.children( ".ui-accordion-header-icon" );
|
||
this._removeClass( clickedChildren, null, options.icons.header )
|
||
._addClass( clickedChildren, null, options.icons.activeHeader );
|
||
}
|
||
|
||
this._addClass( clicked.next(), "ui-accordion-content-active" );
|
||
}
|
||
},
|
||
|
||
_toggle: function( data ) {
|
||
var toShow = data.newPanel,
|
||
toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
|
||
|
||
// Handle activating a panel during the animation for another activation
|
||
this.prevShow.add( this.prevHide ).stop( true, true );
|
||
this.prevShow = toShow;
|
||
this.prevHide = toHide;
|
||
|
||
if ( this.options.animate ) {
|
||
this._animate( toShow, toHide, data );
|
||
} else {
|
||
toHide.hide();
|
||
toShow.show();
|
||
this._toggleComplete( data );
|
||
}
|
||
|
||
toHide.attr( {
|
||
"aria-hidden": "true"
|
||
} );
|
||
toHide.prev().attr( {
|
||
"aria-selected": "false",
|
||
"aria-expanded": "false"
|
||
} );
|
||
|
||
// if we're switching panels, remove the old header from the tab order
|
||
// if we're opening from collapsed state, remove the previous header from the tab order
|
||
// if we're collapsing, then keep the collapsing header in the tab order
|
||
if ( toShow.length && toHide.length ) {
|
||
toHide.prev().attr( {
|
||
"tabIndex": -1,
|
||
"aria-expanded": "false"
|
||
} );
|
||
} else if ( toShow.length ) {
|
||
this.headers.filter( function() {
|
||
return parseInt( $( this ).attr( "tabIndex" ), 10 ) === 0;
|
||
} )
|
||
.attr( "tabIndex", -1 );
|
||
}
|
||
|
||
toShow
|
||
.attr( "aria-hidden", "false" )
|
||
.prev()
|
||
.attr( {
|
||
"aria-selected": "true",
|
||
"aria-expanded": "true",
|
||
tabIndex: 0
|
||
} );
|
||
},
|
||
|
||
_animate: function( toShow, toHide, data ) {
|
||
var total, easing, duration,
|
||
that = this,
|
||
adjust = 0,
|
||
boxSizing = toShow.css( "box-sizing" ),
|
||
down = toShow.length &&
|
||
( !toHide.length || ( toShow.index() < toHide.index() ) ),
|
||
animate = this.options.animate || {},
|
||
options = down && animate.down || animate,
|
||
complete = function() {
|
||
that._toggleComplete( data );
|
||
};
|
||
|
||
if ( typeof options === "number" ) {
|
||
duration = options;
|
||
}
|
||
if ( typeof options === "string" ) {
|
||
easing = options;
|
||
}
|
||
|
||
// fall back from options to animation in case of partial down settings
|
||
easing = easing || options.easing || animate.easing;
|
||
duration = duration || options.duration || animate.duration;
|
||
|
||
if ( !toHide.length ) {
|
||
return toShow.animate( this.showProps, duration, easing, complete );
|
||
}
|
||
if ( !toShow.length ) {
|
||
return toHide.animate( this.hideProps, duration, easing, complete );
|
||
}
|
||
|
||
total = toShow.show().outerHeight();
|
||
toHide.animate( this.hideProps, {
|
||
duration: duration,
|
||
easing: easing,
|
||
step: function( now, fx ) {
|
||
fx.now = Math.round( now );
|
||
}
|
||
} );
|
||
toShow
|
||
.hide()
|
||
.animate( this.showProps, {
|
||
duration: duration,
|
||
easing: easing,
|
||
complete: complete,
|
||
step: function( now, fx ) {
|
||
fx.now = Math.round( now );
|
||
if ( fx.prop !== "height" ) {
|
||
if ( boxSizing === "content-box" ) {
|
||
adjust += fx.now;
|
||
}
|
||
} else if ( that.options.heightStyle !== "content" ) {
|
||
fx.now = Math.round( total - toHide.outerHeight() - adjust );
|
||
adjust = 0;
|
||
}
|
||
}
|
||
} );
|
||
},
|
||
|
||
_toggleComplete: function( data ) {
|
||
var toHide = data.oldPanel,
|
||
prev = toHide.prev();
|
||
|
||
this._removeClass( toHide, "ui-accordion-content-active" );
|
||
this._removeClass( prev, "ui-accordion-header-active" )
|
||
._addClass( prev, "ui-accordion-header-collapsed" );
|
||
|
||
// Work around for rendering bug in IE (#5421)
|
||
if ( toHide.length ) {
|
||
toHide.parent()[ 0 ].className = toHide.parent()[ 0 ].className;
|
||
}
|
||
this._trigger( "activate", null, data );
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Menu 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Menu
|
||
//>>group: Widgets
|
||
//>>description: Creates nestable menus.
|
||
//>>docs: http://api.jqueryui.com/menu/
|
||
//>>demos: http://jqueryui.com/menu/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/menu.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
var widgetsMenu = $.widget( "ui.menu", {
|
||
version: "1.12.1",
|
||
defaultElement: "<ul>",
|
||
delay: 300,
|
||
options: {
|
||
icons: {
|
||
submenu: "ui-icon-caret-1-e"
|
||
},
|
||
items: "> *",
|
||
menus: "ul",
|
||
position: {
|
||
my: "left top",
|
||
at: "right top"
|
||
},
|
||
role: "menu",
|
||
|
||
// Callbacks
|
||
blur: null,
|
||
focus: null,
|
||
select: null
|
||
},
|
||
|
||
_create: function() {
|
||
this.activeMenu = this.element;
|
||
|
||
// Flag used to prevent firing of the click handler
|
||
// as the event bubbles up through nested menus
|
||
this.mouseHandled = false;
|
||
this.element
|
||
.uniqueId()
|
||
.attr( {
|
||
role: this.options.role,
|
||
tabIndex: 0
|
||
} );
|
||
|
||
this._addClass( "ui-menu", "ui-widget ui-widget-content" );
|
||
this._on( {
|
||
|
||
// Prevent focus from sticking to links inside menu after clicking
|
||
// them (focus should always stay on UL during navigation).
|
||
"mousedown .ui-menu-item": function( event ) {
|
||
event.preventDefault();
|
||
},
|
||
"click .ui-menu-item": function( event ) {
|
||
var target = $( event.target );
|
||
var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
|
||
if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
|
||
this.select( event );
|
||
|
||
// Only set the mouseHandled flag if the event will bubble, see #9469.
|
||
if ( !event.isPropagationStopped() ) {
|
||
this.mouseHandled = true;
|
||
}
|
||
|
||
// Open submenu on click
|
||
if ( target.has( ".ui-menu" ).length ) {
|
||
this.expand( event );
|
||
} else if ( !this.element.is( ":focus" ) &&
|
||
active.closest( ".ui-menu" ).length ) {
|
||
|
||
// Redirect focus to the menu
|
||
this.element.trigger( "focus", [ true ] );
|
||
|
||
// If the active item is on the top level, let it stay active.
|
||
// Otherwise, blur the active item since it is no longer visible.
|
||
if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
|
||
clearTimeout( this.timer );
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"mouseenter .ui-menu-item": function( event ) {
|
||
|
||
// Ignore mouse events while typeahead is active, see #10458.
|
||
// Prevents focusing the wrong item when typeahead causes a scroll while the mouse
|
||
// is over an item in the menu
|
||
if ( this.previousFilter ) {
|
||
return;
|
||
}
|
||
|
||
var actualTarget = $( event.target ).closest( ".ui-menu-item" ),
|
||
target = $( event.currentTarget );
|
||
|
||
// Ignore bubbled events on parent items, see #11641
|
||
if ( actualTarget[ 0 ] !== target[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
// Remove ui-state-active class from siblings of the newly focused menu item
|
||
// to avoid a jump caused by adjacent elements both having a class with a border
|
||
this._removeClass( target.siblings().children( ".ui-state-active" ),
|
||
null, "ui-state-active" );
|
||
this.focus( event, target );
|
||
},
|
||
mouseleave: "collapseAll",
|
||
"mouseleave .ui-menu": "collapseAll",
|
||
focus: function( event, keepActiveItem ) {
|
||
|
||
// If there's already an active item, keep it active
|
||
// If not, activate the first item
|
||
var item = this.active || this.element.find( this.options.items ).eq( 0 );
|
||
|
||
if ( !keepActiveItem ) {
|
||
this.focus( event, item );
|
||
}
|
||
},
|
||
blur: function( event ) {
|
||
this._delay( function() {
|
||
var notContained = !$.contains(
|
||
this.element[ 0 ],
|
||
$.ui.safeActiveElement( this.document[ 0 ] )
|
||
);
|
||
if ( notContained ) {
|
||
this.collapseAll( event );
|
||
}
|
||
} );
|
||
},
|
||
keydown: "_keydown"
|
||
} );
|
||
|
||
this.refresh();
|
||
|
||
// Clicks outside of a menu collapse any open menus
|
||
this._on( this.document, {
|
||
click: function( event ) {
|
||
if ( this._closeOnDocumentClick( event ) ) {
|
||
this.collapseAll( event );
|
||
}
|
||
|
||
// Reset the mouseHandled flag
|
||
this.mouseHandled = false;
|
||
}
|
||
} );
|
||
},
|
||
|
||
_destroy: function() {
|
||
var items = this.element.find( ".ui-menu-item" )
|
||
.removeAttr( "role aria-disabled" ),
|
||
submenus = items.children( ".ui-menu-item-wrapper" )
|
||
.removeUniqueId()
|
||
.removeAttr( "tabIndex role aria-haspopup" );
|
||
|
||
// Destroy (sub)menus
|
||
this.element
|
||
.removeAttr( "aria-activedescendant" )
|
||
.find( ".ui-menu" ).addBack()
|
||
.removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " +
|
||
"tabIndex" )
|
||
.removeUniqueId()
|
||
.show();
|
||
|
||
submenus.children().each( function() {
|
||
var elem = $( this );
|
||
if ( elem.data( "ui-menu-submenu-caret" ) ) {
|
||
elem.remove();
|
||
}
|
||
} );
|
||
},
|
||
|
||
_keydown: function( event ) {
|
||
var match, prev, character, skip,
|
||
preventDefault = true;
|
||
|
||
switch ( event.keyCode ) {
|
||
case $.ui.keyCode.PAGE_UP:
|
||
this.previousPage( event );
|
||
break;
|
||
case $.ui.keyCode.PAGE_DOWN:
|
||
this.nextPage( event );
|
||
break;
|
||
case $.ui.keyCode.HOME:
|
||
this._move( "first", "first", event );
|
||
break;
|
||
case $.ui.keyCode.END:
|
||
this._move( "last", "last", event );
|
||
break;
|
||
case $.ui.keyCode.UP:
|
||
this.previous( event );
|
||
break;
|
||
case $.ui.keyCode.DOWN:
|
||
this.next( event );
|
||
break;
|
||
case $.ui.keyCode.LEFT:
|
||
this.collapse( event );
|
||
break;
|
||
case $.ui.keyCode.RIGHT:
|
||
if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
|
||
this.expand( event );
|
||
}
|
||
break;
|
||
case $.ui.keyCode.ENTER:
|
||
case $.ui.keyCode.SPACE:
|
||
this._activate( event );
|
||
break;
|
||
case $.ui.keyCode.ESCAPE:
|
||
this.collapse( event );
|
||
break;
|
||
default:
|
||
preventDefault = false;
|
||
prev = this.previousFilter || "";
|
||
skip = false;
|
||
|
||
// Support number pad values
|
||
character = event.keyCode >= 96 && event.keyCode <= 105 ?
|
||
( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode );
|
||
|
||
clearTimeout( this.filterTimer );
|
||
|
||
if ( character === prev ) {
|
||
skip = true;
|
||
} else {
|
||
character = prev + character;
|
||
}
|
||
|
||
match = this._filterMenuItems( character );
|
||
match = skip && match.index( this.active.next() ) !== -1 ?
|
||
this.active.nextAll( ".ui-menu-item" ) :
|
||
match;
|
||
|
||
// If no matches on the current filter, reset to the last character pressed
|
||
// to move down the menu to the first item that starts with that character
|
||
if ( !match.length ) {
|
||
character = String.fromCharCode( event.keyCode );
|
||
match = this._filterMenuItems( character );
|
||
}
|
||
|
||
if ( match.length ) {
|
||
this.focus( event, match );
|
||
this.previousFilter = character;
|
||
this.filterTimer = this._delay( function() {
|
||
delete this.previousFilter;
|
||
}, 1000 );
|
||
} else {
|
||
delete this.previousFilter;
|
||
}
|
||
}
|
||
|
||
if ( preventDefault ) {
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
|
||
_activate: function( event ) {
|
||
if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
|
||
if ( this.active.children( "[aria-haspopup='true']" ).length ) {
|
||
this.expand( event );
|
||
} else {
|
||
this.select( event );
|
||
}
|
||
}
|
||
},
|
||
|
||
refresh: function() {
|
||
var menus, items, newSubmenus, newItems, newWrappers,
|
||
that = this,
|
||
icon = this.options.icons.submenu,
|
||
submenus = this.element.find( this.options.menus );
|
||
|
||
this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length );
|
||
|
||
// Initialize nested menus
|
||
newSubmenus = submenus.filter( ":not(.ui-menu)" )
|
||
.hide()
|
||
.attr( {
|
||
role: this.options.role,
|
||
"aria-hidden": "true",
|
||
"aria-expanded": "false"
|
||
} )
|
||
.each( function() {
|
||
var menu = $( this ),
|
||
item = menu.prev(),
|
||
submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true );
|
||
|
||
that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon );
|
||
item
|
||
.attr( "aria-haspopup", "true" )
|
||
.prepend( submenuCaret );
|
||
menu.attr( "aria-labelledby", item.attr( "id" ) );
|
||
} );
|
||
|
||
this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" );
|
||
|
||
menus = submenus.add( this.element );
|
||
items = menus.find( this.options.items );
|
||
|
||
// Initialize menu-items containing spaces and/or dashes only as dividers
|
||
items.not( ".ui-menu-item" ).each( function() {
|
||
var item = $( this );
|
||
if ( that._isDivider( item ) ) {
|
||
that._addClass( item, "ui-menu-divider", "ui-widget-content" );
|
||
}
|
||
} );
|
||
|
||
// Don't refresh list items that are already adapted
|
||
newItems = items.not( ".ui-menu-item, .ui-menu-divider" );
|
||
newWrappers = newItems.children()
|
||
.not( ".ui-menu" )
|
||
.uniqueId()
|
||
.attr( {
|
||
tabIndex: -1,
|
||
role: this._itemRole()
|
||
} );
|
||
this._addClass( newItems, "ui-menu-item" )
|
||
._addClass( newWrappers, "ui-menu-item-wrapper" );
|
||
|
||
// Add aria-disabled attribute to any disabled menu item
|
||
items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
|
||
|
||
// If the active item has been removed, blur the menu
|
||
if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
|
||
this.blur();
|
||
}
|
||
},
|
||
|
||
_itemRole: function() {
|
||
return {
|
||
menu: "menuitem",
|
||
listbox: "option"
|
||
}[ this.options.role ];
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "icons" ) {
|
||
var icons = this.element.find( ".ui-menu-icon" );
|
||
this._removeClass( icons, null, this.options.icons.submenu )
|
||
._addClass( icons, null, value.submenu );
|
||
}
|
||
this._super( key, value );
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this.element.attr( "aria-disabled", String( value ) );
|
||
this._toggleClass( null, "ui-state-disabled", !!value );
|
||
},
|
||
|
||
focus: function( event, item ) {
|
||
var nested, focused, activeParent;
|
||
this.blur( event, event && event.type === "focus" );
|
||
|
||
this._scrollIntoView( item );
|
||
|
||
this.active = item.first();
|
||
|
||
focused = this.active.children( ".ui-menu-item-wrapper" );
|
||
this._addClass( focused, null, "ui-state-active" );
|
||
|
||
// Only update aria-activedescendant if there's a role
|
||
// otherwise we assume focus is managed elsewhere
|
||
if ( this.options.role ) {
|
||
this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
|
||
}
|
||
|
||
// Highlight active parent menu item, if any
|
||
activeParent = this.active
|
||
.parent()
|
||
.closest( ".ui-menu-item" )
|
||
.children( ".ui-menu-item-wrapper" );
|
||
this._addClass( activeParent, null, "ui-state-active" );
|
||
|
||
if ( event && event.type === "keydown" ) {
|
||
this._close();
|
||
} else {
|
||
this.timer = this._delay( function() {
|
||
this._close();
|
||
}, this.delay );
|
||
}
|
||
|
||
nested = item.children( ".ui-menu" );
|
||
if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
|
||
this._startOpening( nested );
|
||
}
|
||
this.activeMenu = item.parent();
|
||
|
||
this._trigger( "focus", event, { item: item } );
|
||
},
|
||
|
||
_scrollIntoView: function( item ) {
|
||
var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
|
||
if ( this._hasScroll() ) {
|
||
borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0;
|
||
paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0;
|
||
offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
|
||
scroll = this.activeMenu.scrollTop();
|
||
elementHeight = this.activeMenu.height();
|
||
itemHeight = item.outerHeight();
|
||
|
||
if ( offset < 0 ) {
|
||
this.activeMenu.scrollTop( scroll + offset );
|
||
} else if ( offset + itemHeight > elementHeight ) {
|
||
this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
|
||
}
|
||
}
|
||
},
|
||
|
||
blur: function( event, fromFocus ) {
|
||
if ( !fromFocus ) {
|
||
clearTimeout( this.timer );
|
||
}
|
||
|
||
if ( !this.active ) {
|
||
return;
|
||
}
|
||
|
||
this._removeClass( this.active.children( ".ui-menu-item-wrapper" ),
|
||
null, "ui-state-active" );
|
||
|
||
this._trigger( "blur", event, { item: this.active } );
|
||
this.active = null;
|
||
},
|
||
|
||
_startOpening: function( submenu ) {
|
||
clearTimeout( this.timer );
|
||
|
||
// Don't open if already open fixes a Firefox bug that caused a .5 pixel
|
||
// shift in the submenu position when mousing over the caret icon
|
||
if ( submenu.attr( "aria-hidden" ) !== "true" ) {
|
||
return;
|
||
}
|
||
|
||
this.timer = this._delay( function() {
|
||
this._close();
|
||
this._open( submenu );
|
||
}, this.delay );
|
||
},
|
||
|
||
_open: function( submenu ) {
|
||
var position = $.extend( {
|
||
of: this.active
|
||
}, this.options.position );
|
||
|
||
clearTimeout( this.timer );
|
||
this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
|
||
.hide()
|
||
.attr( "aria-hidden", "true" );
|
||
|
||
submenu
|
||
.show()
|
||
.removeAttr( "aria-hidden" )
|
||
.attr( "aria-expanded", "true" )
|
||
.position( position );
|
||
},
|
||
|
||
collapseAll: function( event, all ) {
|
||
clearTimeout( this.timer );
|
||
this.timer = this._delay( function() {
|
||
|
||
// If we were passed an event, look for the submenu that contains the event
|
||
var currentMenu = all ? this.element :
|
||
$( event && event.target ).closest( this.element.find( ".ui-menu" ) );
|
||
|
||
// If we found no valid submenu ancestor, use the main menu to close all
|
||
// sub menus anyway
|
||
if ( !currentMenu.length ) {
|
||
currentMenu = this.element;
|
||
}
|
||
|
||
this._close( currentMenu );
|
||
|
||
this.blur( event );
|
||
|
||
// Work around active item staying active after menu is blurred
|
||
this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" );
|
||
|
||
this.activeMenu = currentMenu;
|
||
}, this.delay );
|
||
},
|
||
|
||
// With no arguments, closes the currently active menu - if nothing is active
|
||
// it closes all menus. If passed an argument, it will search for menus BELOW
|
||
_close: function( startMenu ) {
|
||
if ( !startMenu ) {
|
||
startMenu = this.active ? this.active.parent() : this.element;
|
||
}
|
||
|
||
startMenu.find( ".ui-menu" )
|
||
.hide()
|
||
.attr( "aria-hidden", "true" )
|
||
.attr( "aria-expanded", "false" );
|
||
},
|
||
|
||
_closeOnDocumentClick: function( event ) {
|
||
return !$( event.target ).closest( ".ui-menu" ).length;
|
||
},
|
||
|
||
_isDivider: function( item ) {
|
||
|
||
// Match hyphen, em dash, en dash
|
||
return !/[^\-\u2014\u2013\s]/.test( item.text() );
|
||
},
|
||
|
||
collapse: function( event ) {
|
||
var newItem = this.active &&
|
||
this.active.parent().closest( ".ui-menu-item", this.element );
|
||
if ( newItem && newItem.length ) {
|
||
this._close();
|
||
this.focus( event, newItem );
|
||
}
|
||
},
|
||
|
||
expand: function( event ) {
|
||
var newItem = this.active &&
|
||
this.active
|
||
.children( ".ui-menu " )
|
||
.find( this.options.items )
|
||
.first();
|
||
|
||
if ( newItem && newItem.length ) {
|
||
this._open( newItem.parent() );
|
||
|
||
// Delay so Firefox will not hide activedescendant change in expanding submenu from AT
|
||
this._delay( function() {
|
||
this.focus( event, newItem );
|
||
} );
|
||
}
|
||
},
|
||
|
||
next: function( event ) {
|
||
this._move( "next", "first", event );
|
||
},
|
||
|
||
previous: function( event ) {
|
||
this._move( "prev", "last", event );
|
||
},
|
||
|
||
isFirstItem: function() {
|
||
return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
|
||
},
|
||
|
||
isLastItem: function() {
|
||
return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
|
||
},
|
||
|
||
_move: function( direction, filter, event ) {
|
||
var next;
|
||
if ( this.active ) {
|
||
if ( direction === "first" || direction === "last" ) {
|
||
next = this.active
|
||
[ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
|
||
.eq( -1 );
|
||
} else {
|
||
next = this.active
|
||
[ direction + "All" ]( ".ui-menu-item" )
|
||
.eq( 0 );
|
||
}
|
||
}
|
||
if ( !next || !next.length || !this.active ) {
|
||
next = this.activeMenu.find( this.options.items )[ filter ]();
|
||
}
|
||
|
||
this.focus( event, next );
|
||
},
|
||
|
||
nextPage: function( event ) {
|
||
var item, base, height;
|
||
|
||
if ( !this.active ) {
|
||
this.next( event );
|
||
return;
|
||
}
|
||
if ( this.isLastItem() ) {
|
||
return;
|
||
}
|
||
if ( this._hasScroll() ) {
|
||
base = this.active.offset().top;
|
||
height = this.element.height();
|
||
this.active.nextAll( ".ui-menu-item" ).each( function() {
|
||
item = $( this );
|
||
return item.offset().top - base - height < 0;
|
||
} );
|
||
|
||
this.focus( event, item );
|
||
} else {
|
||
this.focus( event, this.activeMenu.find( this.options.items )
|
||
[ !this.active ? "first" : "last" ]() );
|
||
}
|
||
},
|
||
|
||
previousPage: function( event ) {
|
||
var item, base, height;
|
||
if ( !this.active ) {
|
||
this.next( event );
|
||
return;
|
||
}
|
||
if ( this.isFirstItem() ) {
|
||
return;
|
||
}
|
||
if ( this._hasScroll() ) {
|
||
base = this.active.offset().top;
|
||
height = this.element.height();
|
||
this.active.prevAll( ".ui-menu-item" ).each( function() {
|
||
item = $( this );
|
||
return item.offset().top - base + height > 0;
|
||
} );
|
||
|
||
this.focus( event, item );
|
||
} else {
|
||
this.focus( event, this.activeMenu.find( this.options.items ).first() );
|
||
}
|
||
},
|
||
|
||
_hasScroll: function() {
|
||
return this.element.outerHeight() < this.element.prop( "scrollHeight" );
|
||
},
|
||
|
||
select: function( event ) {
|
||
|
||
// TODO: It should never be possible to not have an active item at this
|
||
// point, but the tests don't trigger mouseenter before click.
|
||
this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
|
||
var ui = { item: this.active };
|
||
if ( !this.active.has( ".ui-menu" ).length ) {
|
||
this.collapseAll( event, true );
|
||
}
|
||
this._trigger( "select", event, ui );
|
||
},
|
||
|
||
_filterMenuItems: function( character ) {
|
||
var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ),
|
||
regex = new RegExp( "^" + escapedCharacter, "i" );
|
||
|
||
return this.activeMenu
|
||
.find( this.options.items )
|
||
|
||
// Only match on items, not dividers or other content (#10571)
|
||
.filter( ".ui-menu-item" )
|
||
.filter( function() {
|
||
return regex.test(
|
||
$.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) );
|
||
} );
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Autocomplete 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Autocomplete
|
||
//>>group: Widgets
|
||
//>>description: Lists suggested words as the user is typing.
|
||
//>>docs: http://api.jqueryui.com/autocomplete/
|
||
//>>demos: http://jqueryui.com/autocomplete/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/autocomplete.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.autocomplete", {
|
||
version: "1.12.1",
|
||
defaultElement: "<input>",
|
||
options: {
|
||
appendTo: null,
|
||
autoFocus: false,
|
||
delay: 300,
|
||
minLength: 1,
|
||
position: {
|
||
my: "left top",
|
||
at: "left bottom",
|
||
collision: "none"
|
||
},
|
||
source: null,
|
||
|
||
// Callbacks
|
||
change: null,
|
||
close: null,
|
||
focus: null,
|
||
open: null,
|
||
response: null,
|
||
search: null,
|
||
select: null
|
||
},
|
||
|
||
requestIndex: 0,
|
||
pending: 0,
|
||
|
||
_create: function() {
|
||
|
||
// Some browsers only repeat keydown events, not keypress events,
|
||
// so we use the suppressKeyPress flag to determine if we've already
|
||
// handled the keydown event. #7269
|
||
// Unfortunately the code for & in keypress is the same as the up arrow,
|
||
// so we use the suppressKeyPressRepeat flag to avoid handling keypress
|
||
// events when we know the keydown event was used to modify the
|
||
// search term. #7799
|
||
var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
|
||
nodeName = this.element[ 0 ].nodeName.toLowerCase(),
|
||
isTextarea = nodeName === "textarea",
|
||
isInput = nodeName === "input";
|
||
|
||
// Textareas are always multi-line
|
||
// Inputs are always single-line, even if inside a contentEditable element
|
||
// IE also treats inputs as contentEditable
|
||
// All other element types are determined by whether or not they're contentEditable
|
||
this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element );
|
||
|
||
this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
|
||
this.isNewMenu = true;
|
||
|
||
this._addClass( "ui-autocomplete-input" );
|
||
this.element.attr( "autocomplete", "off" );
|
||
|
||
this._on( this.element, {
|
||
keydown: function( event ) {
|
||
if ( this.element.prop( "readOnly" ) ) {
|
||
suppressKeyPress = true;
|
||
suppressInput = true;
|
||
suppressKeyPressRepeat = true;
|
||
return;
|
||
}
|
||
|
||
suppressKeyPress = false;
|
||
suppressInput = false;
|
||
suppressKeyPressRepeat = false;
|
||
var keyCode = $.ui.keyCode;
|
||
switch ( event.keyCode ) {
|
||
case keyCode.PAGE_UP:
|
||
suppressKeyPress = true;
|
||
this._move( "previousPage", event );
|
||
break;
|
||
case keyCode.PAGE_DOWN:
|
||
suppressKeyPress = true;
|
||
this._move( "nextPage", event );
|
||
break;
|
||
case keyCode.UP:
|
||
suppressKeyPress = true;
|
||
this._keyEvent( "previous", event );
|
||
break;
|
||
case keyCode.DOWN:
|
||
suppressKeyPress = true;
|
||
this._keyEvent( "next", event );
|
||
break;
|
||
case keyCode.ENTER:
|
||
|
||
// when menu is open and has focus
|
||
if ( this.menu.active ) {
|
||
|
||
// #6055 - Opera still allows the keypress to occur
|
||
// which causes forms to submit
|
||
suppressKeyPress = true;
|
||
event.preventDefault();
|
||
this.menu.select( event );
|
||
}
|
||
break;
|
||
case keyCode.TAB:
|
||
if ( this.menu.active ) {
|
||
this.menu.select( event );
|
||
}
|
||
break;
|
||
case keyCode.ESCAPE:
|
||
if ( this.menu.element.is( ":visible" ) ) {
|
||
if ( !this.isMultiLine ) {
|
||
this._value( this.term );
|
||
}
|
||
this.close( event );
|
||
|
||
// Different browsers have different default behavior for escape
|
||
// Single press can mean undo or clear
|
||
// Double press in IE means clear the whole form
|
||
event.preventDefault();
|
||
}
|
||
break;
|
||
default:
|
||
suppressKeyPressRepeat = true;
|
||
|
||
// search timeout should be triggered before the input value is changed
|
||
this._searchTimeout( event );
|
||
break;
|
||
}
|
||
},
|
||
keypress: function( event ) {
|
||
if ( suppressKeyPress ) {
|
||
suppressKeyPress = false;
|
||
if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
|
||
event.preventDefault();
|
||
}
|
||
return;
|
||
}
|
||
if ( suppressKeyPressRepeat ) {
|
||
return;
|
||
}
|
||
|
||
// Replicate some key handlers to allow them to repeat in Firefox and Opera
|
||
var keyCode = $.ui.keyCode;
|
||
switch ( event.keyCode ) {
|
||
case keyCode.PAGE_UP:
|
||
this._move( "previousPage", event );
|
||
break;
|
||
case keyCode.PAGE_DOWN:
|
||
this._move( "nextPage", event );
|
||
break;
|
||
case keyCode.UP:
|
||
this._keyEvent( "previous", event );
|
||
break;
|
||
case keyCode.DOWN:
|
||
this._keyEvent( "next", event );
|
||
break;
|
||
}
|
||
},
|
||
input: function( event ) {
|
||
if ( suppressInput ) {
|
||
suppressInput = false;
|
||
event.preventDefault();
|
||
return;
|
||
}
|
||
this._searchTimeout( event );
|
||
},
|
||
focus: function() {
|
||
this.selectedItem = null;
|
||
this.previous = this._value();
|
||
},
|
||
blur: function( event ) {
|
||
if ( this.cancelBlur ) {
|
||
delete this.cancelBlur;
|
||
return;
|
||
}
|
||
|
||
clearTimeout( this.searching );
|
||
this.close( event );
|
||
this._change( event );
|
||
}
|
||
} );
|
||
|
||
this._initSource();
|
||
this.menu = $( "<ul>" )
|
||
.appendTo( this._appendTo() )
|
||
.menu( {
|
||
|
||
// disable ARIA support, the live region takes care of that
|
||
role: null
|
||
} )
|
||
.hide()
|
||
.menu( "instance" );
|
||
|
||
this._addClass( this.menu.element, "ui-autocomplete", "ui-front" );
|
||
this._on( this.menu.element, {
|
||
mousedown: function( event ) {
|
||
|
||
// prevent moving focus out of the text field
|
||
event.preventDefault();
|
||
|
||
// IE doesn't prevent moving focus even with event.preventDefault()
|
||
// so we set a flag to know when we should ignore the blur event
|
||
this.cancelBlur = true;
|
||
this._delay( function() {
|
||
delete this.cancelBlur;
|
||
|
||
// Support: IE 8 only
|
||
// Right clicking a menu item or selecting text from the menu items will
|
||
// result in focus moving out of the input. However, we've already received
|
||
// and ignored the blur event because of the cancelBlur flag set above. So
|
||
// we restore focus to ensure that the menu closes properly based on the user's
|
||
// next actions.
|
||
if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
|
||
this.element.trigger( "focus" );
|
||
}
|
||
} );
|
||
},
|
||
menufocus: function( event, ui ) {
|
||
var label, item;
|
||
|
||
// support: Firefox
|
||
// Prevent accidental activation of menu items in Firefox (#7024 #9118)
|
||
if ( this.isNewMenu ) {
|
||
this.isNewMenu = false;
|
||
if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
|
||
this.menu.blur();
|
||
|
||
this.document.one( "mousemove", function() {
|
||
$( event.target ).trigger( event.originalEvent );
|
||
} );
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
item = ui.item.data( "ui-autocomplete-item" );
|
||
if ( false !== this._trigger( "focus", event, { item: item } ) ) {
|
||
|
||
// use value to match what will end up in the input, if it was a key event
|
||
if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
|
||
this._value( item.value );
|
||
}
|
||
}
|
||
|
||
// Announce the value in the liveRegion
|
||
label = ui.item.attr( "aria-label" ) || item.value;
|
||
if ( label && $.trim( label ).length ) {
|
||
this.liveRegion.children().hide();
|
||
$( "<div>" ).text( label ).appendTo( this.liveRegion );
|
||
}
|
||
},
|
||
menuselect: function( event, ui ) {
|
||
var item = ui.item.data( "ui-autocomplete-item" ),
|
||
previous = this.previous;
|
||
|
||
// Only trigger when focus was lost (click on menu)
|
||
if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) {
|
||
this.element.trigger( "focus" );
|
||
this.previous = previous;
|
||
|
||
// #6109 - IE triggers two focus events and the second
|
||
// is asynchronous, so we need to reset the previous
|
||
// term synchronously and asynchronously :-(
|
||
this._delay( function() {
|
||
this.previous = previous;
|
||
this.selectedItem = item;
|
||
} );
|
||
}
|
||
|
||
if ( false !== this._trigger( "select", event, { item: item } ) ) {
|
||
this._value( item.value );
|
||
}
|
||
|
||
// reset the term after the select event
|
||
// this allows custom select handling to work properly
|
||
this.term = this._value();
|
||
|
||
this.close( event );
|
||
this.selectedItem = item;
|
||
}
|
||
} );
|
||
|
||
this.liveRegion = $( "<div>", {
|
||
role: "status",
|
||
"aria-live": "assertive",
|
||
"aria-relevant": "additions"
|
||
} )
|
||
.appendTo( this.document[ 0 ].body );
|
||
|
||
this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
|
||
|
||
// Turning off autocomplete prevents the browser from remembering the
|
||
// value when navigating through history, so we re-enable autocomplete
|
||
// if the page is unloaded before the widget is destroyed. #7790
|
||
this._on( this.window, {
|
||
beforeunload: function() {
|
||
this.element.removeAttr( "autocomplete" );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_destroy: function() {
|
||
clearTimeout( this.searching );
|
||
this.element.removeAttr( "autocomplete" );
|
||
this.menu.element.remove();
|
||
this.liveRegion.remove();
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
this._super( key, value );
|
||
if ( key === "source" ) {
|
||
this._initSource();
|
||
}
|
||
if ( key === "appendTo" ) {
|
||
this.menu.element.appendTo( this._appendTo() );
|
||
}
|
||
if ( key === "disabled" && value && this.xhr ) {
|
||
this.xhr.abort();
|
||
}
|
||
},
|
||
|
||
_isEventTargetInWidget: function( event ) {
|
||
var menuElement = this.menu.element[ 0 ];
|
||
|
||
return event.target === this.element[ 0 ] ||
|
||
event.target === menuElement ||
|
||
$.contains( menuElement, event.target );
|
||
},
|
||
|
||
_closeOnClickOutside: function( event ) {
|
||
if ( !this._isEventTargetInWidget( event ) ) {
|
||
this.close();
|
||
}
|
||
},
|
||
|
||
_appendTo: function() {
|
||
var element = this.options.appendTo;
|
||
|
||
if ( element ) {
|
||
element = element.jquery || element.nodeType ?
|
||
$( element ) :
|
||
this.document.find( element ).eq( 0 );
|
||
}
|
||
|
||
if ( !element || !element[ 0 ] ) {
|
||
element = this.element.closest( ".ui-front, dialog" );
|
||
}
|
||
|
||
if ( !element.length ) {
|
||
element = this.document[ 0 ].body;
|
||
}
|
||
|
||
return element;
|
||
},
|
||
|
||
_initSource: function() {
|
||
var array, url,
|
||
that = this;
|
||
if ( $.isArray( this.options.source ) ) {
|
||
array = this.options.source;
|
||
this.source = function( request, response ) {
|
||
response( $.ui.autocomplete.filter( array, request.term ) );
|
||
};
|
||
} else if ( typeof this.options.source === "string" ) {
|
||
url = this.options.source;
|
||
this.source = function( request, response ) {
|
||
if ( that.xhr ) {
|
||
that.xhr.abort();
|
||
}
|
||
that.xhr = $.ajax( {
|
||
url: url,
|
||
data: request,
|
||
dataType: "json",
|
||
success: function( data ) {
|
||
response( data );
|
||
},
|
||
error: function() {
|
||
response( [] );
|
||
}
|
||
} );
|
||
};
|
||
} else {
|
||
this.source = this.options.source;
|
||
}
|
||
},
|
||
|
||
_searchTimeout: function( event ) {
|
||
clearTimeout( this.searching );
|
||
this.searching = this._delay( function() {
|
||
|
||
// Search if the value has changed, or if the user retypes the same value (see #7434)
|
||
var equalValues = this.term === this._value(),
|
||
menuVisible = this.menu.element.is( ":visible" ),
|
||
modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
||
|
||
if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) {
|
||
this.selectedItem = null;
|
||
this.search( null, event );
|
||
}
|
||
}, this.options.delay );
|
||
},
|
||
|
||
search: function( value, event ) {
|
||
value = value != null ? value : this._value();
|
||
|
||
// Always save the actual value, not the one passed as an argument
|
||
this.term = this._value();
|
||
|
||
if ( value.length < this.options.minLength ) {
|
||
return this.close( event );
|
||
}
|
||
|
||
if ( this._trigger( "search", event ) === false ) {
|
||
return;
|
||
}
|
||
|
||
return this._search( value );
|
||
},
|
||
|
||
_search: function( value ) {
|
||
this.pending++;
|
||
this._addClass( "ui-autocomplete-loading" );
|
||
this.cancelSearch = false;
|
||
|
||
this.source( { term: value }, this._response() );
|
||
},
|
||
|
||
_response: function() {
|
||
var index = ++this.requestIndex;
|
||
|
||
return $.proxy( function( content ) {
|
||
if ( index === this.requestIndex ) {
|
||
this.__response( content );
|
||
}
|
||
|
||
this.pending--;
|
||
if ( !this.pending ) {
|
||
this._removeClass( "ui-autocomplete-loading" );
|
||
}
|
||
}, this );
|
||
},
|
||
|
||
__response: function( content ) {
|
||
if ( content ) {
|
||
content = this._normalize( content );
|
||
}
|
||
this._trigger( "response", null, { content: content } );
|
||
if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
|
||
this._suggest( content );
|
||
this._trigger( "open" );
|
||
} else {
|
||
|
||
// use ._close() instead of .close() so we don't cancel future searches
|
||
this._close();
|
||
}
|
||
},
|
||
|
||
close: function( event ) {
|
||
this.cancelSearch = true;
|
||
this._close( event );
|
||
},
|
||
|
||
_close: function( event ) {
|
||
|
||
// Remove the handler that closes the menu on outside clicks
|
||
this._off( this.document, "mousedown" );
|
||
|
||
if ( this.menu.element.is( ":visible" ) ) {
|
||
this.menu.element.hide();
|
||
this.menu.blur();
|
||
this.isNewMenu = true;
|
||
this._trigger( "close", event );
|
||
}
|
||
},
|
||
|
||
_change: function( event ) {
|
||
if ( this.previous !== this._value() ) {
|
||
this._trigger( "change", event, { item: this.selectedItem } );
|
||
}
|
||
},
|
||
|
||
_normalize: function( items ) {
|
||
|
||
// assume all items have the right format when the first item is complete
|
||
if ( items.length && items[ 0 ].label && items[ 0 ].value ) {
|
||
return items;
|
||
}
|
||
return $.map( items, function( item ) {
|
||
if ( typeof item === "string" ) {
|
||
return {
|
||
label: item,
|
||
value: item
|
||
};
|
||
}
|
||
return $.extend( {}, item, {
|
||
label: item.label || item.value,
|
||
value: item.value || item.label
|
||
} );
|
||
} );
|
||
},
|
||
|
||
_suggest: function( items ) {
|
||
var ul = this.menu.element.empty();
|
||
this._renderMenu( ul, items );
|
||
this.isNewMenu = true;
|
||
this.menu.refresh();
|
||
|
||
// Size and position menu
|
||
ul.show();
|
||
this._resizeMenu();
|
||
ul.position( $.extend( {
|
||
of: this.element
|
||
}, this.options.position ) );
|
||
|
||
if ( this.options.autoFocus ) {
|
||
this.menu.next();
|
||
}
|
||
|
||
// Listen for interactions outside of the widget (#6642)
|
||
this._on( this.document, {
|
||
mousedown: "_closeOnClickOutside"
|
||
} );
|
||
},
|
||
|
||
_resizeMenu: function() {
|
||
var ul = this.menu.element;
|
||
ul.outerWidth( Math.max(
|
||
|
||
// Firefox wraps long text (possibly a rounding bug)
|
||
// so we add 1px to avoid the wrapping (#7513)
|
||
ul.width( "" ).outerWidth() + 1,
|
||
this.element.outerWidth()
|
||
) );
|
||
},
|
||
|
||
_renderMenu: function( ul, items ) {
|
||
var that = this;
|
||
$.each( items, function( index, item ) {
|
||
that._renderItemData( ul, item );
|
||
} );
|
||
},
|
||
|
||
_renderItemData: function( ul, item ) {
|
||
return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
|
||
},
|
||
|
||
_renderItem: function( ul, item ) {
|
||
return $( "<li>" )
|
||
.append( $( "<div>" ).text( item.label ) )
|
||
.appendTo( ul );
|
||
},
|
||
|
||
_move: function( direction, event ) {
|
||
if ( !this.menu.element.is( ":visible" ) ) {
|
||
this.search( null, event );
|
||
return;
|
||
}
|
||
if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
|
||
this.menu.isLastItem() && /^next/.test( direction ) ) {
|
||
|
||
if ( !this.isMultiLine ) {
|
||
this._value( this.term );
|
||
}
|
||
|
||
this.menu.blur();
|
||
return;
|
||
}
|
||
this.menu[ direction ]( event );
|
||
},
|
||
|
||
widget: function() {
|
||
return this.menu.element;
|
||
},
|
||
|
||
_value: function() {
|
||
return this.valueMethod.apply( this.element, arguments );
|
||
},
|
||
|
||
_keyEvent: function( keyEvent, event ) {
|
||
if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
|
||
this._move( keyEvent, event );
|
||
|
||
// Prevents moving cursor to beginning/end of the text field in some browsers
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
|
||
// Support: Chrome <=50
|
||
// We should be able to just use this.element.prop( "isContentEditable" )
|
||
// but hidden elements always report false in Chrome.
|
||
// https://code.google.com/p/chromium/issues/detail?id=313082
|
||
_isContentEditable: function( element ) {
|
||
if ( !element.length ) {
|
||
return false;
|
||
}
|
||
|
||
var editable = element.prop( "contentEditable" );
|
||
|
||
if ( editable === "inherit" ) {
|
||
return this._isContentEditable( element.parent() );
|
||
}
|
||
|
||
return editable === "true";
|
||
}
|
||
} );
|
||
|
||
$.extend( $.ui.autocomplete, {
|
||
escapeRegex: function( value ) {
|
||
return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
|
||
},
|
||
filter: function( array, term ) {
|
||
var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
|
||
return $.grep( array, function( value ) {
|
||
return matcher.test( value.label || value.value || value );
|
||
} );
|
||
}
|
||
} );
|
||
|
||
// Live region extension, adding a `messages` option
|
||
// NOTE: This is an experimental API. We are still investigating
|
||
// a full solution for string manipulation and internationalization.
|
||
$.widget( "ui.autocomplete", $.ui.autocomplete, {
|
||
options: {
|
||
messages: {
|
||
noResults: "No search results.",
|
||
results: function( amount ) {
|
||
return amount + ( amount > 1 ? " results are" : " result is" ) +
|
||
" available, use up and down arrow keys to navigate.";
|
||
}
|
||
}
|
||
},
|
||
|
||
__response: function( content ) {
|
||
var message;
|
||
this._superApply( arguments );
|
||
if ( this.options.disabled || this.cancelSearch ) {
|
||
return;
|
||
}
|
||
if ( content && content.length ) {
|
||
message = this.options.messages.results( content.length );
|
||
} else {
|
||
message = this.options.messages.noResults;
|
||
}
|
||
this.liveRegion.children().hide();
|
||
$( "<div>" ).text( message ).appendTo( this.liveRegion );
|
||
}
|
||
} );
|
||
|
||
var widgetsAutocomplete = $.ui.autocomplete;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Controlgroup 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Controlgroup
|
||
//>>group: Widgets
|
||
//>>description: Visually groups form control widgets
|
||
//>>docs: http://api.jqueryui.com/controlgroup/
|
||
//>>demos: http://jqueryui.com/controlgroup/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/controlgroup.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
var controlgroupCornerRegex = /ui-corner-([a-z]){2,6}/g;
|
||
|
||
var widgetsControlgroup = $.widget( "ui.controlgroup", {
|
||
version: "1.12.1",
|
||
defaultElement: "<div>",
|
||
options: {
|
||
direction: "horizontal",
|
||
disabled: null,
|
||
onlyVisible: true,
|
||
items: {
|
||
"button": "input[type=button], input[type=submit], input[type=reset], button, a",
|
||
"controlgroupLabel": ".ui-controlgroup-label",
|
||
"checkboxradio": "input[type='checkbox'], input[type='radio']",
|
||
"selectmenu": "select",
|
||
"spinner": ".ui-spinner-input"
|
||
}
|
||
},
|
||
|
||
_create: function() {
|
||
this._enhance();
|
||
},
|
||
|
||
// To support the enhanced option in jQuery Mobile, we isolate DOM manipulation
|
||
_enhance: function() {
|
||
this.element.attr( "role", "toolbar" );
|
||
this.refresh();
|
||
},
|
||
|
||
_destroy: function() {
|
||
this._callChildMethod( "destroy" );
|
||
this.childWidgets.removeData( "ui-controlgroup-data" );
|
||
this.element.removeAttr( "role" );
|
||
if ( this.options.items.controlgroupLabel ) {
|
||
this.element
|
||
.find( this.options.items.controlgroupLabel )
|
||
.find( ".ui-controlgroup-label-contents" )
|
||
.contents().unwrap();
|
||
}
|
||
},
|
||
|
||
_initWidgets: function() {
|
||
var that = this,
|
||
childWidgets = [];
|
||
|
||
// First we iterate over each of the items options
|
||
$.each( this.options.items, function( widget, selector ) {
|
||
var labels;
|
||
var options = {};
|
||
|
||
// Make sure the widget has a selector set
|
||
if ( !selector ) {
|
||
return;
|
||
}
|
||
|
||
if ( widget === "controlgroupLabel" ) {
|
||
labels = that.element.find( selector );
|
||
labels.each( function() {
|
||
var element = $( this );
|
||
|
||
if ( element.children( ".ui-controlgroup-label-contents" ).length ) {
|
||
return;
|
||
}
|
||
element.contents()
|
||
.wrapAll( "<span class='ui-controlgroup-label-contents'></span>" );
|
||
} );
|
||
that._addClass( labels, null, "ui-widget ui-widget-content ui-state-default" );
|
||
childWidgets = childWidgets.concat( labels.get() );
|
||
return;
|
||
}
|
||
|
||
// Make sure the widget actually exists
|
||
if ( !$.fn[ widget ] ) {
|
||
return;
|
||
}
|
||
|
||
// We assume everything is in the middle to start because we can't determine
|
||
// first / last elements until all enhancments are done.
|
||
if ( that[ "_" + widget + "Options" ] ) {
|
||
options = that[ "_" + widget + "Options" ]( "middle" );
|
||
} else {
|
||
options = { classes: {} };
|
||
}
|
||
|
||
// Find instances of this widget inside controlgroup and init them
|
||
that.element
|
||
.find( selector )
|
||
.each( function() {
|
||
var element = $( this );
|
||
var instance = element[ widget ]( "instance" );
|
||
|
||
// We need to clone the default options for this type of widget to avoid
|
||
// polluting the variable options which has a wider scope than a single widget.
|
||
var instanceOptions = $.widget.extend( {}, options );
|
||
|
||
// If the button is the child of a spinner ignore it
|
||
// TODO: Find a more generic solution
|
||
if ( widget === "button" && element.parent( ".ui-spinner" ).length ) {
|
||
return;
|
||
}
|
||
|
||
// Create the widget if it doesn't exist
|
||
if ( !instance ) {
|
||
instance = element[ widget ]()[ widget ]( "instance" );
|
||
}
|
||
if ( instance ) {
|
||
instanceOptions.classes =
|
||
that._resolveClassesValues( instanceOptions.classes, instance );
|
||
}
|
||
element[ widget ]( instanceOptions );
|
||
|
||
// Store an instance of the controlgroup to be able to reference
|
||
// from the outermost element for changing options and refresh
|
||
var widgetElement = element[ widget ]( "widget" );
|
||
$.data( widgetElement[ 0 ], "ui-controlgroup-data",
|
||
instance ? instance : element[ widget ]( "instance" ) );
|
||
|
||
childWidgets.push( widgetElement[ 0 ] );
|
||
} );
|
||
} );
|
||
|
||
this.childWidgets = $( $.unique( childWidgets ) );
|
||
this._addClass( this.childWidgets, "ui-controlgroup-item" );
|
||
},
|
||
|
||
_callChildMethod: function( method ) {
|
||
this.childWidgets.each( function() {
|
||
var element = $( this ),
|
||
data = element.data( "ui-controlgroup-data" );
|
||
if ( data && data[ method ] ) {
|
||
data[ method ]();
|
||
}
|
||
} );
|
||
},
|
||
|
||
_updateCornerClass: function( element, position ) {
|
||
var remove = "ui-corner-top ui-corner-bottom ui-corner-left ui-corner-right ui-corner-all";
|
||
var add = this._buildSimpleOptions( position, "label" ).classes.label;
|
||
|
||
this._removeClass( element, null, remove );
|
||
this._addClass( element, null, add );
|
||
},
|
||
|
||
_buildSimpleOptions: function( position, key ) {
|
||
var direction = this.options.direction === "vertical";
|
||
var result = {
|
||
classes: {}
|
||
};
|
||
result.classes[ key ] = {
|
||
"middle": "",
|
||
"first": "ui-corner-" + ( direction ? "top" : "left" ),
|
||
"last": "ui-corner-" + ( direction ? "bottom" : "right" ),
|
||
"only": "ui-corner-all"
|
||
}[ position ];
|
||
|
||
return result;
|
||
},
|
||
|
||
_spinnerOptions: function( position ) {
|
||
var options = this._buildSimpleOptions( position, "ui-spinner" );
|
||
|
||
options.classes[ "ui-spinner-up" ] = "";
|
||
options.classes[ "ui-spinner-down" ] = "";
|
||
|
||
return options;
|
||
},
|
||
|
||
_buttonOptions: function( position ) {
|
||
return this._buildSimpleOptions( position, "ui-button" );
|
||
},
|
||
|
||
_checkboxradioOptions: function( position ) {
|
||
return this._buildSimpleOptions( position, "ui-checkboxradio-label" );
|
||
},
|
||
|
||
_selectmenuOptions: function( position ) {
|
||
var direction = this.options.direction === "vertical";
|
||
return {
|
||
width: direction ? "auto" : false,
|
||
classes: {
|
||
middle: {
|
||
"ui-selectmenu-button-open": "",
|
||
"ui-selectmenu-button-closed": ""
|
||
},
|
||
first: {
|
||
"ui-selectmenu-button-open": "ui-corner-" + ( direction ? "top" : "tl" ),
|
||
"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "top" : "left" )
|
||
},
|
||
last: {
|
||
"ui-selectmenu-button-open": direction ? "" : "ui-corner-tr",
|
||
"ui-selectmenu-button-closed": "ui-corner-" + ( direction ? "bottom" : "right" )
|
||
},
|
||
only: {
|
||
"ui-selectmenu-button-open": "ui-corner-top",
|
||
"ui-selectmenu-button-closed": "ui-corner-all"
|
||
}
|
||
|
||
}[ position ]
|
||
};
|
||
},
|
||
|
||
_resolveClassesValues: function( classes, instance ) {
|
||
var result = {};
|
||
$.each( classes, function( key ) {
|
||
var current = instance.options.classes[ key ] || "";
|
||
current = $.trim( current.replace( controlgroupCornerRegex, "" ) );
|
||
result[ key ] = ( current + " " + classes[ key ] ).replace( /\s+/g, " " );
|
||
} );
|
||
return result;
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "direction" ) {
|
||
this._removeClass( "ui-controlgroup-" + this.options.direction );
|
||
}
|
||
|
||
this._super( key, value );
|
||
if ( key === "disabled" ) {
|
||
this._callChildMethod( value ? "disable" : "enable" );
|
||
return;
|
||
}
|
||
|
||
this.refresh();
|
||
},
|
||
|
||
refresh: function() {
|
||
var children,
|
||
that = this;
|
||
|
||
this._addClass( "ui-controlgroup ui-controlgroup-" + this.options.direction );
|
||
|
||
if ( this.options.direction === "horizontal" ) {
|
||
this._addClass( null, "ui-helper-clearfix" );
|
||
}
|
||
this._initWidgets();
|
||
|
||
children = this.childWidgets;
|
||
|
||
// We filter here because we need to track all childWidgets not just the visible ones
|
||
if ( this.options.onlyVisible ) {
|
||
children = children.filter( ":visible" );
|
||
}
|
||
|
||
if ( children.length ) {
|
||
|
||
// We do this last because we need to make sure all enhancment is done
|
||
// before determining first and last
|
||
$.each( [ "first", "last" ], function( index, value ) {
|
||
var instance = children[ value ]().data( "ui-controlgroup-data" );
|
||
|
||
if ( instance && that[ "_" + instance.widgetName + "Options" ] ) {
|
||
var options = that[ "_" + instance.widgetName + "Options" ](
|
||
children.length === 1 ? "only" : value
|
||
);
|
||
options.classes = that._resolveClassesValues( options.classes, instance );
|
||
instance.element[ instance.widgetName ]( options );
|
||
} else {
|
||
that._updateCornerClass( children[ value ](), value );
|
||
}
|
||
} );
|
||
|
||
// Finally call the refresh method on each of the child widgets.
|
||
this._callChildMethod( "refresh" );
|
||
}
|
||
}
|
||
} );
|
||
|
||
/*!
|
||
* jQuery UI Checkboxradio 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Checkboxradio
|
||
//>>group: Widgets
|
||
//>>description: Enhances a form with multiple themeable checkboxes or radio buttons.
|
||
//>>docs: http://api.jqueryui.com/checkboxradio/
|
||
//>>demos: http://jqueryui.com/checkboxradio/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/button.css
|
||
//>>css.structure: ../../themes/base/checkboxradio.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.checkboxradio", [ $.ui.formResetMixin, {
|
||
version: "1.12.1",
|
||
options: {
|
||
disabled: null,
|
||
label: null,
|
||
icon: true,
|
||
classes: {
|
||
"ui-checkboxradio-label": "ui-corner-all",
|
||
"ui-checkboxradio-icon": "ui-corner-all"
|
||
}
|
||
},
|
||
|
||
_getCreateOptions: function() {
|
||
var disabled, labels;
|
||
var that = this;
|
||
var options = this._super() || {};
|
||
|
||
// We read the type here, because it makes more sense to throw a element type error first,
|
||
// rather then the error for lack of a label. Often if its the wrong type, it
|
||
// won't have a label (e.g. calling on a div, btn, etc)
|
||
this._readType();
|
||
|
||
labels = this.element.labels();
|
||
|
||
// If there are multiple labels, use the last one
|
||
this.label = $( labels[ labels.length - 1 ] );
|
||
if ( !this.label.length ) {
|
||
$.error( "No label found for checkboxradio widget" );
|
||
}
|
||
|
||
this.originalLabel = "";
|
||
|
||
// We need to get the label text but this may also need to make sure it does not contain the
|
||
// input itself.
|
||
this.label.contents().not( this.element[ 0 ] ).each( function() {
|
||
|
||
// The label contents could be text, html, or a mix. We concat each element to get a
|
||
// string representation of the label, without the input as part of it.
|
||
that.originalLabel += this.nodeType === 3 ? $( this ).text() : this.outerHTML;
|
||
} );
|
||
|
||
// Set the label option if we found label text
|
||
if ( this.originalLabel ) {
|
||
options.label = this.originalLabel;
|
||
}
|
||
|
||
disabled = this.element[ 0 ].disabled;
|
||
if ( disabled != null ) {
|
||
options.disabled = disabled;
|
||
}
|
||
return options;
|
||
},
|
||
|
||
_create: function() {
|
||
var checked = this.element[ 0 ].checked;
|
||
|
||
this._bindFormResetHandler();
|
||
|
||
if ( this.options.disabled == null ) {
|
||
this.options.disabled = this.element[ 0 ].disabled;
|
||
}
|
||
|
||
this._setOption( "disabled", this.options.disabled );
|
||
this._addClass( "ui-checkboxradio", "ui-helper-hidden-accessible" );
|
||
this._addClass( this.label, "ui-checkboxradio-label", "ui-button ui-widget" );
|
||
|
||
if ( this.type === "radio" ) {
|
||
this._addClass( this.label, "ui-checkboxradio-radio-label" );
|
||
}
|
||
|
||
if ( this.options.label && this.options.label !== this.originalLabel ) {
|
||
this._updateLabel();
|
||
} else if ( this.originalLabel ) {
|
||
this.options.label = this.originalLabel;
|
||
}
|
||
|
||
this._enhance();
|
||
|
||
if ( checked ) {
|
||
this._addClass( this.label, "ui-checkboxradio-checked", "ui-state-active" );
|
||
if ( this.icon ) {
|
||
this._addClass( this.icon, null, "ui-state-hover" );
|
||
}
|
||
}
|
||
|
||
this._on( {
|
||
change: "_toggleClasses",
|
||
focus: function() {
|
||
this._addClass( this.label, null, "ui-state-focus ui-visual-focus" );
|
||
},
|
||
blur: function() {
|
||
this._removeClass( this.label, null, "ui-state-focus ui-visual-focus" );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_readType: function() {
|
||
var nodeName = this.element[ 0 ].nodeName.toLowerCase();
|
||
this.type = this.element[ 0 ].type;
|
||
if ( nodeName !== "input" || !/radio|checkbox/.test( this.type ) ) {
|
||
$.error( "Can't create checkboxradio on element.nodeName=" + nodeName +
|
||
" and element.type=" + this.type );
|
||
}
|
||
},
|
||
|
||
// Support jQuery Mobile enhanced option
|
||
_enhance: function() {
|
||
this._updateIcon( this.element[ 0 ].checked );
|
||
},
|
||
|
||
widget: function() {
|
||
return this.label;
|
||
},
|
||
|
||
_getRadioGroup: function() {
|
||
var group;
|
||
var name = this.element[ 0 ].name;
|
||
var nameSelector = "input[name='" + $.ui.escapeSelector( name ) + "']";
|
||
|
||
if ( !name ) {
|
||
return $( [] );
|
||
}
|
||
|
||
if ( this.form.length ) {
|
||
group = $( this.form[ 0 ].elements ).filter( nameSelector );
|
||
} else {
|
||
|
||
// Not inside a form, check all inputs that also are not inside a form
|
||
group = $( nameSelector ).filter( function() {
|
||
return $( this ).form().length === 0;
|
||
} );
|
||
}
|
||
|
||
return group.not( this.element );
|
||
},
|
||
|
||
_toggleClasses: function() {
|
||
var checked = this.element[ 0 ].checked;
|
||
this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
|
||
|
||
if ( this.options.icon && this.type === "checkbox" ) {
|
||
this._toggleClass( this.icon, null, "ui-icon-check ui-state-checked", checked )
|
||
._toggleClass( this.icon, null, "ui-icon-blank", !checked );
|
||
}
|
||
|
||
if ( this.type === "radio" ) {
|
||
this._getRadioGroup()
|
||
.each( function() {
|
||
var instance = $( this ).checkboxradio( "instance" );
|
||
|
||
if ( instance ) {
|
||
instance._removeClass( instance.label,
|
||
"ui-checkboxradio-checked", "ui-state-active" );
|
||
}
|
||
} );
|
||
}
|
||
},
|
||
|
||
_destroy: function() {
|
||
this._unbindFormResetHandler();
|
||
|
||
if ( this.icon ) {
|
||
this.icon.remove();
|
||
this.iconSpace.remove();
|
||
}
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
|
||
// We don't allow the value to be set to nothing
|
||
if ( key === "label" && !value ) {
|
||
return;
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "disabled" ) {
|
||
this._toggleClass( this.label, null, "ui-state-disabled", value );
|
||
this.element[ 0 ].disabled = value;
|
||
|
||
// Don't refresh when setting disabled
|
||
return;
|
||
}
|
||
this.refresh();
|
||
},
|
||
|
||
_updateIcon: function( checked ) {
|
||
var toAdd = "ui-icon ui-icon-background ";
|
||
|
||
if ( this.options.icon ) {
|
||
if ( !this.icon ) {
|
||
this.icon = $( "<span>" );
|
||
this.iconSpace = $( "<span> </span>" );
|
||
this._addClass( this.iconSpace, "ui-checkboxradio-icon-space" );
|
||
}
|
||
|
||
if ( this.type === "checkbox" ) {
|
||
toAdd += checked ? "ui-icon-check ui-state-checked" : "ui-icon-blank";
|
||
this._removeClass( this.icon, null, checked ? "ui-icon-blank" : "ui-icon-check" );
|
||
} else {
|
||
toAdd += "ui-icon-blank";
|
||
}
|
||
this._addClass( this.icon, "ui-checkboxradio-icon", toAdd );
|
||
if ( !checked ) {
|
||
this._removeClass( this.icon, null, "ui-icon-check ui-state-checked" );
|
||
}
|
||
this.icon.prependTo( this.label ).after( this.iconSpace );
|
||
} else if ( this.icon !== undefined ) {
|
||
this.icon.remove();
|
||
this.iconSpace.remove();
|
||
delete this.icon;
|
||
}
|
||
},
|
||
|
||
_updateLabel: function() {
|
||
|
||
// Remove the contents of the label ( minus the icon, icon space, and input )
|
||
var contents = this.label.contents().not( this.element[ 0 ] );
|
||
if ( this.icon ) {
|
||
contents = contents.not( this.icon[ 0 ] );
|
||
}
|
||
if ( this.iconSpace ) {
|
||
contents = contents.not( this.iconSpace[ 0 ] );
|
||
}
|
||
contents.remove();
|
||
|
||
this.label.append( this.options.label );
|
||
},
|
||
|
||
refresh: function() {
|
||
var checked = this.element[ 0 ].checked,
|
||
isDisabled = this.element[ 0 ].disabled;
|
||
|
||
this._updateIcon( checked );
|
||
this._toggleClass( this.label, "ui-checkboxradio-checked", "ui-state-active", checked );
|
||
if ( this.options.label !== null ) {
|
||
this._updateLabel();
|
||
}
|
||
|
||
if ( isDisabled !== this.options.disabled ) {
|
||
this._setOptions( { "disabled": isDisabled } );
|
||
}
|
||
}
|
||
|
||
} ] );
|
||
|
||
var widgetsCheckboxradio = $.ui.checkboxradio;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Button 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Button
|
||
//>>group: Widgets
|
||
//>>description: Enhances a form with themeable buttons.
|
||
//>>docs: http://api.jqueryui.com/button/
|
||
//>>demos: http://jqueryui.com/button/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/button.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.button", {
|
||
version: "1.12.1",
|
||
defaultElement: "<button>",
|
||
options: {
|
||
classes: {
|
||
"ui-button": "ui-corner-all"
|
||
},
|
||
disabled: null,
|
||
icon: null,
|
||
iconPosition: "beginning",
|
||
label: null,
|
||
showLabel: true
|
||
},
|
||
|
||
_getCreateOptions: function() {
|
||
var disabled,
|
||
|
||
// This is to support cases like in jQuery Mobile where the base widget does have
|
||
// an implementation of _getCreateOptions
|
||
options = this._super() || {};
|
||
|
||
this.isInput = this.element.is( "input" );
|
||
|
||
disabled = this.element[ 0 ].disabled;
|
||
if ( disabled != null ) {
|
||
options.disabled = disabled;
|
||
}
|
||
|
||
this.originalLabel = this.isInput ? this.element.val() : this.element.html();
|
||
if ( this.originalLabel ) {
|
||
options.label = this.originalLabel;
|
||
}
|
||
|
||
return options;
|
||
},
|
||
|
||
_create: function() {
|
||
if ( !this.option.showLabel & !this.options.icon ) {
|
||
this.options.showLabel = true;
|
||
}
|
||
|
||
// We have to check the option again here even though we did in _getCreateOptions,
|
||
// because null may have been passed on init which would override what was set in
|
||
// _getCreateOptions
|
||
if ( this.options.disabled == null ) {
|
||
this.options.disabled = this.element[ 0 ].disabled || false;
|
||
}
|
||
|
||
this.hasTitle = !!this.element.attr( "title" );
|
||
|
||
// Check to see if the label needs to be set or if its already correct
|
||
if ( this.options.label && this.options.label !== this.originalLabel ) {
|
||
if ( this.isInput ) {
|
||
this.element.val( this.options.label );
|
||
} else {
|
||
this.element.html( this.options.label );
|
||
}
|
||
}
|
||
this._addClass( "ui-button", "ui-widget" );
|
||
this._setOption( "disabled", this.options.disabled );
|
||
this._enhance();
|
||
|
||
if ( this.element.is( "a" ) ) {
|
||
this._on( {
|
||
"keyup": function( event ) {
|
||
if ( event.keyCode === $.ui.keyCode.SPACE ) {
|
||
event.preventDefault();
|
||
|
||
// Support: PhantomJS <= 1.9, IE 8 Only
|
||
// If a native click is available use it so we actually cause navigation
|
||
// otherwise just trigger a click event
|
||
if ( this.element[ 0 ].click ) {
|
||
this.element[ 0 ].click();
|
||
} else {
|
||
this.element.trigger( "click" );
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
},
|
||
|
||
_enhance: function() {
|
||
if ( !this.element.is( "button" ) ) {
|
||
this.element.attr( "role", "button" );
|
||
}
|
||
|
||
if ( this.options.icon ) {
|
||
this._updateIcon( "icon", this.options.icon );
|
||
this._updateTooltip();
|
||
}
|
||
},
|
||
|
||
_updateTooltip: function() {
|
||
this.title = this.element.attr( "title" );
|
||
|
||
if ( !this.options.showLabel && !this.title ) {
|
||
this.element.attr( "title", this.options.label );
|
||
}
|
||
},
|
||
|
||
_updateIcon: function( option, value ) {
|
||
var icon = option !== "iconPosition",
|
||
position = icon ? this.options.iconPosition : value,
|
||
displayBlock = position === "top" || position === "bottom";
|
||
|
||
// Create icon
|
||
if ( !this.icon ) {
|
||
this.icon = $( "<span>" );
|
||
|
||
this._addClass( this.icon, "ui-button-icon", "ui-icon" );
|
||
|
||
if ( !this.options.showLabel ) {
|
||
this._addClass( "ui-button-icon-only" );
|
||
}
|
||
} else if ( icon ) {
|
||
|
||
// If we are updating the icon remove the old icon class
|
||
this._removeClass( this.icon, null, this.options.icon );
|
||
}
|
||
|
||
// If we are updating the icon add the new icon class
|
||
if ( icon ) {
|
||
this._addClass( this.icon, null, value );
|
||
}
|
||
|
||
this._attachIcon( position );
|
||
|
||
// If the icon is on top or bottom we need to add the ui-widget-icon-block class and remove
|
||
// the iconSpace if there is one.
|
||
if ( displayBlock ) {
|
||
this._addClass( this.icon, null, "ui-widget-icon-block" );
|
||
if ( this.iconSpace ) {
|
||
this.iconSpace.remove();
|
||
}
|
||
} else {
|
||
|
||
// Position is beginning or end so remove the ui-widget-icon-block class and add the
|
||
// space if it does not exist
|
||
if ( !this.iconSpace ) {
|
||
this.iconSpace = $( "<span> </span>" );
|
||
this._addClass( this.iconSpace, "ui-button-icon-space" );
|
||
}
|
||
this._removeClass( this.icon, null, "ui-wiget-icon-block" );
|
||
this._attachIconSpace( position );
|
||
}
|
||
},
|
||
|
||
_destroy: function() {
|
||
this.element.removeAttr( "role" );
|
||
|
||
if ( this.icon ) {
|
||
this.icon.remove();
|
||
}
|
||
if ( this.iconSpace ) {
|
||
this.iconSpace.remove();
|
||
}
|
||
if ( !this.hasTitle ) {
|
||
this.element.removeAttr( "title" );
|
||
}
|
||
},
|
||
|
||
_attachIconSpace: function( iconPosition ) {
|
||
this.icon[ /^(?:end|bottom)/.test( iconPosition ) ? "before" : "after" ]( this.iconSpace );
|
||
},
|
||
|
||
_attachIcon: function( iconPosition ) {
|
||
this.element[ /^(?:end|bottom)/.test( iconPosition ) ? "append" : "prepend" ]( this.icon );
|
||
},
|
||
|
||
_setOptions: function( options ) {
|
||
var newShowLabel = options.showLabel === undefined ?
|
||
this.options.showLabel :
|
||
options.showLabel,
|
||
newIcon = options.icon === undefined ? this.options.icon : options.icon;
|
||
|
||
if ( !newShowLabel && !newIcon ) {
|
||
options.showLabel = true;
|
||
}
|
||
this._super( options );
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "icon" ) {
|
||
if ( value ) {
|
||
this._updateIcon( key, value );
|
||
} else if ( this.icon ) {
|
||
this.icon.remove();
|
||
if ( this.iconSpace ) {
|
||
this.iconSpace.remove();
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( key === "iconPosition" ) {
|
||
this._updateIcon( key, value );
|
||
}
|
||
|
||
// Make sure we can't end up with a button that has neither text nor icon
|
||
if ( key === "showLabel" ) {
|
||
this._toggleClass( "ui-button-icon-only", null, !value );
|
||
this._updateTooltip();
|
||
}
|
||
|
||
if ( key === "label" ) {
|
||
if ( this.isInput ) {
|
||
this.element.val( value );
|
||
} else {
|
||
|
||
// If there is an icon, append it, else nothing then append the value
|
||
// this avoids removal of the icon when setting label text
|
||
this.element.html( value );
|
||
if ( this.icon ) {
|
||
this._attachIcon( this.options.iconPosition );
|
||
this._attachIconSpace( this.options.iconPosition );
|
||
}
|
||
}
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "disabled" ) {
|
||
this._toggleClass( null, "ui-state-disabled", value );
|
||
this.element[ 0 ].disabled = value;
|
||
if ( value ) {
|
||
this.element.blur();
|
||
}
|
||
}
|
||
},
|
||
|
||
refresh: function() {
|
||
|
||
// Make sure to only check disabled if its an element that supports this otherwise
|
||
// check for the disabled class to determine state
|
||
var isDisabled = this.element.is( "input, button" ) ?
|
||
this.element[ 0 ].disabled : this.element.hasClass( "ui-button-disabled" );
|
||
|
||
if ( isDisabled !== this.options.disabled ) {
|
||
this._setOptions( { disabled: isDisabled } );
|
||
}
|
||
|
||
this._updateTooltip();
|
||
}
|
||
} );
|
||
|
||
// DEPRECATED
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Text and Icons options
|
||
$.widget( "ui.button", $.ui.button, {
|
||
options: {
|
||
text: true,
|
||
icons: {
|
||
primary: null,
|
||
secondary: null
|
||
}
|
||
},
|
||
|
||
_create: function() {
|
||
if ( this.options.showLabel && !this.options.text ) {
|
||
this.options.showLabel = this.options.text;
|
||
}
|
||
if ( !this.options.showLabel && this.options.text ) {
|
||
this.options.text = this.options.showLabel;
|
||
}
|
||
if ( !this.options.icon && ( this.options.icons.primary ||
|
||
this.options.icons.secondary ) ) {
|
||
if ( this.options.icons.primary ) {
|
||
this.options.icon = this.options.icons.primary;
|
||
} else {
|
||
this.options.icon = this.options.icons.secondary;
|
||
this.options.iconPosition = "end";
|
||
}
|
||
} else if ( this.options.icon ) {
|
||
this.options.icons.primary = this.options.icon;
|
||
}
|
||
this._super();
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "text" ) {
|
||
this._super( "showLabel", value );
|
||
return;
|
||
}
|
||
if ( key === "showLabel" ) {
|
||
this.options.text = value;
|
||
}
|
||
if ( key === "icon" ) {
|
||
this.options.icons.primary = value;
|
||
}
|
||
if ( key === "icons" ) {
|
||
if ( value.primary ) {
|
||
this._super( "icon", value.primary );
|
||
this._super( "iconPosition", "beginning" );
|
||
} else if ( value.secondary ) {
|
||
this._super( "icon", value.secondary );
|
||
this._super( "iconPosition", "end" );
|
||
}
|
||
}
|
||
this._superApply( arguments );
|
||
}
|
||
} );
|
||
|
||
$.fn.button = ( function( orig ) {
|
||
return function() {
|
||
if ( !this.length || ( this.length && this[ 0 ].tagName !== "INPUT" ) ||
|
||
( this.length && this[ 0 ].tagName === "INPUT" && (
|
||
this.attr( "type" ) !== "checkbox" && this.attr( "type" ) !== "radio"
|
||
) ) ) {
|
||
return orig.apply( this, arguments );
|
||
}
|
||
if ( !$.ui.checkboxradio ) {
|
||
$.error( "Checkboxradio widget missing" );
|
||
}
|
||
if ( arguments.length === 0 ) {
|
||
return this.checkboxradio( {
|
||
"icon": false
|
||
} );
|
||
}
|
||
return this.checkboxradio.apply( this, arguments );
|
||
};
|
||
} )( $.fn.button );
|
||
|
||
$.fn.buttonset = function() {
|
||
if ( !$.ui.controlgroup ) {
|
||
$.error( "Controlgroup widget missing" );
|
||
}
|
||
if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" && arguments[ 2 ] ) {
|
||
return this.controlgroup.apply( this,
|
||
[ arguments[ 0 ], "items.button", arguments[ 2 ] ] );
|
||
}
|
||
if ( arguments[ 0 ] === "option" && arguments[ 1 ] === "items" ) {
|
||
return this.controlgroup.apply( this, [ arguments[ 0 ], "items.button" ] );
|
||
}
|
||
if ( typeof arguments[ 0 ] === "object" && arguments[ 0 ].items ) {
|
||
arguments[ 0 ].items = {
|
||
button: arguments[ 0 ].items
|
||
};
|
||
}
|
||
return this.controlgroup.apply( this, arguments );
|
||
};
|
||
}
|
||
|
||
var widgetsButton = $.ui.button;
|
||
|
||
|
||
// jscs:disable maximumLineLength
|
||
/* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
|
||
/*!
|
||
* jQuery UI Datepicker 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Datepicker
|
||
//>>group: Widgets
|
||
//>>description: Displays a calendar from an input or inline for selecting dates.
|
||
//>>docs: http://api.jqueryui.com/datepicker/
|
||
//>>demos: http://jqueryui.com/datepicker/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/datepicker.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.extend( $.ui, { datepicker: { version: "1.12.1" } } );
|
||
|
||
var datepicker_instActive;
|
||
|
||
function datepicker_getZindex( elem ) {
|
||
var position, value;
|
||
while ( elem.length && elem[ 0 ] !== document ) {
|
||
|
||
// Ignore z-index if position is set to a value where z-index is ignored by the browser
|
||
// This makes behavior of this function consistent across browsers
|
||
// WebKit always returns auto if the element is positioned
|
||
position = elem.css( "position" );
|
||
if ( position === "absolute" || position === "relative" || position === "fixed" ) {
|
||
|
||
// IE returns 0 when zIndex is not specified
|
||
// other browsers return a string
|
||
// we ignore the case of nested elements with an explicit value of 0
|
||
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
|
||
value = parseInt( elem.css( "zIndex" ), 10 );
|
||
if ( !isNaN( value ) && value !== 0 ) {
|
||
return value;
|
||
}
|
||
}
|
||
elem = elem.parent();
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
/* Date picker manager.
|
||
Use the singleton instance of this class, $.datepicker, to interact with the date picker.
|
||
Settings for (groups of) date pickers are maintained in an instance object,
|
||
allowing multiple different settings on the same page. */
|
||
|
||
function Datepicker() {
|
||
this._curInst = null; // The current instance in use
|
||
this._keyEvent = false; // If the last event was a key event
|
||
this._disabledInputs = []; // List of date picker inputs that have been disabled
|
||
this._datepickerShowing = false; // True if the popup picker is showing , false if not
|
||
this._inDialog = false; // True if showing within a "dialog", false if not
|
||
this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
|
||
this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
|
||
this._appendClass = "ui-datepicker-append"; // The name of the append marker class
|
||
this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
|
||
this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
|
||
this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
|
||
this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
|
||
this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
|
||
this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
|
||
this.regional = []; // Available regional settings, indexed by language code
|
||
this.regional[ "" ] = { // Default regional settings
|
||
closeText: "Done", // Display text for close link
|
||
prevText: "Prev", // Display text for previous month link
|
||
nextText: "Next", // Display text for next month link
|
||
currentText: "Today", // Display text for current month link
|
||
monthNames: [ "January","February","March","April","May","June",
|
||
"July","August","September","October","November","December" ], // Names of months for drop-down and formatting
|
||
monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], // For formatting
|
||
dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], // For formatting
|
||
dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], // For formatting
|
||
dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], // Column headings for days starting at Sunday
|
||
weekHeader: "Wk", // Column header for week of the year
|
||
dateFormat: "mm/dd/yy", // See format options on parseDate
|
||
firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
|
||
isRTL: false, // True if right-to-left language, false if left-to-right
|
||
showMonthAfterYear: false, // True if the year select precedes month, false for month then year
|
||
yearSuffix: "" // Additional text to append to the year in the month headers
|
||
};
|
||
this._defaults = { // Global defaults for all the date picker instances
|
||
showOn: "focus", // "focus" for popup on focus,
|
||
// "button" for trigger button, or "both" for either
|
||
showAnim: "fadeIn", // Name of jQuery animation for popup
|
||
showOptions: {}, // Options for enhanced animations
|
||
defaultDate: null, // Used when field is blank: actual date,
|
||
// +/-number for offset from today, null for today
|
||
appendText: "", // Display text following the input box, e.g. showing the format
|
||
buttonText: "...", // Text for trigger button
|
||
buttonImage: "", // URL for trigger button image
|
||
buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
|
||
hideIfNoPrevNext: false, // True to hide next/previous month links
|
||
// if not applicable, false to just disable them
|
||
navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
|
||
gotoCurrent: false, // True if today link goes back to current selection instead
|
||
changeMonth: false, // True if month can be selected directly, false if only prev/next
|
||
changeYear: false, // True if year can be selected directly, false if only prev/next
|
||
yearRange: "c-10:c+10", // Range of years to display in drop-down,
|
||
// either relative to today's year (-nn:+nn), relative to currently displayed year
|
||
// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
|
||
showOtherMonths: false, // True to show dates in other months, false to leave blank
|
||
selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
|
||
showWeek: false, // True to show week of the year, false to not show it
|
||
calculateWeek: this.iso8601Week, // How to calculate the week of the year,
|
||
// takes a Date and returns the number of the week for it
|
||
shortYearCutoff: "+10", // Short year values < this are in the current century,
|
||
// > this are in the previous century,
|
||
// string value starting with "+" for current year + value
|
||
minDate: null, // The earliest selectable date, or null for no limit
|
||
maxDate: null, // The latest selectable date, or null for no limit
|
||
duration: "fast", // Duration of display/closure
|
||
beforeShowDay: null, // Function that takes a date and returns an array with
|
||
// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
|
||
// [2] = cell title (optional), e.g. $.datepicker.noWeekends
|
||
beforeShow: null, // Function that takes an input field and
|
||
// returns a set of custom settings for the date picker
|
||
onSelect: null, // Define a callback function when a date is selected
|
||
onChangeMonthYear: null, // Define a callback function when the month or year is changed
|
||
onClose: null, // Define a callback function when the datepicker is closed
|
||
numberOfMonths: 1, // Number of months to show at a time
|
||
showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
|
||
stepMonths: 1, // Number of months to step back/forward
|
||
stepBigMonths: 12, // Number of months to step back/forward for the big links
|
||
altField: "", // Selector for an alternate field to store selected dates into
|
||
altFormat: "", // The date format to use for the alternate field
|
||
constrainInput: true, // The input is constrained by the current date format
|
||
showButtonPanel: false, // True to show button panel, false to not show it
|
||
autoSize: false, // True to size the input for the date format, false to leave as is
|
||
disabled: false // The initial disabled state
|
||
};
|
||
$.extend( this._defaults, this.regional[ "" ] );
|
||
this.regional.en = $.extend( true, {}, this.regional[ "" ] );
|
||
this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
|
||
this.dpDiv = datepicker_bindHover( $( "<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) );
|
||
}
|
||
|
||
$.extend( Datepicker.prototype, {
|
||
/* Class name added to elements to indicate already configured with a date picker. */
|
||
markerClassName: "hasDatepicker",
|
||
|
||
//Keep track of the maximum number of rows displayed (see #7043)
|
||
maxRows: 4,
|
||
|
||
// TODO rename to "widget" when switching to widget factory
|
||
_widgetDatepicker: function() {
|
||
return this.dpDiv;
|
||
},
|
||
|
||
/* Override the default settings for all instances of the date picker.
|
||
* @param settings object - the new settings to use as defaults (anonymous object)
|
||
* @return the manager object
|
||
*/
|
||
setDefaults: function( settings ) {
|
||
datepicker_extendRemove( this._defaults, settings || {} );
|
||
return this;
|
||
},
|
||
|
||
/* Attach the date picker to a jQuery selection.
|
||
* @param target element - the target input field or division or span
|
||
* @param settings object - the new settings to use for this date picker instance (anonymous)
|
||
*/
|
||
_attachDatepicker: function( target, settings ) {
|
||
var nodeName, inline, inst;
|
||
nodeName = target.nodeName.toLowerCase();
|
||
inline = ( nodeName === "div" || nodeName === "span" );
|
||
if ( !target.id ) {
|
||
this.uuid += 1;
|
||
target.id = "dp" + this.uuid;
|
||
}
|
||
inst = this._newInst( $( target ), inline );
|
||
inst.settings = $.extend( {}, settings || {} );
|
||
if ( nodeName === "input" ) {
|
||
this._connectDatepicker( target, inst );
|
||
} else if ( inline ) {
|
||
this._inlineDatepicker( target, inst );
|
||
}
|
||
},
|
||
|
||
/* Create a new instance object. */
|
||
_newInst: function( target, inline ) {
|
||
var id = target[ 0 ].id.replace( /([^A-Za-z0-9_\-])/g, "\\\\$1" ); // escape jQuery meta chars
|
||
return { id: id, input: target, // associated target
|
||
selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
|
||
drawMonth: 0, drawYear: 0, // month being drawn
|
||
inline: inline, // is datepicker inline or not
|
||
dpDiv: ( !inline ? this.dpDiv : // presentation div
|
||
datepicker_bindHover( $( "<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>" ) ) ) };
|
||
},
|
||
|
||
/* Attach the date picker to an input field. */
|
||
_connectDatepicker: function( target, inst ) {
|
||
var input = $( target );
|
||
inst.append = $( [] );
|
||
inst.trigger = $( [] );
|
||
if ( input.hasClass( this.markerClassName ) ) {
|
||
return;
|
||
}
|
||
this._attachments( input, inst );
|
||
input.addClass( this.markerClassName ).on( "keydown", this._doKeyDown ).
|
||
on( "keypress", this._doKeyPress ).on( "keyup", this._doKeyUp );
|
||
this._autoSize( inst );
|
||
$.data( target, "datepicker", inst );
|
||
|
||
//If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
|
||
if ( inst.settings.disabled ) {
|
||
this._disableDatepicker( target );
|
||
}
|
||
},
|
||
|
||
/* Make attachments based on settings. */
|
||
_attachments: function( input, inst ) {
|
||
var showOn, buttonText, buttonImage,
|
||
appendText = this._get( inst, "appendText" ),
|
||
isRTL = this._get( inst, "isRTL" );
|
||
|
||
if ( inst.append ) {
|
||
inst.append.remove();
|
||
}
|
||
if ( appendText ) {
|
||
inst.append = $( "<span class='" + this._appendClass + "'>" + appendText + "</span>" );
|
||
input[ isRTL ? "before" : "after" ]( inst.append );
|
||
}
|
||
|
||
input.off( "focus", this._showDatepicker );
|
||
|
||
if ( inst.trigger ) {
|
||
inst.trigger.remove();
|
||
}
|
||
|
||
showOn = this._get( inst, "showOn" );
|
||
if ( showOn === "focus" || showOn === "both" ) { // pop-up date picker when in the marked field
|
||
input.on( "focus", this._showDatepicker );
|
||
}
|
||
if ( showOn === "button" || showOn === "both" ) { // pop-up date picker when button clicked
|
||
buttonText = this._get( inst, "buttonText" );
|
||
buttonImage = this._get( inst, "buttonImage" );
|
||
inst.trigger = $( this._get( inst, "buttonImageOnly" ) ?
|
||
$( "<img/>" ).addClass( this._triggerClass ).
|
||
attr( { src: buttonImage, alt: buttonText, title: buttonText } ) :
|
||
$( "<button type='button'></button>" ).addClass( this._triggerClass ).
|
||
html( !buttonImage ? buttonText : $( "<img/>" ).attr(
|
||
{ src:buttonImage, alt:buttonText, title:buttonText } ) ) );
|
||
input[ isRTL ? "before" : "after" ]( inst.trigger );
|
||
inst.trigger.on( "click", function() {
|
||
if ( $.datepicker._datepickerShowing && $.datepicker._lastInput === input[ 0 ] ) {
|
||
$.datepicker._hideDatepicker();
|
||
} else if ( $.datepicker._datepickerShowing && $.datepicker._lastInput !== input[ 0 ] ) {
|
||
$.datepicker._hideDatepicker();
|
||
$.datepicker._showDatepicker( input[ 0 ] );
|
||
} else {
|
||
$.datepicker._showDatepicker( input[ 0 ] );
|
||
}
|
||
return false;
|
||
} );
|
||
}
|
||
},
|
||
|
||
/* Apply the maximum length for the date format. */
|
||
_autoSize: function( inst ) {
|
||
if ( this._get( inst, "autoSize" ) && !inst.inline ) {
|
||
var findMax, max, maxI, i,
|
||
date = new Date( 2009, 12 - 1, 20 ), // Ensure double digits
|
||
dateFormat = this._get( inst, "dateFormat" );
|
||
|
||
if ( dateFormat.match( /[DM]/ ) ) {
|
||
findMax = function( names ) {
|
||
max = 0;
|
||
maxI = 0;
|
||
for ( i = 0; i < names.length; i++ ) {
|
||
if ( names[ i ].length > max ) {
|
||
max = names[ i ].length;
|
||
maxI = i;
|
||
}
|
||
}
|
||
return maxI;
|
||
};
|
||
date.setMonth( findMax( this._get( inst, ( dateFormat.match( /MM/ ) ?
|
||
"monthNames" : "monthNamesShort" ) ) ) );
|
||
date.setDate( findMax( this._get( inst, ( dateFormat.match( /DD/ ) ?
|
||
"dayNames" : "dayNamesShort" ) ) ) + 20 - date.getDay() );
|
||
}
|
||
inst.input.attr( "size", this._formatDate( inst, date ).length );
|
||
}
|
||
},
|
||
|
||
/* Attach an inline date picker to a div. */
|
||
_inlineDatepicker: function( target, inst ) {
|
||
var divSpan = $( target );
|
||
if ( divSpan.hasClass( this.markerClassName ) ) {
|
||
return;
|
||
}
|
||
divSpan.addClass( this.markerClassName ).append( inst.dpDiv );
|
||
$.data( target, "datepicker", inst );
|
||
this._setDate( inst, this._getDefaultDate( inst ), true );
|
||
this._updateDatepicker( inst );
|
||
this._updateAlternate( inst );
|
||
|
||
//If disabled option is true, disable the datepicker before showing it (see ticket #5665)
|
||
if ( inst.settings.disabled ) {
|
||
this._disableDatepicker( target );
|
||
}
|
||
|
||
// Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
|
||
// http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
|
||
inst.dpDiv.css( "display", "block" );
|
||
},
|
||
|
||
/* Pop-up the date picker in a "dialog" box.
|
||
* @param input element - ignored
|
||
* @param date string or Date - the initial date to display
|
||
* @param onSelect function - the function to call when a date is selected
|
||
* @param settings object - update the dialog date picker instance's settings (anonymous object)
|
||
* @param pos int[2] - coordinates for the dialog's position within the screen or
|
||
* event - with x/y coordinates or
|
||
* leave empty for default (screen centre)
|
||
* @return the manager object
|
||
*/
|
||
_dialogDatepicker: function( input, date, onSelect, settings, pos ) {
|
||
var id, browserWidth, browserHeight, scrollX, scrollY,
|
||
inst = this._dialogInst; // internal instance
|
||
|
||
if ( !inst ) {
|
||
this.uuid += 1;
|
||
id = "dp" + this.uuid;
|
||
this._dialogInput = $( "<input type='text' id='" + id +
|
||
"' style='position: absolute; top: -100px; width: 0px;'/>" );
|
||
this._dialogInput.on( "keydown", this._doKeyDown );
|
||
$( "body" ).append( this._dialogInput );
|
||
inst = this._dialogInst = this._newInst( this._dialogInput, false );
|
||
inst.settings = {};
|
||
$.data( this._dialogInput[ 0 ], "datepicker", inst );
|
||
}
|
||
datepicker_extendRemove( inst.settings, settings || {} );
|
||
date = ( date && date.constructor === Date ? this._formatDate( inst, date ) : date );
|
||
this._dialogInput.val( date );
|
||
|
||
this._pos = ( pos ? ( pos.length ? pos : [ pos.pageX, pos.pageY ] ) : null );
|
||
if ( !this._pos ) {
|
||
browserWidth = document.documentElement.clientWidth;
|
||
browserHeight = document.documentElement.clientHeight;
|
||
scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
|
||
scrollY = document.documentElement.scrollTop || document.body.scrollTop;
|
||
this._pos = // should use actual width/height below
|
||
[ ( browserWidth / 2 ) - 100 + scrollX, ( browserHeight / 2 ) - 150 + scrollY ];
|
||
}
|
||
|
||
// Move input on screen for focus, but hidden behind dialog
|
||
this._dialogInput.css( "left", ( this._pos[ 0 ] + 20 ) + "px" ).css( "top", this._pos[ 1 ] + "px" );
|
||
inst.settings.onSelect = onSelect;
|
||
this._inDialog = true;
|
||
this.dpDiv.addClass( this._dialogClass );
|
||
this._showDatepicker( this._dialogInput[ 0 ] );
|
||
if ( $.blockUI ) {
|
||
$.blockUI( this.dpDiv );
|
||
}
|
||
$.data( this._dialogInput[ 0 ], "datepicker", inst );
|
||
return this;
|
||
},
|
||
|
||
/* Detach a datepicker from its control.
|
||
* @param target element - the target input field or division or span
|
||
*/
|
||
_destroyDatepicker: function( target ) {
|
||
var nodeName,
|
||
$target = $( target ),
|
||
inst = $.data( target, "datepicker" );
|
||
|
||
if ( !$target.hasClass( this.markerClassName ) ) {
|
||
return;
|
||
}
|
||
|
||
nodeName = target.nodeName.toLowerCase();
|
||
$.removeData( target, "datepicker" );
|
||
if ( nodeName === "input" ) {
|
||
inst.append.remove();
|
||
inst.trigger.remove();
|
||
$target.removeClass( this.markerClassName ).
|
||
off( "focus", this._showDatepicker ).
|
||
off( "keydown", this._doKeyDown ).
|
||
off( "keypress", this._doKeyPress ).
|
||
off( "keyup", this._doKeyUp );
|
||
} else if ( nodeName === "div" || nodeName === "span" ) {
|
||
$target.removeClass( this.markerClassName ).empty();
|
||
}
|
||
|
||
if ( datepicker_instActive === inst ) {
|
||
datepicker_instActive = null;
|
||
}
|
||
},
|
||
|
||
/* Enable the date picker to a jQuery selection.
|
||
* @param target element - the target input field or division or span
|
||
*/
|
||
_enableDatepicker: function( target ) {
|
||
var nodeName, inline,
|
||
$target = $( target ),
|
||
inst = $.data( target, "datepicker" );
|
||
|
||
if ( !$target.hasClass( this.markerClassName ) ) {
|
||
return;
|
||
}
|
||
|
||
nodeName = target.nodeName.toLowerCase();
|
||
if ( nodeName === "input" ) {
|
||
target.disabled = false;
|
||
inst.trigger.filter( "button" ).
|
||
each( function() { this.disabled = false; } ).end().
|
||
filter( "img" ).css( { opacity: "1.0", cursor: "" } );
|
||
} else if ( nodeName === "div" || nodeName === "span" ) {
|
||
inline = $target.children( "." + this._inlineClass );
|
||
inline.children().removeClass( "ui-state-disabled" );
|
||
inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
|
||
prop( "disabled", false );
|
||
}
|
||
this._disabledInputs = $.map( this._disabledInputs,
|
||
function( value ) { return ( value === target ? null : value ); } ); // delete entry
|
||
},
|
||
|
||
/* Disable the date picker to a jQuery selection.
|
||
* @param target element - the target input field or division or span
|
||
*/
|
||
_disableDatepicker: function( target ) {
|
||
var nodeName, inline,
|
||
$target = $( target ),
|
||
inst = $.data( target, "datepicker" );
|
||
|
||
if ( !$target.hasClass( this.markerClassName ) ) {
|
||
return;
|
||
}
|
||
|
||
nodeName = target.nodeName.toLowerCase();
|
||
if ( nodeName === "input" ) {
|
||
target.disabled = true;
|
||
inst.trigger.filter( "button" ).
|
||
each( function() { this.disabled = true; } ).end().
|
||
filter( "img" ).css( { opacity: "0.5", cursor: "default" } );
|
||
} else if ( nodeName === "div" || nodeName === "span" ) {
|
||
inline = $target.children( "." + this._inlineClass );
|
||
inline.children().addClass( "ui-state-disabled" );
|
||
inline.find( "select.ui-datepicker-month, select.ui-datepicker-year" ).
|
||
prop( "disabled", true );
|
||
}
|
||
this._disabledInputs = $.map( this._disabledInputs,
|
||
function( value ) { return ( value === target ? null : value ); } ); // delete entry
|
||
this._disabledInputs[ this._disabledInputs.length ] = target;
|
||
},
|
||
|
||
/* Is the first field in a jQuery collection disabled as a datepicker?
|
||
* @param target element - the target input field or division or span
|
||
* @return boolean - true if disabled, false if enabled
|
||
*/
|
||
_isDisabledDatepicker: function( target ) {
|
||
if ( !target ) {
|
||
return false;
|
||
}
|
||
for ( var i = 0; i < this._disabledInputs.length; i++ ) {
|
||
if ( this._disabledInputs[ i ] === target ) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
|
||
/* Retrieve the instance data for the target control.
|
||
* @param target element - the target input field or division or span
|
||
* @return object - the associated instance data
|
||
* @throws error if a jQuery problem getting data
|
||
*/
|
||
_getInst: function( target ) {
|
||
try {
|
||
return $.data( target, "datepicker" );
|
||
}
|
||
catch ( err ) {
|
||
throw "Missing instance data for this datepicker";
|
||
}
|
||
},
|
||
|
||
/* Update or retrieve the settings for a date picker attached to an input field or division.
|
||
* @param target element - the target input field or division or span
|
||
* @param name object - the new settings to update or
|
||
* string - the name of the setting to change or retrieve,
|
||
* when retrieving also "all" for all instance settings or
|
||
* "defaults" for all global defaults
|
||
* @param value any - the new value for the setting
|
||
* (omit if above is an object or to retrieve a value)
|
||
*/
|
||
_optionDatepicker: function( target, name, value ) {
|
||
var settings, date, minDate, maxDate,
|
||
inst = this._getInst( target );
|
||
|
||
if ( arguments.length === 2 && typeof name === "string" ) {
|
||
return ( name === "defaults" ? $.extend( {}, $.datepicker._defaults ) :
|
||
( inst ? ( name === "all" ? $.extend( {}, inst.settings ) :
|
||
this._get( inst, name ) ) : null ) );
|
||
}
|
||
|
||
settings = name || {};
|
||
if ( typeof name === "string" ) {
|
||
settings = {};
|
||
settings[ name ] = value;
|
||
}
|
||
|
||
if ( inst ) {
|
||
if ( this._curInst === inst ) {
|
||
this._hideDatepicker();
|
||
}
|
||
|
||
date = this._getDateDatepicker( target, true );
|
||
minDate = this._getMinMaxDate( inst, "min" );
|
||
maxDate = this._getMinMaxDate( inst, "max" );
|
||
datepicker_extendRemove( inst.settings, settings );
|
||
|
||
// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
|
||
if ( minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined ) {
|
||
inst.settings.minDate = this._formatDate( inst, minDate );
|
||
}
|
||
if ( maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined ) {
|
||
inst.settings.maxDate = this._formatDate( inst, maxDate );
|
||
}
|
||
if ( "disabled" in settings ) {
|
||
if ( settings.disabled ) {
|
||
this._disableDatepicker( target );
|
||
} else {
|
||
this._enableDatepicker( target );
|
||
}
|
||
}
|
||
this._attachments( $( target ), inst );
|
||
this._autoSize( inst );
|
||
this._setDate( inst, date );
|
||
this._updateAlternate( inst );
|
||
this._updateDatepicker( inst );
|
||
}
|
||
},
|
||
|
||
// Change method deprecated
|
||
_changeDatepicker: function( target, name, value ) {
|
||
this._optionDatepicker( target, name, value );
|
||
},
|
||
|
||
/* Redraw the date picker attached to an input field or division.
|
||
* @param target element - the target input field or division or span
|
||
*/
|
||
_refreshDatepicker: function( target ) {
|
||
var inst = this._getInst( target );
|
||
if ( inst ) {
|
||
this._updateDatepicker( inst );
|
||
}
|
||
},
|
||
|
||
/* Set the dates for a jQuery selection.
|
||
* @param target element - the target input field or division or span
|
||
* @param date Date - the new date
|
||
*/
|
||
_setDateDatepicker: function( target, date ) {
|
||
var inst = this._getInst( target );
|
||
if ( inst ) {
|
||
this._setDate( inst, date );
|
||
this._updateDatepicker( inst );
|
||
this._updateAlternate( inst );
|
||
}
|
||
},
|
||
|
||
/* Get the date(s) for the first entry in a jQuery selection.
|
||
* @param target element - the target input field or division or span
|
||
* @param noDefault boolean - true if no default date is to be used
|
||
* @return Date - the current date
|
||
*/
|
||
_getDateDatepicker: function( target, noDefault ) {
|
||
var inst = this._getInst( target );
|
||
if ( inst && !inst.inline ) {
|
||
this._setDateFromField( inst, noDefault );
|
||
}
|
||
return ( inst ? this._getDate( inst ) : null );
|
||
},
|
||
|
||
/* Handle keystrokes. */
|
||
_doKeyDown: function( event ) {
|
||
var onSelect, dateStr, sel,
|
||
inst = $.datepicker._getInst( event.target ),
|
||
handled = true,
|
||
isRTL = inst.dpDiv.is( ".ui-datepicker-rtl" );
|
||
|
||
inst._keyEvent = true;
|
||
if ( $.datepicker._datepickerShowing ) {
|
||
switch ( event.keyCode ) {
|
||
case 9: $.datepicker._hideDatepicker();
|
||
handled = false;
|
||
break; // hide on tab out
|
||
case 13: sel = $( "td." + $.datepicker._dayOverClass + ":not(." +
|
||
$.datepicker._currentClass + ")", inst.dpDiv );
|
||
if ( sel[ 0 ] ) {
|
||
$.datepicker._selectDay( event.target, inst.selectedMonth, inst.selectedYear, sel[ 0 ] );
|
||
}
|
||
|
||
onSelect = $.datepicker._get( inst, "onSelect" );
|
||
if ( onSelect ) {
|
||
dateStr = $.datepicker._formatDate( inst );
|
||
|
||
// Trigger custom callback
|
||
onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] );
|
||
} else {
|
||
$.datepicker._hideDatepicker();
|
||
}
|
||
|
||
return false; // don't submit the form
|
||
case 27: $.datepicker._hideDatepicker();
|
||
break; // hide on escape
|
||
case 33: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
|
||
-$.datepicker._get( inst, "stepBigMonths" ) :
|
||
-$.datepicker._get( inst, "stepMonths" ) ), "M" );
|
||
break; // previous month/year on page up/+ ctrl
|
||
case 34: $.datepicker._adjustDate( event.target, ( event.ctrlKey ?
|
||
+$.datepicker._get( inst, "stepBigMonths" ) :
|
||
+$.datepicker._get( inst, "stepMonths" ) ), "M" );
|
||
break; // next month/year on page down/+ ctrl
|
||
case 35: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._clearDate( event.target );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
break; // clear on ctrl or command +end
|
||
case 36: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._gotoToday( event.target );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
break; // current on ctrl or command +home
|
||
case 37: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._adjustDate( event.target, ( isRTL ? +1 : -1 ), "D" );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
|
||
// -1 day on ctrl or command +left
|
||
if ( event.originalEvent.altKey ) {
|
||
$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
|
||
-$.datepicker._get( inst, "stepBigMonths" ) :
|
||
-$.datepicker._get( inst, "stepMonths" ) ), "M" );
|
||
}
|
||
|
||
// next month/year on alt +left on Mac
|
||
break;
|
||
case 38: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._adjustDate( event.target, -7, "D" );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
break; // -1 week on ctrl or command +up
|
||
case 39: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._adjustDate( event.target, ( isRTL ? -1 : +1 ), "D" );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
|
||
// +1 day on ctrl or command +right
|
||
if ( event.originalEvent.altKey ) {
|
||
$.datepicker._adjustDate( event.target, ( event.ctrlKey ?
|
||
+$.datepicker._get( inst, "stepBigMonths" ) :
|
||
+$.datepicker._get( inst, "stepMonths" ) ), "M" );
|
||
}
|
||
|
||
// next month/year on alt +right
|
||
break;
|
||
case 40: if ( event.ctrlKey || event.metaKey ) {
|
||
$.datepicker._adjustDate( event.target, +7, "D" );
|
||
}
|
||
handled = event.ctrlKey || event.metaKey;
|
||
break; // +1 week on ctrl or command +down
|
||
default: handled = false;
|
||
}
|
||
} else if ( event.keyCode === 36 && event.ctrlKey ) { // display the date picker on ctrl+home
|
||
$.datepicker._showDatepicker( this );
|
||
} else {
|
||
handled = false;
|
||
}
|
||
|
||
if ( handled ) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
},
|
||
|
||
/* Filter entered characters - based on date format. */
|
||
_doKeyPress: function( event ) {
|
||
var chars, chr,
|
||
inst = $.datepicker._getInst( event.target );
|
||
|
||
if ( $.datepicker._get( inst, "constrainInput" ) ) {
|
||
chars = $.datepicker._possibleChars( $.datepicker._get( inst, "dateFormat" ) );
|
||
chr = String.fromCharCode( event.charCode == null ? event.keyCode : event.charCode );
|
||
return event.ctrlKey || event.metaKey || ( chr < " " || !chars || chars.indexOf( chr ) > -1 );
|
||
}
|
||
},
|
||
|
||
/* Synchronise manual entry and field/alternate field. */
|
||
_doKeyUp: function( event ) {
|
||
var date,
|
||
inst = $.datepicker._getInst( event.target );
|
||
|
||
if ( inst.input.val() !== inst.lastVal ) {
|
||
try {
|
||
date = $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
|
||
( inst.input ? inst.input.val() : null ),
|
||
$.datepicker._getFormatConfig( inst ) );
|
||
|
||
if ( date ) { // only if valid
|
||
$.datepicker._setDateFromField( inst );
|
||
$.datepicker._updateAlternate( inst );
|
||
$.datepicker._updateDatepicker( inst );
|
||
}
|
||
}
|
||
catch ( err ) {
|
||
}
|
||
}
|
||
return true;
|
||
},
|
||
|
||
/* Pop-up the date picker for a given input field.
|
||
* If false returned from beforeShow event handler do not show.
|
||
* @param input element - the input field attached to the date picker or
|
||
* event - if triggered by focus
|
||
*/
|
||
_showDatepicker: function( input ) {
|
||
input = input.target || input;
|
||
if ( input.nodeName.toLowerCase() !== "input" ) { // find from button/image trigger
|
||
input = $( "input", input.parentNode )[ 0 ];
|
||
}
|
||
|
||
if ( $.datepicker._isDisabledDatepicker( input ) || $.datepicker._lastInput === input ) { // already here
|
||
return;
|
||
}
|
||
|
||
var inst, beforeShow, beforeShowSettings, isFixed,
|
||
offset, showAnim, duration;
|
||
|
||
inst = $.datepicker._getInst( input );
|
||
if ( $.datepicker._curInst && $.datepicker._curInst !== inst ) {
|
||
$.datepicker._curInst.dpDiv.stop( true, true );
|
||
if ( inst && $.datepicker._datepickerShowing ) {
|
||
$.datepicker._hideDatepicker( $.datepicker._curInst.input[ 0 ] );
|
||
}
|
||
}
|
||
|
||
beforeShow = $.datepicker._get( inst, "beforeShow" );
|
||
beforeShowSettings = beforeShow ? beforeShow.apply( input, [ input, inst ] ) : {};
|
||
if ( beforeShowSettings === false ) {
|
||
return;
|
||
}
|
||
datepicker_extendRemove( inst.settings, beforeShowSettings );
|
||
|
||
inst.lastVal = null;
|
||
$.datepicker._lastInput = input;
|
||
$.datepicker._setDateFromField( inst );
|
||
|
||
if ( $.datepicker._inDialog ) { // hide cursor
|
||
input.value = "";
|
||
}
|
||
if ( !$.datepicker._pos ) { // position below input
|
||
$.datepicker._pos = $.datepicker._findPos( input );
|
||
$.datepicker._pos[ 1 ] += input.offsetHeight; // add the height
|
||
}
|
||
|
||
isFixed = false;
|
||
$( input ).parents().each( function() {
|
||
isFixed |= $( this ).css( "position" ) === "fixed";
|
||
return !isFixed;
|
||
} );
|
||
|
||
offset = { left: $.datepicker._pos[ 0 ], top: $.datepicker._pos[ 1 ] };
|
||
$.datepicker._pos = null;
|
||
|
||
//to avoid flashes on Firefox
|
||
inst.dpDiv.empty();
|
||
|
||
// determine sizing offscreen
|
||
inst.dpDiv.css( { position: "absolute", display: "block", top: "-1000px" } );
|
||
$.datepicker._updateDatepicker( inst );
|
||
|
||
// fix width for dynamic number of date pickers
|
||
// and adjust position before showing
|
||
offset = $.datepicker._checkOffset( inst, offset, isFixed );
|
||
inst.dpDiv.css( { position: ( $.datepicker._inDialog && $.blockUI ?
|
||
"static" : ( isFixed ? "fixed" : "absolute" ) ), display: "none",
|
||
left: offset.left + "px", top: offset.top + "px" } );
|
||
|
||
if ( !inst.inline ) {
|
||
showAnim = $.datepicker._get( inst, "showAnim" );
|
||
duration = $.datepicker._get( inst, "duration" );
|
||
inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
|
||
$.datepicker._datepickerShowing = true;
|
||
|
||
if ( $.effects && $.effects.effect[ showAnim ] ) {
|
||
inst.dpDiv.show( showAnim, $.datepicker._get( inst, "showOptions" ), duration );
|
||
} else {
|
||
inst.dpDiv[ showAnim || "show" ]( showAnim ? duration : null );
|
||
}
|
||
|
||
if ( $.datepicker._shouldFocusInput( inst ) ) {
|
||
inst.input.trigger( "focus" );
|
||
}
|
||
|
||
$.datepicker._curInst = inst;
|
||
}
|
||
},
|
||
|
||
/* Generate the date picker content. */
|
||
_updateDatepicker: function( inst ) {
|
||
this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
|
||
datepicker_instActive = inst; // for delegate hover events
|
||
inst.dpDiv.empty().append( this._generateHTML( inst ) );
|
||
this._attachHandlers( inst );
|
||
|
||
var origyearshtml,
|
||
numMonths = this._getNumberOfMonths( inst ),
|
||
cols = numMonths[ 1 ],
|
||
width = 17,
|
||
activeCell = inst.dpDiv.find( "." + this._dayOverClass + " a" );
|
||
|
||
if ( activeCell.length > 0 ) {
|
||
datepicker_handleMouseover.apply( activeCell.get( 0 ) );
|
||
}
|
||
|
||
inst.dpDiv.removeClass( "ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4" ).width( "" );
|
||
if ( cols > 1 ) {
|
||
inst.dpDiv.addClass( "ui-datepicker-multi-" + cols ).css( "width", ( width * cols ) + "em" );
|
||
}
|
||
inst.dpDiv[ ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ? "add" : "remove" ) +
|
||
"Class" ]( "ui-datepicker-multi" );
|
||
inst.dpDiv[ ( this._get( inst, "isRTL" ) ? "add" : "remove" ) +
|
||
"Class" ]( "ui-datepicker-rtl" );
|
||
|
||
if ( inst === $.datepicker._curInst && $.datepicker._datepickerShowing && $.datepicker._shouldFocusInput( inst ) ) {
|
||
inst.input.trigger( "focus" );
|
||
}
|
||
|
||
// Deffered render of the years select (to avoid flashes on Firefox)
|
||
if ( inst.yearshtml ) {
|
||
origyearshtml = inst.yearshtml;
|
||
setTimeout( function() {
|
||
|
||
//assure that inst.yearshtml didn't change.
|
||
if ( origyearshtml === inst.yearshtml && inst.yearshtml ) {
|
||
inst.dpDiv.find( "select.ui-datepicker-year:first" ).replaceWith( inst.yearshtml );
|
||
}
|
||
origyearshtml = inst.yearshtml = null;
|
||
}, 0 );
|
||
}
|
||
},
|
||
|
||
// #6694 - don't focus the input if it's already focused
|
||
// this breaks the change event in IE
|
||
// Support: IE and jQuery <1.9
|
||
_shouldFocusInput: function( inst ) {
|
||
return inst.input && inst.input.is( ":visible" ) && !inst.input.is( ":disabled" ) && !inst.input.is( ":focus" );
|
||
},
|
||
|
||
/* Check positioning to remain on screen. */
|
||
_checkOffset: function( inst, offset, isFixed ) {
|
||
var dpWidth = inst.dpDiv.outerWidth(),
|
||
dpHeight = inst.dpDiv.outerHeight(),
|
||
inputWidth = inst.input ? inst.input.outerWidth() : 0,
|
||
inputHeight = inst.input ? inst.input.outerHeight() : 0,
|
||
viewWidth = document.documentElement.clientWidth + ( isFixed ? 0 : $( document ).scrollLeft() ),
|
||
viewHeight = document.documentElement.clientHeight + ( isFixed ? 0 : $( document ).scrollTop() );
|
||
|
||
offset.left -= ( this._get( inst, "isRTL" ) ? ( dpWidth - inputWidth ) : 0 );
|
||
offset.left -= ( isFixed && offset.left === inst.input.offset().left ) ? $( document ).scrollLeft() : 0;
|
||
offset.top -= ( isFixed && offset.top === ( inst.input.offset().top + inputHeight ) ) ? $( document ).scrollTop() : 0;
|
||
|
||
// Now check if datepicker is showing outside window viewport - move to a better place if so.
|
||
offset.left -= Math.min( offset.left, ( offset.left + dpWidth > viewWidth && viewWidth > dpWidth ) ?
|
||
Math.abs( offset.left + dpWidth - viewWidth ) : 0 );
|
||
offset.top -= Math.min( offset.top, ( offset.top + dpHeight > viewHeight && viewHeight > dpHeight ) ?
|
||
Math.abs( dpHeight + inputHeight ) : 0 );
|
||
|
||
return offset;
|
||
},
|
||
|
||
/* Find an object's position on the screen. */
|
||
_findPos: function( obj ) {
|
||
var position,
|
||
inst = this._getInst( obj ),
|
||
isRTL = this._get( inst, "isRTL" );
|
||
|
||
while ( obj && ( obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden( obj ) ) ) {
|
||
obj = obj[ isRTL ? "previousSibling" : "nextSibling" ];
|
||
}
|
||
|
||
position = $( obj ).offset();
|
||
return [ position.left, position.top ];
|
||
},
|
||
|
||
/* Hide the date picker from view.
|
||
* @param input element - the input field attached to the date picker
|
||
*/
|
||
_hideDatepicker: function( input ) {
|
||
var showAnim, duration, postProcess, onClose,
|
||
inst = this._curInst;
|
||
|
||
if ( !inst || ( input && inst !== $.data( input, "datepicker" ) ) ) {
|
||
return;
|
||
}
|
||
|
||
if ( this._datepickerShowing ) {
|
||
showAnim = this._get( inst, "showAnim" );
|
||
duration = this._get( inst, "duration" );
|
||
postProcess = function() {
|
||
$.datepicker._tidyDialog( inst );
|
||
};
|
||
|
||
// DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
|
||
if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
|
||
inst.dpDiv.hide( showAnim, $.datepicker._get( inst, "showOptions" ), duration, postProcess );
|
||
} else {
|
||
inst.dpDiv[ ( showAnim === "slideDown" ? "slideUp" :
|
||
( showAnim === "fadeIn" ? "fadeOut" : "hide" ) ) ]( ( showAnim ? duration : null ), postProcess );
|
||
}
|
||
|
||
if ( !showAnim ) {
|
||
postProcess();
|
||
}
|
||
this._datepickerShowing = false;
|
||
|
||
onClose = this._get( inst, "onClose" );
|
||
if ( onClose ) {
|
||
onClose.apply( ( inst.input ? inst.input[ 0 ] : null ), [ ( inst.input ? inst.input.val() : "" ), inst ] );
|
||
}
|
||
|
||
this._lastInput = null;
|
||
if ( this._inDialog ) {
|
||
this._dialogInput.css( { position: "absolute", left: "0", top: "-100px" } );
|
||
if ( $.blockUI ) {
|
||
$.unblockUI();
|
||
$( "body" ).append( this.dpDiv );
|
||
}
|
||
}
|
||
this._inDialog = false;
|
||
}
|
||
},
|
||
|
||
/* Tidy up after a dialog display. */
|
||
_tidyDialog: function( inst ) {
|
||
inst.dpDiv.removeClass( this._dialogClass ).off( ".ui-datepicker-calendar" );
|
||
},
|
||
|
||
/* Close date picker if clicked elsewhere. */
|
||
_checkExternalClick: function( event ) {
|
||
if ( !$.datepicker._curInst ) {
|
||
return;
|
||
}
|
||
|
||
var $target = $( event.target ),
|
||
inst = $.datepicker._getInst( $target[ 0 ] );
|
||
|
||
if ( ( ( $target[ 0 ].id !== $.datepicker._mainDivId &&
|
||
$target.parents( "#" + $.datepicker._mainDivId ).length === 0 &&
|
||
!$target.hasClass( $.datepicker.markerClassName ) &&
|
||
!$target.closest( "." + $.datepicker._triggerClass ).length &&
|
||
$.datepicker._datepickerShowing && !( $.datepicker._inDialog && $.blockUI ) ) ) ||
|
||
( $target.hasClass( $.datepicker.markerClassName ) && $.datepicker._curInst !== inst ) ) {
|
||
$.datepicker._hideDatepicker();
|
||
}
|
||
},
|
||
|
||
/* Adjust one of the date sub-fields. */
|
||
_adjustDate: function( id, offset, period ) {
|
||
var target = $( id ),
|
||
inst = this._getInst( target[ 0 ] );
|
||
|
||
if ( this._isDisabledDatepicker( target[ 0 ] ) ) {
|
||
return;
|
||
}
|
||
this._adjustInstDate( inst, offset +
|
||
( period === "M" ? this._get( inst, "showCurrentAtPos" ) : 0 ), // undo positioning
|
||
period );
|
||
this._updateDatepicker( inst );
|
||
},
|
||
|
||
/* Action for current link. */
|
||
_gotoToday: function( id ) {
|
||
var date,
|
||
target = $( id ),
|
||
inst = this._getInst( target[ 0 ] );
|
||
|
||
if ( this._get( inst, "gotoCurrent" ) && inst.currentDay ) {
|
||
inst.selectedDay = inst.currentDay;
|
||
inst.drawMonth = inst.selectedMonth = inst.currentMonth;
|
||
inst.drawYear = inst.selectedYear = inst.currentYear;
|
||
} else {
|
||
date = new Date();
|
||
inst.selectedDay = date.getDate();
|
||
inst.drawMonth = inst.selectedMonth = date.getMonth();
|
||
inst.drawYear = inst.selectedYear = date.getFullYear();
|
||
}
|
||
this._notifyChange( inst );
|
||
this._adjustDate( target );
|
||
},
|
||
|
||
/* Action for selecting a new month/year. */
|
||
_selectMonthYear: function( id, select, period ) {
|
||
var target = $( id ),
|
||
inst = this._getInst( target[ 0 ] );
|
||
|
||
inst[ "selected" + ( period === "M" ? "Month" : "Year" ) ] =
|
||
inst[ "draw" + ( period === "M" ? "Month" : "Year" ) ] =
|
||
parseInt( select.options[ select.selectedIndex ].value, 10 );
|
||
|
||
this._notifyChange( inst );
|
||
this._adjustDate( target );
|
||
},
|
||
|
||
/* Action for selecting a day. */
|
||
_selectDay: function( id, month, year, td ) {
|
||
var inst,
|
||
target = $( id );
|
||
|
||
if ( $( td ).hasClass( this._unselectableClass ) || this._isDisabledDatepicker( target[ 0 ] ) ) {
|
||
return;
|
||
}
|
||
|
||
inst = this._getInst( target[ 0 ] );
|
||
inst.selectedDay = inst.currentDay = $( "a", td ).html();
|
||
inst.selectedMonth = inst.currentMonth = month;
|
||
inst.selectedYear = inst.currentYear = year;
|
||
this._selectDate( id, this._formatDate( inst,
|
||
inst.currentDay, inst.currentMonth, inst.currentYear ) );
|
||
},
|
||
|
||
/* Erase the input field and hide the date picker. */
|
||
_clearDate: function( id ) {
|
||
var target = $( id );
|
||
this._selectDate( target, "" );
|
||
},
|
||
|
||
/* Update the input field with the selected date. */
|
||
_selectDate: function( id, dateStr ) {
|
||
var onSelect,
|
||
target = $( id ),
|
||
inst = this._getInst( target[ 0 ] );
|
||
|
||
dateStr = ( dateStr != null ? dateStr : this._formatDate( inst ) );
|
||
if ( inst.input ) {
|
||
inst.input.val( dateStr );
|
||
}
|
||
this._updateAlternate( inst );
|
||
|
||
onSelect = this._get( inst, "onSelect" );
|
||
if ( onSelect ) {
|
||
onSelect.apply( ( inst.input ? inst.input[ 0 ] : null ), [ dateStr, inst ] ); // trigger custom callback
|
||
} else if ( inst.input ) {
|
||
inst.input.trigger( "change" ); // fire the change event
|
||
}
|
||
|
||
if ( inst.inline ) {
|
||
this._updateDatepicker( inst );
|
||
} else {
|
||
this._hideDatepicker();
|
||
this._lastInput = inst.input[ 0 ];
|
||
if ( typeof( inst.input[ 0 ] ) !== "object" ) {
|
||
inst.input.trigger( "focus" ); // restore focus
|
||
}
|
||
this._lastInput = null;
|
||
}
|
||
},
|
||
|
||
/* Update any alternate field to synchronise with the main field. */
|
||
_updateAlternate: function( inst ) {
|
||
var altFormat, date, dateStr,
|
||
altField = this._get( inst, "altField" );
|
||
|
||
if ( altField ) { // update alternate field too
|
||
altFormat = this._get( inst, "altFormat" ) || this._get( inst, "dateFormat" );
|
||
date = this._getDate( inst );
|
||
dateStr = this.formatDate( altFormat, date, this._getFormatConfig( inst ) );
|
||
$( altField ).val( dateStr );
|
||
}
|
||
},
|
||
|
||
/* Set as beforeShowDay function to prevent selection of weekends.
|
||
* @param date Date - the date to customise
|
||
* @return [boolean, string] - is this date selectable?, what is its CSS class?
|
||
*/
|
||
noWeekends: function( date ) {
|
||
var day = date.getDay();
|
||
return [ ( day > 0 && day < 6 ), "" ];
|
||
},
|
||
|
||
/* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
|
||
* @param date Date - the date to get the week for
|
||
* @return number - the number of the week within the year that contains this date
|
||
*/
|
||
iso8601Week: function( date ) {
|
||
var time,
|
||
checkDate = new Date( date.getTime() );
|
||
|
||
// Find Thursday of this week starting on Monday
|
||
checkDate.setDate( checkDate.getDate() + 4 - ( checkDate.getDay() || 7 ) );
|
||
|
||
time = checkDate.getTime();
|
||
checkDate.setMonth( 0 ); // Compare with Jan 1
|
||
checkDate.setDate( 1 );
|
||
return Math.floor( Math.round( ( time - checkDate ) / 86400000 ) / 7 ) + 1;
|
||
},
|
||
|
||
/* Parse a string value into a date object.
|
||
* See formatDate below for the possible formats.
|
||
*
|
||
* @param format string - the expected format of the date
|
||
* @param value string - the date in the above format
|
||
* @param settings Object - attributes include:
|
||
* shortYearCutoff number - the cutoff year for determining the century (optional)
|
||
* dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
|
||
* dayNames string[7] - names of the days from Sunday (optional)
|
||
* monthNamesShort string[12] - abbreviated names of the months (optional)
|
||
* monthNames string[12] - names of the months (optional)
|
||
* @return Date - the extracted date value or null if value is blank
|
||
*/
|
||
parseDate: function( format, value, settings ) {
|
||
if ( format == null || value == null ) {
|
||
throw "Invalid arguments";
|
||
}
|
||
|
||
value = ( typeof value === "object" ? value.toString() : value + "" );
|
||
if ( value === "" ) {
|
||
return null;
|
||
}
|
||
|
||
var iFormat, dim, extra,
|
||
iValue = 0,
|
||
shortYearCutoffTemp = ( settings ? settings.shortYearCutoff : null ) || this._defaults.shortYearCutoff,
|
||
shortYearCutoff = ( typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
|
||
new Date().getFullYear() % 100 + parseInt( shortYearCutoffTemp, 10 ) ),
|
||
dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
|
||
dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
|
||
monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
|
||
monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
|
||
year = -1,
|
||
month = -1,
|
||
day = -1,
|
||
doy = -1,
|
||
literal = false,
|
||
date,
|
||
|
||
// Check whether a format character is doubled
|
||
lookAhead = function( match ) {
|
||
var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
|
||
if ( matches ) {
|
||
iFormat++;
|
||
}
|
||
return matches;
|
||
},
|
||
|
||
// Extract a number from the string value
|
||
getNumber = function( match ) {
|
||
var isDoubled = lookAhead( match ),
|
||
size = ( match === "@" ? 14 : ( match === "!" ? 20 :
|
||
( match === "y" && isDoubled ? 4 : ( match === "o" ? 3 : 2 ) ) ) ),
|
||
minSize = ( match === "y" ? size : 1 ),
|
||
digits = new RegExp( "^\\d{" + minSize + "," + size + "}" ),
|
||
num = value.substring( iValue ).match( digits );
|
||
if ( !num ) {
|
||
throw "Missing number at position " + iValue;
|
||
}
|
||
iValue += num[ 0 ].length;
|
||
return parseInt( num[ 0 ], 10 );
|
||
},
|
||
|
||
// Extract a name from the string value and convert to an index
|
||
getName = function( match, shortNames, longNames ) {
|
||
var index = -1,
|
||
names = $.map( lookAhead( match ) ? longNames : shortNames, function( v, k ) {
|
||
return [ [ k, v ] ];
|
||
} ).sort( function( a, b ) {
|
||
return -( a[ 1 ].length - b[ 1 ].length );
|
||
} );
|
||
|
||
$.each( names, function( i, pair ) {
|
||
var name = pair[ 1 ];
|
||
if ( value.substr( iValue, name.length ).toLowerCase() === name.toLowerCase() ) {
|
||
index = pair[ 0 ];
|
||
iValue += name.length;
|
||
return false;
|
||
}
|
||
} );
|
||
if ( index !== -1 ) {
|
||
return index + 1;
|
||
} else {
|
||
throw "Unknown name at position " + iValue;
|
||
}
|
||
},
|
||
|
||
// Confirm that a literal character matches the string value
|
||
checkLiteral = function() {
|
||
if ( value.charAt( iValue ) !== format.charAt( iFormat ) ) {
|
||
throw "Unexpected literal at position " + iValue;
|
||
}
|
||
iValue++;
|
||
};
|
||
|
||
for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
|
||
if ( literal ) {
|
||
if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
|
||
literal = false;
|
||
} else {
|
||
checkLiteral();
|
||
}
|
||
} else {
|
||
switch ( format.charAt( iFormat ) ) {
|
||
case "d":
|
||
day = getNumber( "d" );
|
||
break;
|
||
case "D":
|
||
getName( "D", dayNamesShort, dayNames );
|
||
break;
|
||
case "o":
|
||
doy = getNumber( "o" );
|
||
break;
|
||
case "m":
|
||
month = getNumber( "m" );
|
||
break;
|
||
case "M":
|
||
month = getName( "M", monthNamesShort, monthNames );
|
||
break;
|
||
case "y":
|
||
year = getNumber( "y" );
|
||
break;
|
||
case "@":
|
||
date = new Date( getNumber( "@" ) );
|
||
year = date.getFullYear();
|
||
month = date.getMonth() + 1;
|
||
day = date.getDate();
|
||
break;
|
||
case "!":
|
||
date = new Date( ( getNumber( "!" ) - this._ticksTo1970 ) / 10000 );
|
||
year = date.getFullYear();
|
||
month = date.getMonth() + 1;
|
||
day = date.getDate();
|
||
break;
|
||
case "'":
|
||
if ( lookAhead( "'" ) ) {
|
||
checkLiteral();
|
||
} else {
|
||
literal = true;
|
||
}
|
||
break;
|
||
default:
|
||
checkLiteral();
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( iValue < value.length ) {
|
||
extra = value.substr( iValue );
|
||
if ( !/^\s+/.test( extra ) ) {
|
||
throw "Extra/unparsed characters found in date: " + extra;
|
||
}
|
||
}
|
||
|
||
if ( year === -1 ) {
|
||
year = new Date().getFullYear();
|
||
} else if ( year < 100 ) {
|
||
year += new Date().getFullYear() - new Date().getFullYear() % 100 +
|
||
( year <= shortYearCutoff ? 0 : -100 );
|
||
}
|
||
|
||
if ( doy > -1 ) {
|
||
month = 1;
|
||
day = doy;
|
||
do {
|
||
dim = this._getDaysInMonth( year, month - 1 );
|
||
if ( day <= dim ) {
|
||
break;
|
||
}
|
||
month++;
|
||
day -= dim;
|
||
} while ( true );
|
||
}
|
||
|
||
date = this._daylightSavingAdjust( new Date( year, month - 1, day ) );
|
||
if ( date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day ) {
|
||
throw "Invalid date"; // E.g. 31/02/00
|
||
}
|
||
return date;
|
||
},
|
||
|
||
/* Standard date formats. */
|
||
ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
|
||
COOKIE: "D, dd M yy",
|
||
ISO_8601: "yy-mm-dd",
|
||
RFC_822: "D, d M y",
|
||
RFC_850: "DD, dd-M-y",
|
||
RFC_1036: "D, d M y",
|
||
RFC_1123: "D, d M yy",
|
||
RFC_2822: "D, d M yy",
|
||
RSS: "D, d M y", // RFC 822
|
||
TICKS: "!",
|
||
TIMESTAMP: "@",
|
||
W3C: "yy-mm-dd", // ISO 8601
|
||
|
||
_ticksTo1970: ( ( ( 1970 - 1 ) * 365 + Math.floor( 1970 / 4 ) - Math.floor( 1970 / 100 ) +
|
||
Math.floor( 1970 / 400 ) ) * 24 * 60 * 60 * 10000000 ),
|
||
|
||
/* Format a date object into a string value.
|
||
* The format can be combinations of the following:
|
||
* d - day of month (no leading zero)
|
||
* dd - day of month (two digit)
|
||
* o - day of year (no leading zeros)
|
||
* oo - day of year (three digit)
|
||
* D - day name short
|
||
* DD - day name long
|
||
* m - month of year (no leading zero)
|
||
* mm - month of year (two digit)
|
||
* M - month name short
|
||
* MM - month name long
|
||
* y - year (two digit)
|
||
* yy - year (four digit)
|
||
* @ - Unix timestamp (ms since 01/01/1970)
|
||
* ! - Windows ticks (100ns since 01/01/0001)
|
||
* "..." - literal text
|
||
* '' - single quote
|
||
*
|
||
* @param format string - the desired format of the date
|
||
* @param date Date - the date value to format
|
||
* @param settings Object - attributes include:
|
||
* dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
|
||
* dayNames string[7] - names of the days from Sunday (optional)
|
||
* monthNamesShort string[12] - abbreviated names of the months (optional)
|
||
* monthNames string[12] - names of the months (optional)
|
||
* @return string - the date in the above format
|
||
*/
|
||
formatDate: function( format, date, settings ) {
|
||
if ( !date ) {
|
||
return "";
|
||
}
|
||
|
||
var iFormat,
|
||
dayNamesShort = ( settings ? settings.dayNamesShort : null ) || this._defaults.dayNamesShort,
|
||
dayNames = ( settings ? settings.dayNames : null ) || this._defaults.dayNames,
|
||
monthNamesShort = ( settings ? settings.monthNamesShort : null ) || this._defaults.monthNamesShort,
|
||
monthNames = ( settings ? settings.monthNames : null ) || this._defaults.monthNames,
|
||
|
||
// Check whether a format character is doubled
|
||
lookAhead = function( match ) {
|
||
var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
|
||
if ( matches ) {
|
||
iFormat++;
|
||
}
|
||
return matches;
|
||
},
|
||
|
||
// Format a number, with leading zero if necessary
|
||
formatNumber = function( match, value, len ) {
|
||
var num = "" + value;
|
||
if ( lookAhead( match ) ) {
|
||
while ( num.length < len ) {
|
||
num = "0" + num;
|
||
}
|
||
}
|
||
return num;
|
||
},
|
||
|
||
// Format a name, short or long as requested
|
||
formatName = function( match, value, shortNames, longNames ) {
|
||
return ( lookAhead( match ) ? longNames[ value ] : shortNames[ value ] );
|
||
},
|
||
output = "",
|
||
literal = false;
|
||
|
||
if ( date ) {
|
||
for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
|
||
if ( literal ) {
|
||
if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
|
||
literal = false;
|
||
} else {
|
||
output += format.charAt( iFormat );
|
||
}
|
||
} else {
|
||
switch ( format.charAt( iFormat ) ) {
|
||
case "d":
|
||
output += formatNumber( "d", date.getDate(), 2 );
|
||
break;
|
||
case "D":
|
||
output += formatName( "D", date.getDay(), dayNamesShort, dayNames );
|
||
break;
|
||
case "o":
|
||
output += formatNumber( "o",
|
||
Math.round( ( new Date( date.getFullYear(), date.getMonth(), date.getDate() ).getTime() - new Date( date.getFullYear(), 0, 0 ).getTime() ) / 86400000 ), 3 );
|
||
break;
|
||
case "m":
|
||
output += formatNumber( "m", date.getMonth() + 1, 2 );
|
||
break;
|
||
case "M":
|
||
output += formatName( "M", date.getMonth(), monthNamesShort, monthNames );
|
||
break;
|
||
case "y":
|
||
output += ( lookAhead( "y" ) ? date.getFullYear() :
|
||
( date.getFullYear() % 100 < 10 ? "0" : "" ) + date.getFullYear() % 100 );
|
||
break;
|
||
case "@":
|
||
output += date.getTime();
|
||
break;
|
||
case "!":
|
||
output += date.getTime() * 10000 + this._ticksTo1970;
|
||
break;
|
||
case "'":
|
||
if ( lookAhead( "'" ) ) {
|
||
output += "'";
|
||
} else {
|
||
literal = true;
|
||
}
|
||
break;
|
||
default:
|
||
output += format.charAt( iFormat );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return output;
|
||
},
|
||
|
||
/* Extract all possible characters from the date format. */
|
||
_possibleChars: function( format ) {
|
||
var iFormat,
|
||
chars = "",
|
||
literal = false,
|
||
|
||
// Check whether a format character is doubled
|
||
lookAhead = function( match ) {
|
||
var matches = ( iFormat + 1 < format.length && format.charAt( iFormat + 1 ) === match );
|
||
if ( matches ) {
|
||
iFormat++;
|
||
}
|
||
return matches;
|
||
};
|
||
|
||
for ( iFormat = 0; iFormat < format.length; iFormat++ ) {
|
||
if ( literal ) {
|
||
if ( format.charAt( iFormat ) === "'" && !lookAhead( "'" ) ) {
|
||
literal = false;
|
||
} else {
|
||
chars += format.charAt( iFormat );
|
||
}
|
||
} else {
|
||
switch ( format.charAt( iFormat ) ) {
|
||
case "d": case "m": case "y": case "@":
|
||
chars += "0123456789";
|
||
break;
|
||
case "D": case "M":
|
||
return null; // Accept anything
|
||
case "'":
|
||
if ( lookAhead( "'" ) ) {
|
||
chars += "'";
|
||
} else {
|
||
literal = true;
|
||
}
|
||
break;
|
||
default:
|
||
chars += format.charAt( iFormat );
|
||
}
|
||
}
|
||
}
|
||
return chars;
|
||
},
|
||
|
||
/* Get a setting value, defaulting if necessary. */
|
||
_get: function( inst, name ) {
|
||
return inst.settings[ name ] !== undefined ?
|
||
inst.settings[ name ] : this._defaults[ name ];
|
||
},
|
||
|
||
/* Parse existing date and initialise date picker. */
|
||
_setDateFromField: function( inst, noDefault ) {
|
||
if ( inst.input.val() === inst.lastVal ) {
|
||
return;
|
||
}
|
||
|
||
var dateFormat = this._get( inst, "dateFormat" ),
|
||
dates = inst.lastVal = inst.input ? inst.input.val() : null,
|
||
defaultDate = this._getDefaultDate( inst ),
|
||
date = defaultDate,
|
||
settings = this._getFormatConfig( inst );
|
||
|
||
try {
|
||
date = this.parseDate( dateFormat, dates, settings ) || defaultDate;
|
||
} catch ( event ) {
|
||
dates = ( noDefault ? "" : dates );
|
||
}
|
||
inst.selectedDay = date.getDate();
|
||
inst.drawMonth = inst.selectedMonth = date.getMonth();
|
||
inst.drawYear = inst.selectedYear = date.getFullYear();
|
||
inst.currentDay = ( dates ? date.getDate() : 0 );
|
||
inst.currentMonth = ( dates ? date.getMonth() : 0 );
|
||
inst.currentYear = ( dates ? date.getFullYear() : 0 );
|
||
this._adjustInstDate( inst );
|
||
},
|
||
|
||
/* Retrieve the default date shown on opening. */
|
||
_getDefaultDate: function( inst ) {
|
||
return this._restrictMinMax( inst,
|
||
this._determineDate( inst, this._get( inst, "defaultDate" ), new Date() ) );
|
||
},
|
||
|
||
/* A date may be specified as an exact value or a relative one. */
|
||
_determineDate: function( inst, date, defaultDate ) {
|
||
var offsetNumeric = function( offset ) {
|
||
var date = new Date();
|
||
date.setDate( date.getDate() + offset );
|
||
return date;
|
||
},
|
||
offsetString = function( offset ) {
|
||
try {
|
||
return $.datepicker.parseDate( $.datepicker._get( inst, "dateFormat" ),
|
||
offset, $.datepicker._getFormatConfig( inst ) );
|
||
}
|
||
catch ( e ) {
|
||
|
||
// Ignore
|
||
}
|
||
|
||
var date = ( offset.toLowerCase().match( /^c/ ) ?
|
||
$.datepicker._getDate( inst ) : null ) || new Date(),
|
||
year = date.getFullYear(),
|
||
month = date.getMonth(),
|
||
day = date.getDate(),
|
||
pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
|
||
matches = pattern.exec( offset );
|
||
|
||
while ( matches ) {
|
||
switch ( matches[ 2 ] || "d" ) {
|
||
case "d" : case "D" :
|
||
day += parseInt( matches[ 1 ], 10 ); break;
|
||
case "w" : case "W" :
|
||
day += parseInt( matches[ 1 ], 10 ) * 7; break;
|
||
case "m" : case "M" :
|
||
month += parseInt( matches[ 1 ], 10 );
|
||
day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
|
||
break;
|
||
case "y": case "Y" :
|
||
year += parseInt( matches[ 1 ], 10 );
|
||
day = Math.min( day, $.datepicker._getDaysInMonth( year, month ) );
|
||
break;
|
||
}
|
||
matches = pattern.exec( offset );
|
||
}
|
||
return new Date( year, month, day );
|
||
},
|
||
newDate = ( date == null || date === "" ? defaultDate : ( typeof date === "string" ? offsetString( date ) :
|
||
( typeof date === "number" ? ( isNaN( date ) ? defaultDate : offsetNumeric( date ) ) : new Date( date.getTime() ) ) ) );
|
||
|
||
newDate = ( newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate );
|
||
if ( newDate ) {
|
||
newDate.setHours( 0 );
|
||
newDate.setMinutes( 0 );
|
||
newDate.setSeconds( 0 );
|
||
newDate.setMilliseconds( 0 );
|
||
}
|
||
return this._daylightSavingAdjust( newDate );
|
||
},
|
||
|
||
/* Handle switch to/from daylight saving.
|
||
* Hours may be non-zero on daylight saving cut-over:
|
||
* > 12 when midnight changeover, but then cannot generate
|
||
* midnight datetime, so jump to 1AM, otherwise reset.
|
||
* @param date (Date) the date to check
|
||
* @return (Date) the corrected date
|
||
*/
|
||
_daylightSavingAdjust: function( date ) {
|
||
if ( !date ) {
|
||
return null;
|
||
}
|
||
date.setHours( date.getHours() > 12 ? date.getHours() + 2 : 0 );
|
||
return date;
|
||
},
|
||
|
||
/* Set the date(s) directly. */
|
||
_setDate: function( inst, date, noChange ) {
|
||
var clear = !date,
|
||
origMonth = inst.selectedMonth,
|
||
origYear = inst.selectedYear,
|
||
newDate = this._restrictMinMax( inst, this._determineDate( inst, date, new Date() ) );
|
||
|
||
inst.selectedDay = inst.currentDay = newDate.getDate();
|
||
inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
|
||
inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
|
||
if ( ( origMonth !== inst.selectedMonth || origYear !== inst.selectedYear ) && !noChange ) {
|
||
this._notifyChange( inst );
|
||
}
|
||
this._adjustInstDate( inst );
|
||
if ( inst.input ) {
|
||
inst.input.val( clear ? "" : this._formatDate( inst ) );
|
||
}
|
||
},
|
||
|
||
/* Retrieve the date(s) directly. */
|
||
_getDate: function( inst ) {
|
||
var startDate = ( !inst.currentYear || ( inst.input && inst.input.val() === "" ) ? null :
|
||
this._daylightSavingAdjust( new Date(
|
||
inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
|
||
return startDate;
|
||
},
|
||
|
||
/* Attach the onxxx handlers. These are declared statically so
|
||
* they work with static code transformers like Caja.
|
||
*/
|
||
_attachHandlers: function( inst ) {
|
||
var stepMonths = this._get( inst, "stepMonths" ),
|
||
id = "#" + inst.id.replace( /\\\\/g, "\\" );
|
||
inst.dpDiv.find( "[data-handler]" ).map( function() {
|
||
var handler = {
|
||
prev: function() {
|
||
$.datepicker._adjustDate( id, -stepMonths, "M" );
|
||
},
|
||
next: function() {
|
||
$.datepicker._adjustDate( id, +stepMonths, "M" );
|
||
},
|
||
hide: function() {
|
||
$.datepicker._hideDatepicker();
|
||
},
|
||
today: function() {
|
||
$.datepicker._gotoToday( id );
|
||
},
|
||
selectDay: function() {
|
||
$.datepicker._selectDay( id, +this.getAttribute( "data-month" ), +this.getAttribute( "data-year" ), this );
|
||
return false;
|
||
},
|
||
selectMonth: function() {
|
||
$.datepicker._selectMonthYear( id, this, "M" );
|
||
return false;
|
||
},
|
||
selectYear: function() {
|
||
$.datepicker._selectMonthYear( id, this, "Y" );
|
||
return false;
|
||
}
|
||
};
|
||
$( this ).on( this.getAttribute( "data-event" ), handler[ this.getAttribute( "data-handler" ) ] );
|
||
} );
|
||
},
|
||
|
||
/* Generate the HTML for the current state of the date picker. */
|
||
_generateHTML: function( inst ) {
|
||
var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
|
||
controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
|
||
monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
|
||
selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
|
||
cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
|
||
printDate, dRow, tbody, daySettings, otherMonth, unselectable,
|
||
tempDate = new Date(),
|
||
today = this._daylightSavingAdjust(
|
||
new Date( tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate() ) ), // clear time
|
||
isRTL = this._get( inst, "isRTL" ),
|
||
showButtonPanel = this._get( inst, "showButtonPanel" ),
|
||
hideIfNoPrevNext = this._get( inst, "hideIfNoPrevNext" ),
|
||
navigationAsDateFormat = this._get( inst, "navigationAsDateFormat" ),
|
||
numMonths = this._getNumberOfMonths( inst ),
|
||
showCurrentAtPos = this._get( inst, "showCurrentAtPos" ),
|
||
stepMonths = this._get( inst, "stepMonths" ),
|
||
isMultiMonth = ( numMonths[ 0 ] !== 1 || numMonths[ 1 ] !== 1 ),
|
||
currentDate = this._daylightSavingAdjust( ( !inst.currentDay ? new Date( 9999, 9, 9 ) :
|
||
new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) ),
|
||
minDate = this._getMinMaxDate( inst, "min" ),
|
||
maxDate = this._getMinMaxDate( inst, "max" ),
|
||
drawMonth = inst.drawMonth - showCurrentAtPos,
|
||
drawYear = inst.drawYear;
|
||
|
||
if ( drawMonth < 0 ) {
|
||
drawMonth += 12;
|
||
drawYear--;
|
||
}
|
||
if ( maxDate ) {
|
||
maxDraw = this._daylightSavingAdjust( new Date( maxDate.getFullYear(),
|
||
maxDate.getMonth() - ( numMonths[ 0 ] * numMonths[ 1 ] ) + 1, maxDate.getDate() ) );
|
||
maxDraw = ( minDate && maxDraw < minDate ? minDate : maxDraw );
|
||
while ( this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 ) ) > maxDraw ) {
|
||
drawMonth--;
|
||
if ( drawMonth < 0 ) {
|
||
drawMonth = 11;
|
||
drawYear--;
|
||
}
|
||
}
|
||
}
|
||
inst.drawMonth = drawMonth;
|
||
inst.drawYear = drawYear;
|
||
|
||
prevText = this._get( inst, "prevText" );
|
||
prevText = ( !navigationAsDateFormat ? prevText : this.formatDate( prevText,
|
||
this._daylightSavingAdjust( new Date( drawYear, drawMonth - stepMonths, 1 ) ),
|
||
this._getFormatConfig( inst ) ) );
|
||
|
||
prev = ( this._canAdjustMonth( inst, -1, drawYear, drawMonth ) ?
|
||
"<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
|
||
" title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" :
|
||
( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w" ) + "'>" + prevText + "</span></a>" ) );
|
||
|
||
nextText = this._get( inst, "nextText" );
|
||
nextText = ( !navigationAsDateFormat ? nextText : this.formatDate( nextText,
|
||
this._daylightSavingAdjust( new Date( drawYear, drawMonth + stepMonths, 1 ) ),
|
||
this._getFormatConfig( inst ) ) );
|
||
|
||
next = ( this._canAdjustMonth( inst, +1, drawYear, drawMonth ) ?
|
||
"<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
|
||
" title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" :
|
||
( hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e" ) + "'>" + nextText + "</span></a>" ) );
|
||
|
||
currentText = this._get( inst, "currentText" );
|
||
gotoDate = ( this._get( inst, "gotoCurrent" ) && inst.currentDay ? currentDate : today );
|
||
currentText = ( !navigationAsDateFormat ? currentText :
|
||
this.formatDate( currentText, gotoDate, this._getFormatConfig( inst ) ) );
|
||
|
||
controls = ( !inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
|
||
this._get( inst, "closeText" ) + "</button>" : "" );
|
||
|
||
buttonPanel = ( showButtonPanel ) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + ( isRTL ? controls : "" ) +
|
||
( this._isInRange( inst, gotoDate ) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
|
||
">" + currentText + "</button>" : "" ) + ( isRTL ? "" : controls ) + "</div>" : "";
|
||
|
||
firstDay = parseInt( this._get( inst, "firstDay" ), 10 );
|
||
firstDay = ( isNaN( firstDay ) ? 0 : firstDay );
|
||
|
||
showWeek = this._get( inst, "showWeek" );
|
||
dayNames = this._get( inst, "dayNames" );
|
||
dayNamesMin = this._get( inst, "dayNamesMin" );
|
||
monthNames = this._get( inst, "monthNames" );
|
||
monthNamesShort = this._get( inst, "monthNamesShort" );
|
||
beforeShowDay = this._get( inst, "beforeShowDay" );
|
||
showOtherMonths = this._get( inst, "showOtherMonths" );
|
||
selectOtherMonths = this._get( inst, "selectOtherMonths" );
|
||
defaultDate = this._getDefaultDate( inst );
|
||
html = "";
|
||
|
||
for ( row = 0; row < numMonths[ 0 ]; row++ ) {
|
||
group = "";
|
||
this.maxRows = 4;
|
||
for ( col = 0; col < numMonths[ 1 ]; col++ ) {
|
||
selectedDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, inst.selectedDay ) );
|
||
cornerClass = " ui-corner-all";
|
||
calender = "";
|
||
if ( isMultiMonth ) {
|
||
calender += "<div class='ui-datepicker-group";
|
||
if ( numMonths[ 1 ] > 1 ) {
|
||
switch ( col ) {
|
||
case 0: calender += " ui-datepicker-group-first";
|
||
cornerClass = " ui-corner-" + ( isRTL ? "right" : "left" ); break;
|
||
case numMonths[ 1 ] - 1: calender += " ui-datepicker-group-last";
|
||
cornerClass = " ui-corner-" + ( isRTL ? "left" : "right" ); break;
|
||
default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
|
||
}
|
||
}
|
||
calender += "'>";
|
||
}
|
||
calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
|
||
( /all|left/.test( cornerClass ) && row === 0 ? ( isRTL ? next : prev ) : "" ) +
|
||
( /all|right/.test( cornerClass ) && row === 0 ? ( isRTL ? prev : next ) : "" ) +
|
||
this._generateMonthYearHeader( inst, drawMonth, drawYear, minDate, maxDate,
|
||
row > 0 || col > 0, monthNames, monthNamesShort ) + // draw month headers
|
||
"</div><table class='ui-datepicker-calendar'><thead>" +
|
||
"<tr>";
|
||
thead = ( showWeek ? "<th class='ui-datepicker-week-col'>" + this._get( inst, "weekHeader" ) + "</th>" : "" );
|
||
for ( dow = 0; dow < 7; dow++ ) { // days of the week
|
||
day = ( dow + firstDay ) % 7;
|
||
thead += "<th scope='col'" + ( ( dow + firstDay + 6 ) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "" ) + ">" +
|
||
"<span title='" + dayNames[ day ] + "'>" + dayNamesMin[ day ] + "</span></th>";
|
||
}
|
||
calender += thead + "</tr></thead><tbody>";
|
||
daysInMonth = this._getDaysInMonth( drawYear, drawMonth );
|
||
if ( drawYear === inst.selectedYear && drawMonth === inst.selectedMonth ) {
|
||
inst.selectedDay = Math.min( inst.selectedDay, daysInMonth );
|
||
}
|
||
leadDays = ( this._getFirstDayOfMonth( drawYear, drawMonth ) - firstDay + 7 ) % 7;
|
||
curRows = Math.ceil( ( leadDays + daysInMonth ) / 7 ); // calculate the number of rows to generate
|
||
numRows = ( isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows ); //If multiple months, use the higher number of rows (see #7043)
|
||
this.maxRows = numRows;
|
||
printDate = this._daylightSavingAdjust( new Date( drawYear, drawMonth, 1 - leadDays ) );
|
||
for ( dRow = 0; dRow < numRows; dRow++ ) { // create date picker rows
|
||
calender += "<tr>";
|
||
tbody = ( !showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
|
||
this._get( inst, "calculateWeek" )( printDate ) + "</td>" );
|
||
for ( dow = 0; dow < 7; dow++ ) { // create date picker days
|
||
daySettings = ( beforeShowDay ?
|
||
beforeShowDay.apply( ( inst.input ? inst.input[ 0 ] : null ), [ printDate ] ) : [ true, "" ] );
|
||
otherMonth = ( printDate.getMonth() !== drawMonth );
|
||
unselectable = ( otherMonth && !selectOtherMonths ) || !daySettings[ 0 ] ||
|
||
( minDate && printDate < minDate ) || ( maxDate && printDate > maxDate );
|
||
tbody += "<td class='" +
|
||
( ( dow + firstDay + 6 ) % 7 >= 5 ? " ui-datepicker-week-end" : "" ) + // highlight weekends
|
||
( otherMonth ? " ui-datepicker-other-month" : "" ) + // highlight days from other months
|
||
( ( printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent ) || // user pressed key
|
||
( defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime() ) ?
|
||
|
||
// or defaultDate is current printedDate and defaultDate is selectedDate
|
||
" " + this._dayOverClass : "" ) + // highlight selected day
|
||
( unselectable ? " " + this._unselectableClass + " ui-state-disabled" : "" ) + // highlight unselectable days
|
||
( otherMonth && !showOtherMonths ? "" : " " + daySettings[ 1 ] + // highlight custom dates
|
||
( printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "" ) + // highlight selected day
|
||
( printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "" ) ) + "'" + // highlight today (if different)
|
||
( ( !otherMonth || showOtherMonths ) && daySettings[ 2 ] ? " title='" + daySettings[ 2 ].replace( /'/g, "'" ) + "'" : "" ) + // cell title
|
||
( unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'" ) + ">" + // actions
|
||
( otherMonth && !showOtherMonths ? " " : // display for other months
|
||
( unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
|
||
( printDate.getTime() === today.getTime() ? " ui-state-highlight" : "" ) +
|
||
( printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "" ) + // highlight selected day
|
||
( otherMonth ? " ui-priority-secondary" : "" ) + // distinguish dates from other months
|
||
"' href='#'>" + printDate.getDate() + "</a>" ) ) + "</td>"; // display selectable date
|
||
printDate.setDate( printDate.getDate() + 1 );
|
||
printDate = this._daylightSavingAdjust( printDate );
|
||
}
|
||
calender += tbody + "</tr>";
|
||
}
|
||
drawMonth++;
|
||
if ( drawMonth > 11 ) {
|
||
drawMonth = 0;
|
||
drawYear++;
|
||
}
|
||
calender += "</tbody></table>" + ( isMultiMonth ? "</div>" +
|
||
( ( numMonths[ 0 ] > 0 && col === numMonths[ 1 ] - 1 ) ? "<div class='ui-datepicker-row-break'></div>" : "" ) : "" );
|
||
group += calender;
|
||
}
|
||
html += group;
|
||
}
|
||
html += buttonPanel;
|
||
inst._keyEvent = false;
|
||
return html;
|
||
},
|
||
|
||
/* Generate the month and year header. */
|
||
_generateMonthYearHeader: function( inst, drawMonth, drawYear, minDate, maxDate,
|
||
secondary, monthNames, monthNamesShort ) {
|
||
|
||
var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
|
||
changeMonth = this._get( inst, "changeMonth" ),
|
||
changeYear = this._get( inst, "changeYear" ),
|
||
showMonthAfterYear = this._get( inst, "showMonthAfterYear" ),
|
||
html = "<div class='ui-datepicker-title'>",
|
||
monthHtml = "";
|
||
|
||
// Month selection
|
||
if ( secondary || !changeMonth ) {
|
||
monthHtml += "<span class='ui-datepicker-month'>" + monthNames[ drawMonth ] + "</span>";
|
||
} else {
|
||
inMinYear = ( minDate && minDate.getFullYear() === drawYear );
|
||
inMaxYear = ( maxDate && maxDate.getFullYear() === drawYear );
|
||
monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
|
||
for ( month = 0; month < 12; month++ ) {
|
||
if ( ( !inMinYear || month >= minDate.getMonth() ) && ( !inMaxYear || month <= maxDate.getMonth() ) ) {
|
||
monthHtml += "<option value='" + month + "'" +
|
||
( month === drawMonth ? " selected='selected'" : "" ) +
|
||
">" + monthNamesShort[ month ] + "</option>";
|
||
}
|
||
}
|
||
monthHtml += "</select>";
|
||
}
|
||
|
||
if ( !showMonthAfterYear ) {
|
||
html += monthHtml + ( secondary || !( changeMonth && changeYear ) ? " " : "" );
|
||
}
|
||
|
||
// Year selection
|
||
if ( !inst.yearshtml ) {
|
||
inst.yearshtml = "";
|
||
if ( secondary || !changeYear ) {
|
||
html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
|
||
} else {
|
||
|
||
// determine range of years to display
|
||
years = this._get( inst, "yearRange" ).split( ":" );
|
||
thisYear = new Date().getFullYear();
|
||
determineYear = function( value ) {
|
||
var year = ( value.match( /c[+\-].*/ ) ? drawYear + parseInt( value.substring( 1 ), 10 ) :
|
||
( value.match( /[+\-].*/ ) ? thisYear + parseInt( value, 10 ) :
|
||
parseInt( value, 10 ) ) );
|
||
return ( isNaN( year ) ? thisYear : year );
|
||
};
|
||
year = determineYear( years[ 0 ] );
|
||
endYear = Math.max( year, determineYear( years[ 1 ] || "" ) );
|
||
year = ( minDate ? Math.max( year, minDate.getFullYear() ) : year );
|
||
endYear = ( maxDate ? Math.min( endYear, maxDate.getFullYear() ) : endYear );
|
||
inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
|
||
for ( ; year <= endYear; year++ ) {
|
||
inst.yearshtml += "<option value='" + year + "'" +
|
||
( year === drawYear ? " selected='selected'" : "" ) +
|
||
">" + year + "</option>";
|
||
}
|
||
inst.yearshtml += "</select>";
|
||
|
||
html += inst.yearshtml;
|
||
inst.yearshtml = null;
|
||
}
|
||
}
|
||
|
||
html += this._get( inst, "yearSuffix" );
|
||
if ( showMonthAfterYear ) {
|
||
html += ( secondary || !( changeMonth && changeYear ) ? " " : "" ) + monthHtml;
|
||
}
|
||
html += "</div>"; // Close datepicker_header
|
||
return html;
|
||
},
|
||
|
||
/* Adjust one of the date sub-fields. */
|
||
_adjustInstDate: function( inst, offset, period ) {
|
||
var year = inst.selectedYear + ( period === "Y" ? offset : 0 ),
|
||
month = inst.selectedMonth + ( period === "M" ? offset : 0 ),
|
||
day = Math.min( inst.selectedDay, this._getDaysInMonth( year, month ) ) + ( period === "D" ? offset : 0 ),
|
||
date = this._restrictMinMax( inst, this._daylightSavingAdjust( new Date( year, month, day ) ) );
|
||
|
||
inst.selectedDay = date.getDate();
|
||
inst.drawMonth = inst.selectedMonth = date.getMonth();
|
||
inst.drawYear = inst.selectedYear = date.getFullYear();
|
||
if ( period === "M" || period === "Y" ) {
|
||
this._notifyChange( inst );
|
||
}
|
||
},
|
||
|
||
/* Ensure a date is within any min/max bounds. */
|
||
_restrictMinMax: function( inst, date ) {
|
||
var minDate = this._getMinMaxDate( inst, "min" ),
|
||
maxDate = this._getMinMaxDate( inst, "max" ),
|
||
newDate = ( minDate && date < minDate ? minDate : date );
|
||
return ( maxDate && newDate > maxDate ? maxDate : newDate );
|
||
},
|
||
|
||
/* Notify change of month/year. */
|
||
_notifyChange: function( inst ) {
|
||
var onChange = this._get( inst, "onChangeMonthYear" );
|
||
if ( onChange ) {
|
||
onChange.apply( ( inst.input ? inst.input[ 0 ] : null ),
|
||
[ inst.selectedYear, inst.selectedMonth + 1, inst ] );
|
||
}
|
||
},
|
||
|
||
/* Determine the number of months to show. */
|
||
_getNumberOfMonths: function( inst ) {
|
||
var numMonths = this._get( inst, "numberOfMonths" );
|
||
return ( numMonths == null ? [ 1, 1 ] : ( typeof numMonths === "number" ? [ 1, numMonths ] : numMonths ) );
|
||
},
|
||
|
||
/* Determine the current maximum date - ensure no time components are set. */
|
||
_getMinMaxDate: function( inst, minMax ) {
|
||
return this._determineDate( inst, this._get( inst, minMax + "Date" ), null );
|
||
},
|
||
|
||
/* Find the number of days in a given month. */
|
||
_getDaysInMonth: function( year, month ) {
|
||
return 32 - this._daylightSavingAdjust( new Date( year, month, 32 ) ).getDate();
|
||
},
|
||
|
||
/* Find the day of the week of the first of a month. */
|
||
_getFirstDayOfMonth: function( year, month ) {
|
||
return new Date( year, month, 1 ).getDay();
|
||
},
|
||
|
||
/* Determines if we should allow a "next/prev" month display change. */
|
||
_canAdjustMonth: function( inst, offset, curYear, curMonth ) {
|
||
var numMonths = this._getNumberOfMonths( inst ),
|
||
date = this._daylightSavingAdjust( new Date( curYear,
|
||
curMonth + ( offset < 0 ? offset : numMonths[ 0 ] * numMonths[ 1 ] ), 1 ) );
|
||
|
||
if ( offset < 0 ) {
|
||
date.setDate( this._getDaysInMonth( date.getFullYear(), date.getMonth() ) );
|
||
}
|
||
return this._isInRange( inst, date );
|
||
},
|
||
|
||
/* Is the given date in the accepted range? */
|
||
_isInRange: function( inst, date ) {
|
||
var yearSplit, currentYear,
|
||
minDate = this._getMinMaxDate( inst, "min" ),
|
||
maxDate = this._getMinMaxDate( inst, "max" ),
|
||
minYear = null,
|
||
maxYear = null,
|
||
years = this._get( inst, "yearRange" );
|
||
if ( years ) {
|
||
yearSplit = years.split( ":" );
|
||
currentYear = new Date().getFullYear();
|
||
minYear = parseInt( yearSplit[ 0 ], 10 );
|
||
maxYear = parseInt( yearSplit[ 1 ], 10 );
|
||
if ( yearSplit[ 0 ].match( /[+\-].*/ ) ) {
|
||
minYear += currentYear;
|
||
}
|
||
if ( yearSplit[ 1 ].match( /[+\-].*/ ) ) {
|
||
maxYear += currentYear;
|
||
}
|
||
}
|
||
|
||
return ( ( !minDate || date.getTime() >= minDate.getTime() ) &&
|
||
( !maxDate || date.getTime() <= maxDate.getTime() ) &&
|
||
( !minYear || date.getFullYear() >= minYear ) &&
|
||
( !maxYear || date.getFullYear() <= maxYear ) );
|
||
},
|
||
|
||
/* Provide the configuration settings for formatting/parsing. */
|
||
_getFormatConfig: function( inst ) {
|
||
var shortYearCutoff = this._get( inst, "shortYearCutoff" );
|
||
shortYearCutoff = ( typeof shortYearCutoff !== "string" ? shortYearCutoff :
|
||
new Date().getFullYear() % 100 + parseInt( shortYearCutoff, 10 ) );
|
||
return { shortYearCutoff: shortYearCutoff,
|
||
dayNamesShort: this._get( inst, "dayNamesShort" ), dayNames: this._get( inst, "dayNames" ),
|
||
monthNamesShort: this._get( inst, "monthNamesShort" ), monthNames: this._get( inst, "monthNames" ) };
|
||
},
|
||
|
||
/* Format the given date for display. */
|
||
_formatDate: function( inst, day, month, year ) {
|
||
if ( !day ) {
|
||
inst.currentDay = inst.selectedDay;
|
||
inst.currentMonth = inst.selectedMonth;
|
||
inst.currentYear = inst.selectedYear;
|
||
}
|
||
var date = ( day ? ( typeof day === "object" ? day :
|
||
this._daylightSavingAdjust( new Date( year, month, day ) ) ) :
|
||
this._daylightSavingAdjust( new Date( inst.currentYear, inst.currentMonth, inst.currentDay ) ) );
|
||
return this.formatDate( this._get( inst, "dateFormat" ), date, this._getFormatConfig( inst ) );
|
||
}
|
||
} );
|
||
|
||
/*
|
||
* Bind hover events for datepicker elements.
|
||
* Done via delegate so the binding only occurs once in the lifetime of the parent div.
|
||
* Global datepicker_instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
|
||
*/
|
||
function datepicker_bindHover( dpDiv ) {
|
||
var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
|
||
return dpDiv.on( "mouseout", selector, function() {
|
||
$( this ).removeClass( "ui-state-hover" );
|
||
if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
|
||
$( this ).removeClass( "ui-datepicker-prev-hover" );
|
||
}
|
||
if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
|
||
$( this ).removeClass( "ui-datepicker-next-hover" );
|
||
}
|
||
} )
|
||
.on( "mouseover", selector, datepicker_handleMouseover );
|
||
}
|
||
|
||
function datepicker_handleMouseover() {
|
||
if ( !$.datepicker._isDisabledDatepicker( datepicker_instActive.inline ? datepicker_instActive.dpDiv.parent()[ 0 ] : datepicker_instActive.input[ 0 ] ) ) {
|
||
$( this ).parents( ".ui-datepicker-calendar" ).find( "a" ).removeClass( "ui-state-hover" );
|
||
$( this ).addClass( "ui-state-hover" );
|
||
if ( this.className.indexOf( "ui-datepicker-prev" ) !== -1 ) {
|
||
$( this ).addClass( "ui-datepicker-prev-hover" );
|
||
}
|
||
if ( this.className.indexOf( "ui-datepicker-next" ) !== -1 ) {
|
||
$( this ).addClass( "ui-datepicker-next-hover" );
|
||
}
|
||
}
|
||
}
|
||
|
||
/* jQuery extend now ignores nulls! */
|
||
function datepicker_extendRemove( target, props ) {
|
||
$.extend( target, props );
|
||
for ( var name in props ) {
|
||
if ( props[ name ] == null ) {
|
||
target[ name ] = props[ name ];
|
||
}
|
||
}
|
||
return target;
|
||
}
|
||
|
||
/* Invoke the datepicker functionality.
|
||
@param options string - a command, optionally followed by additional parameters or
|
||
Object - settings for attaching new datepicker functionality
|
||
@return jQuery object */
|
||
$.fn.datepicker = function( options ) {
|
||
|
||
/* Verify an empty collection wasn't passed - Fixes #6976 */
|
||
if ( !this.length ) {
|
||
return this;
|
||
}
|
||
|
||
/* Initialise the date picker. */
|
||
if ( !$.datepicker.initialized ) {
|
||
$( document ).on( "mousedown", $.datepicker._checkExternalClick );
|
||
$.datepicker.initialized = true;
|
||
}
|
||
|
||
/* Append datepicker main container to body if not exist. */
|
||
if ( $( "#" + $.datepicker._mainDivId ).length === 0 ) {
|
||
$( "body" ).append( $.datepicker.dpDiv );
|
||
}
|
||
|
||
var otherArgs = Array.prototype.slice.call( arguments, 1 );
|
||
if ( typeof options === "string" && ( options === "isDisabled" || options === "getDate" || options === "widget" ) ) {
|
||
return $.datepicker[ "_" + options + "Datepicker" ].
|
||
apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
|
||
}
|
||
if ( options === "option" && arguments.length === 2 && typeof arguments[ 1 ] === "string" ) {
|
||
return $.datepicker[ "_" + options + "Datepicker" ].
|
||
apply( $.datepicker, [ this[ 0 ] ].concat( otherArgs ) );
|
||
}
|
||
return this.each( function() {
|
||
typeof options === "string" ?
|
||
$.datepicker[ "_" + options + "Datepicker" ].
|
||
apply( $.datepicker, [ this ].concat( otherArgs ) ) :
|
||
$.datepicker._attachDatepicker( this, options );
|
||
} );
|
||
};
|
||
|
||
$.datepicker = new Datepicker(); // singleton instance
|
||
$.datepicker.initialized = false;
|
||
$.datepicker.uuid = new Date().getTime();
|
||
$.datepicker.version = "1.12.1";
|
||
|
||
var widgetsDatepicker = $.datepicker;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Dialog 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Dialog
|
||
//>>group: Widgets
|
||
//>>description: Displays customizable dialog windows.
|
||
//>>docs: http://api.jqueryui.com/dialog/
|
||
//>>demos: http://jqueryui.com/dialog/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/dialog.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.dialog", {
|
||
version: "1.12.1",
|
||
options: {
|
||
appendTo: "body",
|
||
autoOpen: true,
|
||
buttons: [],
|
||
classes: {
|
||
"ui-dialog": "ui-corner-all",
|
||
"ui-dialog-titlebar": "ui-corner-all"
|
||
},
|
||
closeOnEscape: true,
|
||
closeText: "Close",
|
||
draggable: true,
|
||
hide: null,
|
||
height: "auto",
|
||
maxHeight: null,
|
||
maxWidth: null,
|
||
minHeight: 150,
|
||
minWidth: 150,
|
||
modal: false,
|
||
position: {
|
||
my: "center",
|
||
at: "center",
|
||
of: window,
|
||
collision: "fit",
|
||
|
||
// Ensure the titlebar is always visible
|
||
using: function( pos ) {
|
||
var topOffset = $( this ).css( pos ).offset().top;
|
||
if ( topOffset < 0 ) {
|
||
$( this ).css( "top", pos.top - topOffset );
|
||
}
|
||
}
|
||
},
|
||
resizable: true,
|
||
show: null,
|
||
title: null,
|
||
width: 300,
|
||
|
||
// Callbacks
|
||
beforeClose: null,
|
||
close: null,
|
||
drag: null,
|
||
dragStart: null,
|
||
dragStop: null,
|
||
focus: null,
|
||
open: null,
|
||
resize: null,
|
||
resizeStart: null,
|
||
resizeStop: null
|
||
},
|
||
|
||
sizeRelatedOptions: {
|
||
buttons: true,
|
||
height: true,
|
||
maxHeight: true,
|
||
maxWidth: true,
|
||
minHeight: true,
|
||
minWidth: true,
|
||
width: true
|
||
},
|
||
|
||
resizableRelatedOptions: {
|
||
maxHeight: true,
|
||
maxWidth: true,
|
||
minHeight: true,
|
||
minWidth: true
|
||
},
|
||
|
||
_create: function() {
|
||
this.originalCss = {
|
||
display: this.element[ 0 ].style.display,
|
||
width: this.element[ 0 ].style.width,
|
||
minHeight: this.element[ 0 ].style.minHeight,
|
||
maxHeight: this.element[ 0 ].style.maxHeight,
|
||
height: this.element[ 0 ].style.height
|
||
};
|
||
this.originalPosition = {
|
||
parent: this.element.parent(),
|
||
index: this.element.parent().children().index( this.element )
|
||
};
|
||
this.originalTitle = this.element.attr( "title" );
|
||
if ( this.options.title == null && this.originalTitle != null ) {
|
||
this.options.title = this.originalTitle;
|
||
}
|
||
|
||
// Dialogs can't be disabled
|
||
if ( this.options.disabled ) {
|
||
this.options.disabled = false;
|
||
}
|
||
|
||
this._createWrapper();
|
||
|
||
this.element
|
||
.show()
|
||
.removeAttr( "title" )
|
||
.appendTo( this.uiDialog );
|
||
|
||
this._addClass( "ui-dialog-content", "ui-widget-content" );
|
||
|
||
this._createTitlebar();
|
||
this._createButtonPane();
|
||
|
||
if ( this.options.draggable && $.fn.draggable ) {
|
||
this._makeDraggable();
|
||
}
|
||
if ( this.options.resizable && $.fn.resizable ) {
|
||
this._makeResizable();
|
||
}
|
||
|
||
this._isOpen = false;
|
||
|
||
this._trackFocus();
|
||
},
|
||
|
||
_init: function() {
|
||
if ( this.options.autoOpen ) {
|
||
this.open();
|
||
}
|
||
},
|
||
|
||
_appendTo: function() {
|
||
var element = this.options.appendTo;
|
||
if ( element && ( element.jquery || element.nodeType ) ) {
|
||
return $( element );
|
||
}
|
||
return this.document.find( element || "body" ).eq( 0 );
|
||
},
|
||
|
||
_destroy: function() {
|
||
var next,
|
||
originalPosition = this.originalPosition;
|
||
|
||
this._untrackInstance();
|
||
this._destroyOverlay();
|
||
|
||
this.element
|
||
.removeUniqueId()
|
||
.css( this.originalCss )
|
||
|
||
// Without detaching first, the following becomes really slow
|
||
.detach();
|
||
|
||
this.uiDialog.remove();
|
||
|
||
if ( this.originalTitle ) {
|
||
this.element.attr( "title", this.originalTitle );
|
||
}
|
||
|
||
next = originalPosition.parent.children().eq( originalPosition.index );
|
||
|
||
// Don't try to place the dialog next to itself (#8613)
|
||
if ( next.length && next[ 0 ] !== this.element[ 0 ] ) {
|
||
next.before( this.element );
|
||
} else {
|
||
originalPosition.parent.append( this.element );
|
||
}
|
||
},
|
||
|
||
widget: function() {
|
||
return this.uiDialog;
|
||
},
|
||
|
||
disable: $.noop,
|
||
enable: $.noop,
|
||
|
||
close: function( event ) {
|
||
var that = this;
|
||
|
||
if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
|
||
return;
|
||
}
|
||
|
||
this._isOpen = false;
|
||
this._focusedElement = null;
|
||
this._destroyOverlay();
|
||
this._untrackInstance();
|
||
|
||
if ( !this.opener.filter( ":focusable" ).trigger( "focus" ).length ) {
|
||
|
||
// Hiding a focused element doesn't trigger blur in WebKit
|
||
// so in case we have nothing to focus on, explicitly blur the active element
|
||
// https://bugs.webkit.org/show_bug.cgi?id=47182
|
||
$.ui.safeBlur( $.ui.safeActiveElement( this.document[ 0 ] ) );
|
||
}
|
||
|
||
this._hide( this.uiDialog, this.options.hide, function() {
|
||
that._trigger( "close", event );
|
||
} );
|
||
},
|
||
|
||
isOpen: function() {
|
||
return this._isOpen;
|
||
},
|
||
|
||
moveToTop: function() {
|
||
this._moveToTop();
|
||
},
|
||
|
||
_moveToTop: function( event, silent ) {
|
||
var moved = false,
|
||
zIndices = this.uiDialog.siblings( ".ui-front:visible" ).map( function() {
|
||
return +$( this ).css( "z-index" );
|
||
} ).get(),
|
||
zIndexMax = Math.max.apply( null, zIndices );
|
||
|
||
if ( zIndexMax >= +this.uiDialog.css( "z-index" ) ) {
|
||
this.uiDialog.css( "z-index", zIndexMax + 1 );
|
||
moved = true;
|
||
}
|
||
|
||
if ( moved && !silent ) {
|
||
this._trigger( "focus", event );
|
||
}
|
||
return moved;
|
||
},
|
||
|
||
open: function() {
|
||
var that = this;
|
||
if ( this._isOpen ) {
|
||
if ( this._moveToTop() ) {
|
||
this._focusTabbable();
|
||
}
|
||
return;
|
||
}
|
||
|
||
this._isOpen = true;
|
||
this.opener = $( $.ui.safeActiveElement( this.document[ 0 ] ) );
|
||
|
||
this._size();
|
||
this._position();
|
||
this._createOverlay();
|
||
this._moveToTop( null, true );
|
||
|
||
// Ensure the overlay is moved to the top with the dialog, but only when
|
||
// opening. The overlay shouldn't move after the dialog is open so that
|
||
// modeless dialogs opened after the modal dialog stack properly.
|
||
if ( this.overlay ) {
|
||
this.overlay.css( "z-index", this.uiDialog.css( "z-index" ) - 1 );
|
||
}
|
||
|
||
this._show( this.uiDialog, this.options.show, function() {
|
||
that._focusTabbable();
|
||
that._trigger( "focus" );
|
||
} );
|
||
|
||
// Track the dialog immediately upon openening in case a focus event
|
||
// somehow occurs outside of the dialog before an element inside the
|
||
// dialog is focused (#10152)
|
||
this._makeFocusTarget();
|
||
|
||
this._trigger( "open" );
|
||
},
|
||
|
||
_focusTabbable: function() {
|
||
|
||
// Set focus to the first match:
|
||
// 1. An element that was focused previously
|
||
// 2. First element inside the dialog matching [autofocus]
|
||
// 3. Tabbable element inside the content element
|
||
// 4. Tabbable element inside the buttonpane
|
||
// 5. The close button
|
||
// 6. The dialog itself
|
||
var hasFocus = this._focusedElement;
|
||
if ( !hasFocus ) {
|
||
hasFocus = this.element.find( "[autofocus]" );
|
||
}
|
||
if ( !hasFocus.length ) {
|
||
hasFocus = this.element.find( ":tabbable" );
|
||
}
|
||
if ( !hasFocus.length ) {
|
||
hasFocus = this.uiDialogButtonPane.find( ":tabbable" );
|
||
}
|
||
if ( !hasFocus.length ) {
|
||
hasFocus = this.uiDialogTitlebarClose.filter( ":tabbable" );
|
||
}
|
||
if ( !hasFocus.length ) {
|
||
hasFocus = this.uiDialog;
|
||
}
|
||
hasFocus.eq( 0 ).trigger( "focus" );
|
||
},
|
||
|
||
_keepFocus: function( event ) {
|
||
function checkFocus() {
|
||
var activeElement = $.ui.safeActiveElement( this.document[ 0 ] ),
|
||
isActive = this.uiDialog[ 0 ] === activeElement ||
|
||
$.contains( this.uiDialog[ 0 ], activeElement );
|
||
if ( !isActive ) {
|
||
this._focusTabbable();
|
||
}
|
||
}
|
||
event.preventDefault();
|
||
checkFocus.call( this );
|
||
|
||
// support: IE
|
||
// IE <= 8 doesn't prevent moving focus even with event.preventDefault()
|
||
// so we check again later
|
||
this._delay( checkFocus );
|
||
},
|
||
|
||
_createWrapper: function() {
|
||
this.uiDialog = $( "<div>" )
|
||
.hide()
|
||
.attr( {
|
||
|
||
// Setting tabIndex makes the div focusable
|
||
tabIndex: -1,
|
||
role: "dialog"
|
||
} )
|
||
.appendTo( this._appendTo() );
|
||
|
||
this._addClass( this.uiDialog, "ui-dialog", "ui-widget ui-widget-content ui-front" );
|
||
this._on( this.uiDialog, {
|
||
keydown: function( event ) {
|
||
if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
|
||
event.keyCode === $.ui.keyCode.ESCAPE ) {
|
||
event.preventDefault();
|
||
this.close( event );
|
||
return;
|
||
}
|
||
|
||
// Prevent tabbing out of dialogs
|
||
if ( event.keyCode !== $.ui.keyCode.TAB || event.isDefaultPrevented() ) {
|
||
return;
|
||
}
|
||
var tabbables = this.uiDialog.find( ":tabbable" ),
|
||
first = tabbables.filter( ":first" ),
|
||
last = tabbables.filter( ":last" );
|
||
|
||
if ( ( event.target === last[ 0 ] || event.target === this.uiDialog[ 0 ] ) &&
|
||
!event.shiftKey ) {
|
||
this._delay( function() {
|
||
first.trigger( "focus" );
|
||
} );
|
||
event.preventDefault();
|
||
} else if ( ( event.target === first[ 0 ] ||
|
||
event.target === this.uiDialog[ 0 ] ) && event.shiftKey ) {
|
||
this._delay( function() {
|
||
last.trigger( "focus" );
|
||
} );
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
mousedown: function( event ) {
|
||
if ( this._moveToTop( event ) ) {
|
||
this._focusTabbable();
|
||
}
|
||
}
|
||
} );
|
||
|
||
// We assume that any existing aria-describedby attribute means
|
||
// that the dialog content is marked up properly
|
||
// otherwise we brute force the content as the description
|
||
if ( !this.element.find( "[aria-describedby]" ).length ) {
|
||
this.uiDialog.attr( {
|
||
"aria-describedby": this.element.uniqueId().attr( "id" )
|
||
} );
|
||
}
|
||
},
|
||
|
||
_createTitlebar: function() {
|
||
var uiDialogTitle;
|
||
|
||
this.uiDialogTitlebar = $( "<div>" );
|
||
this._addClass( this.uiDialogTitlebar,
|
||
"ui-dialog-titlebar", "ui-widget-header ui-helper-clearfix" );
|
||
this._on( this.uiDialogTitlebar, {
|
||
mousedown: function( event ) {
|
||
|
||
// Don't prevent click on close button (#8838)
|
||
// Focusing a dialog that is partially scrolled out of view
|
||
// causes the browser to scroll it into view, preventing the click event
|
||
if ( !$( event.target ).closest( ".ui-dialog-titlebar-close" ) ) {
|
||
|
||
// Dialog isn't getting focus when dragging (#8063)
|
||
this.uiDialog.trigger( "focus" );
|
||
}
|
||
}
|
||
} );
|
||
|
||
// Support: IE
|
||
// Use type="button" to prevent enter keypresses in textboxes from closing the
|
||
// dialog in IE (#9312)
|
||
this.uiDialogTitlebarClose = $( "<button type='button'></button>" )
|
||
.button( {
|
||
label: $( "<a>" ).text( this.options.closeText ).html(),
|
||
icon: "ui-icon-closethick",
|
||
showLabel: false
|
||
} )
|
||
.appendTo( this.uiDialogTitlebar );
|
||
|
||
this._addClass( this.uiDialogTitlebarClose, "ui-dialog-titlebar-close" );
|
||
this._on( this.uiDialogTitlebarClose, {
|
||
click: function( event ) {
|
||
event.preventDefault();
|
||
this.close( event );
|
||
}
|
||
} );
|
||
|
||
uiDialogTitle = $( "<span>" ).uniqueId().prependTo( this.uiDialogTitlebar );
|
||
this._addClass( uiDialogTitle, "ui-dialog-title" );
|
||
this._title( uiDialogTitle );
|
||
|
||
this.uiDialogTitlebar.prependTo( this.uiDialog );
|
||
|
||
this.uiDialog.attr( {
|
||
"aria-labelledby": uiDialogTitle.attr( "id" )
|
||
} );
|
||
},
|
||
|
||
_title: function( title ) {
|
||
if ( this.options.title ) {
|
||
title.text( this.options.title );
|
||
} else {
|
||
title.html( " " );
|
||
}
|
||
},
|
||
|
||
_createButtonPane: function() {
|
||
this.uiDialogButtonPane = $( "<div>" );
|
||
this._addClass( this.uiDialogButtonPane, "ui-dialog-buttonpane",
|
||
"ui-widget-content ui-helper-clearfix" );
|
||
|
||
this.uiButtonSet = $( "<div>" )
|
||
.appendTo( this.uiDialogButtonPane );
|
||
this._addClass( this.uiButtonSet, "ui-dialog-buttonset" );
|
||
|
||
this._createButtons();
|
||
},
|
||
|
||
_createButtons: function() {
|
||
var that = this,
|
||
buttons = this.options.buttons;
|
||
|
||
// If we already have a button pane, remove it
|
||
this.uiDialogButtonPane.remove();
|
||
this.uiButtonSet.empty();
|
||
|
||
if ( $.isEmptyObject( buttons ) || ( $.isArray( buttons ) && !buttons.length ) ) {
|
||
this._removeClass( this.uiDialog, "ui-dialog-buttons" );
|
||
return;
|
||
}
|
||
|
||
$.each( buttons, function( name, props ) {
|
||
var click, buttonOptions;
|
||
props = $.isFunction( props ) ?
|
||
{ click: props, text: name } :
|
||
props;
|
||
|
||
// Default to a non-submitting button
|
||
props = $.extend( { type: "button" }, props );
|
||
|
||
// Change the context for the click callback to be the main element
|
||
click = props.click;
|
||
buttonOptions = {
|
||
icon: props.icon,
|
||
iconPosition: props.iconPosition,
|
||
showLabel: props.showLabel,
|
||
|
||
// Deprecated options
|
||
icons: props.icons,
|
||
text: props.text
|
||
};
|
||
|
||
delete props.click;
|
||
delete props.icon;
|
||
delete props.iconPosition;
|
||
delete props.showLabel;
|
||
|
||
// Deprecated options
|
||
delete props.icons;
|
||
if ( typeof props.text === "boolean" ) {
|
||
delete props.text;
|
||
}
|
||
|
||
$( "<button></button>", props )
|
||
.button( buttonOptions )
|
||
.appendTo( that.uiButtonSet )
|
||
.on( "click", function() {
|
||
click.apply( that.element[ 0 ], arguments );
|
||
} );
|
||
} );
|
||
this._addClass( this.uiDialog, "ui-dialog-buttons" );
|
||
this.uiDialogButtonPane.appendTo( this.uiDialog );
|
||
},
|
||
|
||
_makeDraggable: function() {
|
||
var that = this,
|
||
options = this.options;
|
||
|
||
function filteredUi( ui ) {
|
||
return {
|
||
position: ui.position,
|
||
offset: ui.offset
|
||
};
|
||
}
|
||
|
||
this.uiDialog.draggable( {
|
||
cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
|
||
handle: ".ui-dialog-titlebar",
|
||
containment: "document",
|
||
start: function( event, ui ) {
|
||
that._addClass( $( this ), "ui-dialog-dragging" );
|
||
that._blockFrames();
|
||
that._trigger( "dragStart", event, filteredUi( ui ) );
|
||
},
|
||
drag: function( event, ui ) {
|
||
that._trigger( "drag", event, filteredUi( ui ) );
|
||
},
|
||
stop: function( event, ui ) {
|
||
var left = ui.offset.left - that.document.scrollLeft(),
|
||
top = ui.offset.top - that.document.scrollTop();
|
||
|
||
options.position = {
|
||
my: "left top",
|
||
at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
|
||
"top" + ( top >= 0 ? "+" : "" ) + top,
|
||
of: that.window
|
||
};
|
||
that._removeClass( $( this ), "ui-dialog-dragging" );
|
||
that._unblockFrames();
|
||
that._trigger( "dragStop", event, filteredUi( ui ) );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_makeResizable: function() {
|
||
var that = this,
|
||
options = this.options,
|
||
handles = options.resizable,
|
||
|
||
// .ui-resizable has position: relative defined in the stylesheet
|
||
// but dialogs have to use absolute or fixed positioning
|
||
position = this.uiDialog.css( "position" ),
|
||
resizeHandles = typeof handles === "string" ?
|
||
handles :
|
||
"n,e,s,w,se,sw,ne,nw";
|
||
|
||
function filteredUi( ui ) {
|
||
return {
|
||
originalPosition: ui.originalPosition,
|
||
originalSize: ui.originalSize,
|
||
position: ui.position,
|
||
size: ui.size
|
||
};
|
||
}
|
||
|
||
this.uiDialog.resizable( {
|
||
cancel: ".ui-dialog-content",
|
||
containment: "document",
|
||
alsoResize: this.element,
|
||
maxWidth: options.maxWidth,
|
||
maxHeight: options.maxHeight,
|
||
minWidth: options.minWidth,
|
||
minHeight: this._minHeight(),
|
||
handles: resizeHandles,
|
||
start: function( event, ui ) {
|
||
that._addClass( $( this ), "ui-dialog-resizing" );
|
||
that._blockFrames();
|
||
that._trigger( "resizeStart", event, filteredUi( ui ) );
|
||
},
|
||
resize: function( event, ui ) {
|
||
that._trigger( "resize", event, filteredUi( ui ) );
|
||
},
|
||
stop: function( event, ui ) {
|
||
var offset = that.uiDialog.offset(),
|
||
left = offset.left - that.document.scrollLeft(),
|
||
top = offset.top - that.document.scrollTop();
|
||
|
||
options.height = that.uiDialog.height();
|
||
options.width = that.uiDialog.width();
|
||
options.position = {
|
||
my: "left top",
|
||
at: "left" + ( left >= 0 ? "+" : "" ) + left + " " +
|
||
"top" + ( top >= 0 ? "+" : "" ) + top,
|
||
of: that.window
|
||
};
|
||
that._removeClass( $( this ), "ui-dialog-resizing" );
|
||
that._unblockFrames();
|
||
that._trigger( "resizeStop", event, filteredUi( ui ) );
|
||
}
|
||
} )
|
||
.css( "position", position );
|
||
},
|
||
|
||
_trackFocus: function() {
|
||
this._on( this.widget(), {
|
||
focusin: function( event ) {
|
||
this._makeFocusTarget();
|
||
this._focusedElement = $( event.target );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_makeFocusTarget: function() {
|
||
this._untrackInstance();
|
||
this._trackingInstances().unshift( this );
|
||
},
|
||
|
||
_untrackInstance: function() {
|
||
var instances = this._trackingInstances(),
|
||
exists = $.inArray( this, instances );
|
||
if ( exists !== -1 ) {
|
||
instances.splice( exists, 1 );
|
||
}
|
||
},
|
||
|
||
_trackingInstances: function() {
|
||
var instances = this.document.data( "ui-dialog-instances" );
|
||
if ( !instances ) {
|
||
instances = [];
|
||
this.document.data( "ui-dialog-instances", instances );
|
||
}
|
||
return instances;
|
||
},
|
||
|
||
_minHeight: function() {
|
||
var options = this.options;
|
||
|
||
return options.height === "auto" ?
|
||
options.minHeight :
|
||
Math.min( options.minHeight, options.height );
|
||
},
|
||
|
||
_position: function() {
|
||
|
||
// Need to show the dialog to get the actual offset in the position plugin
|
||
var isVisible = this.uiDialog.is( ":visible" );
|
||
if ( !isVisible ) {
|
||
this.uiDialog.show();
|
||
}
|
||
this.uiDialog.position( this.options.position );
|
||
if ( !isVisible ) {
|
||
this.uiDialog.hide();
|
||
}
|
||
},
|
||
|
||
_setOptions: function( options ) {
|
||
var that = this,
|
||
resize = false,
|
||
resizableOptions = {};
|
||
|
||
$.each( options, function( key, value ) {
|
||
that._setOption( key, value );
|
||
|
||
if ( key in that.sizeRelatedOptions ) {
|
||
resize = true;
|
||
}
|
||
if ( key in that.resizableRelatedOptions ) {
|
||
resizableOptions[ key ] = value;
|
||
}
|
||
} );
|
||
|
||
if ( resize ) {
|
||
this._size();
|
||
this._position();
|
||
}
|
||
if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
|
||
this.uiDialog.resizable( "option", resizableOptions );
|
||
}
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
var isDraggable, isResizable,
|
||
uiDialog = this.uiDialog;
|
||
|
||
if ( key === "disabled" ) {
|
||
return;
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "appendTo" ) {
|
||
this.uiDialog.appendTo( this._appendTo() );
|
||
}
|
||
|
||
if ( key === "buttons" ) {
|
||
this._createButtons();
|
||
}
|
||
|
||
if ( key === "closeText" ) {
|
||
this.uiDialogTitlebarClose.button( {
|
||
|
||
// Ensure that we always pass a string
|
||
label: $( "<a>" ).text( "" + this.options.closeText ).html()
|
||
} );
|
||
}
|
||
|
||
if ( key === "draggable" ) {
|
||
isDraggable = uiDialog.is( ":data(ui-draggable)" );
|
||
if ( isDraggable && !value ) {
|
||
uiDialog.draggable( "destroy" );
|
||
}
|
||
|
||
if ( !isDraggable && value ) {
|
||
this._makeDraggable();
|
||
}
|
||
}
|
||
|
||
if ( key === "position" ) {
|
||
this._position();
|
||
}
|
||
|
||
if ( key === "resizable" ) {
|
||
|
||
// currently resizable, becoming non-resizable
|
||
isResizable = uiDialog.is( ":data(ui-resizable)" );
|
||
if ( isResizable && !value ) {
|
||
uiDialog.resizable( "destroy" );
|
||
}
|
||
|
||
// Currently resizable, changing handles
|
||
if ( isResizable && typeof value === "string" ) {
|
||
uiDialog.resizable( "option", "handles", value );
|
||
}
|
||
|
||
// Currently non-resizable, becoming resizable
|
||
if ( !isResizable && value !== false ) {
|
||
this._makeResizable();
|
||
}
|
||
}
|
||
|
||
if ( key === "title" ) {
|
||
this._title( this.uiDialogTitlebar.find( ".ui-dialog-title" ) );
|
||
}
|
||
},
|
||
|
||
_size: function() {
|
||
|
||
// If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
|
||
// divs will both have width and height set, so we need to reset them
|
||
var nonContentHeight, minContentHeight, maxContentHeight,
|
||
options = this.options;
|
||
|
||
// Reset content sizing
|
||
this.element.show().css( {
|
||
width: "auto",
|
||
minHeight: 0,
|
||
maxHeight: "none",
|
||
height: 0
|
||
} );
|
||
|
||
if ( options.minWidth > options.width ) {
|
||
options.width = options.minWidth;
|
||
}
|
||
|
||
// Reset wrapper sizing
|
||
// determine the height of all the non-content elements
|
||
nonContentHeight = this.uiDialog.css( {
|
||
height: "auto",
|
||
width: options.width
|
||
} )
|
||
.outerHeight();
|
||
minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
|
||
maxContentHeight = typeof options.maxHeight === "number" ?
|
||
Math.max( 0, options.maxHeight - nonContentHeight ) :
|
||
"none";
|
||
|
||
if ( options.height === "auto" ) {
|
||
this.element.css( {
|
||
minHeight: minContentHeight,
|
||
maxHeight: maxContentHeight,
|
||
height: "auto"
|
||
} );
|
||
} else {
|
||
this.element.height( Math.max( 0, options.height - nonContentHeight ) );
|
||
}
|
||
|
||
if ( this.uiDialog.is( ":data(ui-resizable)" ) ) {
|
||
this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
|
||
}
|
||
},
|
||
|
||
_blockFrames: function() {
|
||
this.iframeBlocks = this.document.find( "iframe" ).map( function() {
|
||
var iframe = $( this );
|
||
|
||
return $( "<div>" )
|
||
.css( {
|
||
position: "absolute",
|
||
width: iframe.outerWidth(),
|
||
height: iframe.outerHeight()
|
||
} )
|
||
.appendTo( iframe.parent() )
|
||
.offset( iframe.offset() )[ 0 ];
|
||
} );
|
||
},
|
||
|
||
_unblockFrames: function() {
|
||
if ( this.iframeBlocks ) {
|
||
this.iframeBlocks.remove();
|
||
delete this.iframeBlocks;
|
||
}
|
||
},
|
||
|
||
_allowInteraction: function( event ) {
|
||
if ( $( event.target ).closest( ".ui-dialog" ).length ) {
|
||
return true;
|
||
}
|
||
|
||
// TODO: Remove hack when datepicker implements
|
||
// the .ui-front logic (#8989)
|
||
return !!$( event.target ).closest( ".ui-datepicker" ).length;
|
||
},
|
||
|
||
_createOverlay: function() {
|
||
if ( !this.options.modal ) {
|
||
return;
|
||
}
|
||
|
||
// We use a delay in case the overlay is created from an
|
||
// event that we're going to be cancelling (#2804)
|
||
var isOpening = true;
|
||
this._delay( function() {
|
||
isOpening = false;
|
||
} );
|
||
|
||
if ( !this.document.data( "ui-dialog-overlays" ) ) {
|
||
|
||
// Prevent use of anchors and inputs
|
||
// Using _on() for an event handler shared across many instances is
|
||
// safe because the dialogs stack and must be closed in reverse order
|
||
this._on( this.document, {
|
||
focusin: function( event ) {
|
||
if ( isOpening ) {
|
||
return;
|
||
}
|
||
|
||
if ( !this._allowInteraction( event ) ) {
|
||
event.preventDefault();
|
||
this._trackingInstances()[ 0 ]._focusTabbable();
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
|
||
this.overlay = $( "<div>" )
|
||
.appendTo( this._appendTo() );
|
||
|
||
this._addClass( this.overlay, null, "ui-widget-overlay ui-front" );
|
||
this._on( this.overlay, {
|
||
mousedown: "_keepFocus"
|
||
} );
|
||
this.document.data( "ui-dialog-overlays",
|
||
( this.document.data( "ui-dialog-overlays" ) || 0 ) + 1 );
|
||
},
|
||
|
||
_destroyOverlay: function() {
|
||
if ( !this.options.modal ) {
|
||
return;
|
||
}
|
||
|
||
if ( this.overlay ) {
|
||
var overlays = this.document.data( "ui-dialog-overlays" ) - 1;
|
||
|
||
if ( !overlays ) {
|
||
this._off( this.document, "focusin" );
|
||
this.document.removeData( "ui-dialog-overlays" );
|
||
} else {
|
||
this.document.data( "ui-dialog-overlays", overlays );
|
||
}
|
||
|
||
this.overlay.remove();
|
||
this.overlay = null;
|
||
}
|
||
}
|
||
} );
|
||
|
||
// DEPRECATED
|
||
// TODO: switch return back to widget declaration at top of file when this is removed
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Backcompat for dialogClass option
|
||
$.widget( "ui.dialog", $.ui.dialog, {
|
||
options: {
|
||
dialogClass: ""
|
||
},
|
||
_createWrapper: function() {
|
||
this._super();
|
||
this.uiDialog.addClass( this.options.dialogClass );
|
||
},
|
||
_setOption: function( key, value ) {
|
||
if ( key === "dialogClass" ) {
|
||
this.uiDialog
|
||
.removeClass( this.options.dialogClass )
|
||
.addClass( value );
|
||
}
|
||
this._superApply( arguments );
|
||
}
|
||
} );
|
||
}
|
||
|
||
var widgetsDialog = $.ui.dialog;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Progressbar 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Progressbar
|
||
//>>group: Widgets
|
||
// jscs:disable maximumLineLength
|
||
//>>description: Displays a status indicator for loading state, standard percentage, and other progress indicators.
|
||
// jscs:enable maximumLineLength
|
||
//>>docs: http://api.jqueryui.com/progressbar/
|
||
//>>demos: http://jqueryui.com/progressbar/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/progressbar.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
var widgetsProgressbar = $.widget( "ui.progressbar", {
|
||
version: "1.12.1",
|
||
options: {
|
||
classes: {
|
||
"ui-progressbar": "ui-corner-all",
|
||
"ui-progressbar-value": "ui-corner-left",
|
||
"ui-progressbar-complete": "ui-corner-right"
|
||
},
|
||
max: 100,
|
||
value: 0,
|
||
|
||
change: null,
|
||
complete: null
|
||
},
|
||
|
||
min: 0,
|
||
|
||
_create: function() {
|
||
|
||
// Constrain initial value
|
||
this.oldValue = this.options.value = this._constrainedValue();
|
||
|
||
this.element.attr( {
|
||
|
||
// Only set static values; aria-valuenow and aria-valuemax are
|
||
// set inside _refreshValue()
|
||
role: "progressbar",
|
||
"aria-valuemin": this.min
|
||
} );
|
||
this._addClass( "ui-progressbar", "ui-widget ui-widget-content" );
|
||
|
||
this.valueDiv = $( "<div>" ).appendTo( this.element );
|
||
this._addClass( this.valueDiv, "ui-progressbar-value", "ui-widget-header" );
|
||
this._refreshValue();
|
||
},
|
||
|
||
_destroy: function() {
|
||
this.element.removeAttr( "role aria-valuemin aria-valuemax aria-valuenow" );
|
||
|
||
this.valueDiv.remove();
|
||
},
|
||
|
||
value: function( newValue ) {
|
||
if ( newValue === undefined ) {
|
||
return this.options.value;
|
||
}
|
||
|
||
this.options.value = this._constrainedValue( newValue );
|
||
this._refreshValue();
|
||
},
|
||
|
||
_constrainedValue: function( newValue ) {
|
||
if ( newValue === undefined ) {
|
||
newValue = this.options.value;
|
||
}
|
||
|
||
this.indeterminate = newValue === false;
|
||
|
||
// Sanitize value
|
||
if ( typeof newValue !== "number" ) {
|
||
newValue = 0;
|
||
}
|
||
|
||
return this.indeterminate ? false :
|
||
Math.min( this.options.max, Math.max( this.min, newValue ) );
|
||
},
|
||
|
||
_setOptions: function( options ) {
|
||
|
||
// Ensure "value" option is set after other values (like max)
|
||
var value = options.value;
|
||
delete options.value;
|
||
|
||
this._super( options );
|
||
|
||
this.options.value = this._constrainedValue( value );
|
||
this._refreshValue();
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "max" ) {
|
||
|
||
// Don't allow a max less than min
|
||
value = Math.max( this.min, value );
|
||
}
|
||
this._super( key, value );
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this.element.attr( "aria-disabled", value );
|
||
this._toggleClass( null, "ui-state-disabled", !!value );
|
||
},
|
||
|
||
_percentage: function() {
|
||
return this.indeterminate ?
|
||
100 :
|
||
100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
|
||
},
|
||
|
||
_refreshValue: function() {
|
||
var value = this.options.value,
|
||
percentage = this._percentage();
|
||
|
||
this.valueDiv
|
||
.toggle( this.indeterminate || value > this.min )
|
||
.width( percentage.toFixed( 0 ) + "%" );
|
||
|
||
this
|
||
._toggleClass( this.valueDiv, "ui-progressbar-complete", null,
|
||
value === this.options.max )
|
||
._toggleClass( "ui-progressbar-indeterminate", null, this.indeterminate );
|
||
|
||
if ( this.indeterminate ) {
|
||
this.element.removeAttr( "aria-valuenow" );
|
||
if ( !this.overlayDiv ) {
|
||
this.overlayDiv = $( "<div>" ).appendTo( this.valueDiv );
|
||
this._addClass( this.overlayDiv, "ui-progressbar-overlay" );
|
||
}
|
||
} else {
|
||
this.element.attr( {
|
||
"aria-valuemax": this.options.max,
|
||
"aria-valuenow": value
|
||
} );
|
||
if ( this.overlayDiv ) {
|
||
this.overlayDiv.remove();
|
||
this.overlayDiv = null;
|
||
}
|
||
}
|
||
|
||
if ( this.oldValue !== value ) {
|
||
this.oldValue = value;
|
||
this._trigger( "change" );
|
||
}
|
||
if ( value === this.options.max ) {
|
||
this._trigger( "complete" );
|
||
}
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Selectmenu 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Selectmenu
|
||
//>>group: Widgets
|
||
// jscs:disable maximumLineLength
|
||
//>>description: Duplicates and extends the functionality of a native HTML select element, allowing it to be customizable in behavior and appearance far beyond the limitations of a native select.
|
||
// jscs:enable maximumLineLength
|
||
//>>docs: http://api.jqueryui.com/selectmenu/
|
||
//>>demos: http://jqueryui.com/selectmenu/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/selectmenu.css, ../../themes/base/button.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
var widgetsSelectmenu = $.widget( "ui.selectmenu", [ $.ui.formResetMixin, {
|
||
version: "1.12.1",
|
||
defaultElement: "<select>",
|
||
options: {
|
||
appendTo: null,
|
||
classes: {
|
||
"ui-selectmenu-button-open": "ui-corner-top",
|
||
"ui-selectmenu-button-closed": "ui-corner-all"
|
||
},
|
||
disabled: null,
|
||
icons: {
|
||
button: "ui-icon-triangle-1-s"
|
||
},
|
||
position: {
|
||
my: "left top",
|
||
at: "left bottom",
|
||
collision: "none"
|
||
},
|
||
width: false,
|
||
|
||
// Callbacks
|
||
change: null,
|
||
close: null,
|
||
focus: null,
|
||
open: null,
|
||
select: null
|
||
},
|
||
|
||
_create: function() {
|
||
var selectmenuId = this.element.uniqueId().attr( "id" );
|
||
this.ids = {
|
||
element: selectmenuId,
|
||
button: selectmenuId + "-button",
|
||
menu: selectmenuId + "-menu"
|
||
};
|
||
|
||
this._drawButton();
|
||
this._drawMenu();
|
||
this._bindFormResetHandler();
|
||
|
||
this._rendered = false;
|
||
this.menuItems = $();
|
||
},
|
||
|
||
_drawButton: function() {
|
||
var icon,
|
||
that = this,
|
||
item = this._parseOption(
|
||
this.element.find( "option:selected" ),
|
||
this.element[ 0 ].selectedIndex
|
||
);
|
||
|
||
// Associate existing label with the new button
|
||
this.labels = this.element.labels().attr( "for", this.ids.button );
|
||
this._on( this.labels, {
|
||
click: function( event ) {
|
||
this.button.focus();
|
||
event.preventDefault();
|
||
}
|
||
} );
|
||
|
||
// Hide original select element
|
||
this.element.hide();
|
||
|
||
// Create button
|
||
this.button = $( "<span>", {
|
||
tabindex: this.options.disabled ? -1 : 0,
|
||
id: this.ids.button,
|
||
role: "combobox",
|
||
"aria-expanded": "false",
|
||
"aria-autocomplete": "list",
|
||
"aria-owns": this.ids.menu,
|
||
"aria-haspopup": "true",
|
||
title: this.element.attr( "title" )
|
||
} )
|
||
.insertAfter( this.element );
|
||
|
||
this._addClass( this.button, "ui-selectmenu-button ui-selectmenu-button-closed",
|
||
"ui-button ui-widget" );
|
||
|
||
icon = $( "<span>" ).appendTo( this.button );
|
||
this._addClass( icon, "ui-selectmenu-icon", "ui-icon " + this.options.icons.button );
|
||
this.buttonItem = this._renderButtonItem( item )
|
||
.appendTo( this.button );
|
||
|
||
if ( this.options.width !== false ) {
|
||
this._resizeButton();
|
||
}
|
||
|
||
this._on( this.button, this._buttonEvents );
|
||
this.button.one( "focusin", function() {
|
||
|
||
// Delay rendering the menu items until the button receives focus.
|
||
// The menu may have already been rendered via a programmatic open.
|
||
if ( !that._rendered ) {
|
||
that._refreshMenu();
|
||
}
|
||
} );
|
||
},
|
||
|
||
_drawMenu: function() {
|
||
var that = this;
|
||
|
||
// Create menu
|
||
this.menu = $( "<ul>", {
|
||
"aria-hidden": "true",
|
||
"aria-labelledby": this.ids.button,
|
||
id: this.ids.menu
|
||
} );
|
||
|
||
// Wrap menu
|
||
this.menuWrap = $( "<div>" ).append( this.menu );
|
||
this._addClass( this.menuWrap, "ui-selectmenu-menu", "ui-front" );
|
||
this.menuWrap.appendTo( this._appendTo() );
|
||
|
||
// Initialize menu widget
|
||
this.menuInstance = this.menu
|
||
.menu( {
|
||
classes: {
|
||
"ui-menu": "ui-corner-bottom"
|
||
},
|
||
role: "listbox",
|
||
select: function( event, ui ) {
|
||
event.preventDefault();
|
||
|
||
// Support: IE8
|
||
// If the item was selected via a click, the text selection
|
||
// will be destroyed in IE
|
||
that._setSelection();
|
||
|
||
that._select( ui.item.data( "ui-selectmenu-item" ), event );
|
||
},
|
||
focus: function( event, ui ) {
|
||
var item = ui.item.data( "ui-selectmenu-item" );
|
||
|
||
// Prevent inital focus from firing and check if its a newly focused item
|
||
if ( that.focusIndex != null && item.index !== that.focusIndex ) {
|
||
that._trigger( "focus", event, { item: item } );
|
||
if ( !that.isOpen ) {
|
||
that._select( item, event );
|
||
}
|
||
}
|
||
that.focusIndex = item.index;
|
||
|
||
that.button.attr( "aria-activedescendant",
|
||
that.menuItems.eq( item.index ).attr( "id" ) );
|
||
}
|
||
} )
|
||
.menu( "instance" );
|
||
|
||
// Don't close the menu on mouseleave
|
||
this.menuInstance._off( this.menu, "mouseleave" );
|
||
|
||
// Cancel the menu's collapseAll on document click
|
||
this.menuInstance._closeOnDocumentClick = function() {
|
||
return false;
|
||
};
|
||
|
||
// Selects often contain empty items, but never contain dividers
|
||
this.menuInstance._isDivider = function() {
|
||
return false;
|
||
};
|
||
},
|
||
|
||
refresh: function() {
|
||
this._refreshMenu();
|
||
this.buttonItem.replaceWith(
|
||
this.buttonItem = this._renderButtonItem(
|
||
|
||
// Fall back to an empty object in case there are no options
|
||
this._getSelectedItem().data( "ui-selectmenu-item" ) || {}
|
||
)
|
||
);
|
||
if ( this.options.width === null ) {
|
||
this._resizeButton();
|
||
}
|
||
},
|
||
|
||
_refreshMenu: function() {
|
||
var item,
|
||
options = this.element.find( "option" );
|
||
|
||
this.menu.empty();
|
||
|
||
this._parseOptions( options );
|
||
this._renderMenu( this.menu, this.items );
|
||
|
||
this.menuInstance.refresh();
|
||
this.menuItems = this.menu.find( "li" )
|
||
.not( ".ui-selectmenu-optgroup" )
|
||
.find( ".ui-menu-item-wrapper" );
|
||
|
||
this._rendered = true;
|
||
|
||
if ( !options.length ) {
|
||
return;
|
||
}
|
||
|
||
item = this._getSelectedItem();
|
||
|
||
// Update the menu to have the correct item focused
|
||
this.menuInstance.focus( null, item );
|
||
this._setAria( item.data( "ui-selectmenu-item" ) );
|
||
|
||
// Set disabled state
|
||
this._setOption( "disabled", this.element.prop( "disabled" ) );
|
||
},
|
||
|
||
open: function( event ) {
|
||
if ( this.options.disabled ) {
|
||
return;
|
||
}
|
||
|
||
// If this is the first time the menu is being opened, render the items
|
||
if ( !this._rendered ) {
|
||
this._refreshMenu();
|
||
} else {
|
||
|
||
// Menu clears focus on close, reset focus to selected item
|
||
this._removeClass( this.menu.find( ".ui-state-active" ), null, "ui-state-active" );
|
||
this.menuInstance.focus( null, this._getSelectedItem() );
|
||
}
|
||
|
||
// If there are no options, don't open the menu
|
||
if ( !this.menuItems.length ) {
|
||
return;
|
||
}
|
||
|
||
this.isOpen = true;
|
||
this._toggleAttr();
|
||
this._resizeMenu();
|
||
this._position();
|
||
|
||
this._on( this.document, this._documentClick );
|
||
|
||
this._trigger( "open", event );
|
||
},
|
||
|
||
_position: function() {
|
||
this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );
|
||
},
|
||
|
||
close: function( event ) {
|
||
if ( !this.isOpen ) {
|
||
return;
|
||
}
|
||
|
||
this.isOpen = false;
|
||
this._toggleAttr();
|
||
|
||
this.range = null;
|
||
this._off( this.document );
|
||
|
||
this._trigger( "close", event );
|
||
},
|
||
|
||
widget: function() {
|
||
return this.button;
|
||
},
|
||
|
||
menuWidget: function() {
|
||
return this.menu;
|
||
},
|
||
|
||
_renderButtonItem: function( item ) {
|
||
var buttonItem = $( "<span>" );
|
||
|
||
this._setText( buttonItem, item.label );
|
||
this._addClass( buttonItem, "ui-selectmenu-text" );
|
||
|
||
return buttonItem;
|
||
},
|
||
|
||
_renderMenu: function( ul, items ) {
|
||
var that = this,
|
||
currentOptgroup = "";
|
||
|
||
$.each( items, function( index, item ) {
|
||
var li;
|
||
|
||
if ( item.optgroup !== currentOptgroup ) {
|
||
li = $( "<li>", {
|
||
text: item.optgroup
|
||
} );
|
||
that._addClass( li, "ui-selectmenu-optgroup", "ui-menu-divider" +
|
||
( item.element.parent( "optgroup" ).prop( "disabled" ) ?
|
||
" ui-state-disabled" :
|
||
"" ) );
|
||
|
||
li.appendTo( ul );
|
||
|
||
currentOptgroup = item.optgroup;
|
||
}
|
||
|
||
that._renderItemData( ul, item );
|
||
} );
|
||
},
|
||
|
||
_renderItemData: function( ul, item ) {
|
||
return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
|
||
},
|
||
|
||
_renderItem: function( ul, item ) {
|
||
var li = $( "<li>" ),
|
||
wrapper = $( "<div>", {
|
||
title: item.element.attr( "title" )
|
||
} );
|
||
|
||
if ( item.disabled ) {
|
||
this._addClass( li, null, "ui-state-disabled" );
|
||
}
|
||
this._setText( wrapper, item.label );
|
||
|
||
return li.append( wrapper ).appendTo( ul );
|
||
},
|
||
|
||
_setText: function( element, value ) {
|
||
if ( value ) {
|
||
element.text( value );
|
||
} else {
|
||
element.html( " " );
|
||
}
|
||
},
|
||
|
||
_move: function( direction, event ) {
|
||
var item, next,
|
||
filter = ".ui-menu-item";
|
||
|
||
if ( this.isOpen ) {
|
||
item = this.menuItems.eq( this.focusIndex ).parent( "li" );
|
||
} else {
|
||
item = this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
|
||
filter += ":not(.ui-state-disabled)";
|
||
}
|
||
|
||
if ( direction === "first" || direction === "last" ) {
|
||
next = item[ direction === "first" ? "prevAll" : "nextAll" ]( filter ).eq( -1 );
|
||
} else {
|
||
next = item[ direction + "All" ]( filter ).eq( 0 );
|
||
}
|
||
|
||
if ( next.length ) {
|
||
this.menuInstance.focus( event, next );
|
||
}
|
||
},
|
||
|
||
_getSelectedItem: function() {
|
||
return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
|
||
},
|
||
|
||
_toggle: function( event ) {
|
||
this[ this.isOpen ? "close" : "open" ]( event );
|
||
},
|
||
|
||
_setSelection: function() {
|
||
var selection;
|
||
|
||
if ( !this.range ) {
|
||
return;
|
||
}
|
||
|
||
if ( window.getSelection ) {
|
||
selection = window.getSelection();
|
||
selection.removeAllRanges();
|
||
selection.addRange( this.range );
|
||
|
||
// Support: IE8
|
||
} else {
|
||
this.range.select();
|
||
}
|
||
|
||
// Support: IE
|
||
// Setting the text selection kills the button focus in IE, but
|
||
// restoring the focus doesn't kill the selection.
|
||
this.button.focus();
|
||
},
|
||
|
||
_documentClick: {
|
||
mousedown: function( event ) {
|
||
if ( !this.isOpen ) {
|
||
return;
|
||
}
|
||
|
||
if ( !$( event.target ).closest( ".ui-selectmenu-menu, #" +
|
||
$.ui.escapeSelector( this.ids.button ) ).length ) {
|
||
this.close( event );
|
||
}
|
||
}
|
||
},
|
||
|
||
_buttonEvents: {
|
||
|
||
// Prevent text selection from being reset when interacting with the selectmenu (#10144)
|
||
mousedown: function() {
|
||
var selection;
|
||
|
||
if ( window.getSelection ) {
|
||
selection = window.getSelection();
|
||
if ( selection.rangeCount ) {
|
||
this.range = selection.getRangeAt( 0 );
|
||
}
|
||
|
||
// Support: IE8
|
||
} else {
|
||
this.range = document.selection.createRange();
|
||
}
|
||
},
|
||
|
||
click: function( event ) {
|
||
this._setSelection();
|
||
this._toggle( event );
|
||
},
|
||
|
||
keydown: function( event ) {
|
||
var preventDefault = true;
|
||
switch ( event.keyCode ) {
|
||
case $.ui.keyCode.TAB:
|
||
case $.ui.keyCode.ESCAPE:
|
||
this.close( event );
|
||
preventDefault = false;
|
||
break;
|
||
case $.ui.keyCode.ENTER:
|
||
if ( this.isOpen ) {
|
||
this._selectFocusedItem( event );
|
||
}
|
||
break;
|
||
case $.ui.keyCode.UP:
|
||
if ( event.altKey ) {
|
||
this._toggle( event );
|
||
} else {
|
||
this._move( "prev", event );
|
||
}
|
||
break;
|
||
case $.ui.keyCode.DOWN:
|
||
if ( event.altKey ) {
|
||
this._toggle( event );
|
||
} else {
|
||
this._move( "next", event );
|
||
}
|
||
break;
|
||
case $.ui.keyCode.SPACE:
|
||
if ( this.isOpen ) {
|
||
this._selectFocusedItem( event );
|
||
} else {
|
||
this._toggle( event );
|
||
}
|
||
break;
|
||
case $.ui.keyCode.LEFT:
|
||
this._move( "prev", event );
|
||
break;
|
||
case $.ui.keyCode.RIGHT:
|
||
this._move( "next", event );
|
||
break;
|
||
case $.ui.keyCode.HOME:
|
||
case $.ui.keyCode.PAGE_UP:
|
||
this._move( "first", event );
|
||
break;
|
||
case $.ui.keyCode.END:
|
||
case $.ui.keyCode.PAGE_DOWN:
|
||
this._move( "last", event );
|
||
break;
|
||
default:
|
||
this.menu.trigger( event );
|
||
preventDefault = false;
|
||
}
|
||
|
||
if ( preventDefault ) {
|
||
event.preventDefault();
|
||
}
|
||
}
|
||
},
|
||
|
||
_selectFocusedItem: function( event ) {
|
||
var item = this.menuItems.eq( this.focusIndex ).parent( "li" );
|
||
if ( !item.hasClass( "ui-state-disabled" ) ) {
|
||
this._select( item.data( "ui-selectmenu-item" ), event );
|
||
}
|
||
},
|
||
|
||
_select: function( item, event ) {
|
||
var oldIndex = this.element[ 0 ].selectedIndex;
|
||
|
||
// Change native select element
|
||
this.element[ 0 ].selectedIndex = item.index;
|
||
this.buttonItem.replaceWith( this.buttonItem = this._renderButtonItem( item ) );
|
||
this._setAria( item );
|
||
this._trigger( "select", event, { item: item } );
|
||
|
||
if ( item.index !== oldIndex ) {
|
||
this._trigger( "change", event, { item: item } );
|
||
}
|
||
|
||
this.close( event );
|
||
},
|
||
|
||
_setAria: function( item ) {
|
||
var id = this.menuItems.eq( item.index ).attr( "id" );
|
||
|
||
this.button.attr( {
|
||
"aria-labelledby": id,
|
||
"aria-activedescendant": id
|
||
} );
|
||
this.menu.attr( "aria-activedescendant", id );
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "icons" ) {
|
||
var icon = this.button.find( "span.ui-icon" );
|
||
this._removeClass( icon, null, this.options.icons.button )
|
||
._addClass( icon, null, value.button );
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "appendTo" ) {
|
||
this.menuWrap.appendTo( this._appendTo() );
|
||
}
|
||
|
||
if ( key === "width" ) {
|
||
this._resizeButton();
|
||
}
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this.menuInstance.option( "disabled", value );
|
||
this.button.attr( "aria-disabled", value );
|
||
this._toggleClass( this.button, null, "ui-state-disabled", value );
|
||
|
||
this.element.prop( "disabled", value );
|
||
if ( value ) {
|
||
this.button.attr( "tabindex", -1 );
|
||
this.close();
|
||
} else {
|
||
this.button.attr( "tabindex", 0 );
|
||
}
|
||
},
|
||
|
||
_appendTo: function() {
|
||
var element = this.options.appendTo;
|
||
|
||
if ( element ) {
|
||
element = element.jquery || element.nodeType ?
|
||
$( element ) :
|
||
this.document.find( element ).eq( 0 );
|
||
}
|
||
|
||
if ( !element || !element[ 0 ] ) {
|
||
element = this.element.closest( ".ui-front, dialog" );
|
||
}
|
||
|
||
if ( !element.length ) {
|
||
element = this.document[ 0 ].body;
|
||
}
|
||
|
||
return element;
|
||
},
|
||
|
||
_toggleAttr: function() {
|
||
this.button.attr( "aria-expanded", this.isOpen );
|
||
|
||
// We can't use two _toggleClass() calls here, because we need to make sure
|
||
// we always remove classes first and add them second, otherwise if both classes have the
|
||
// same theme class, it will be removed after we add it.
|
||
this._removeClass( this.button, "ui-selectmenu-button-" +
|
||
( this.isOpen ? "closed" : "open" ) )
|
||
._addClass( this.button, "ui-selectmenu-button-" +
|
||
( this.isOpen ? "open" : "closed" ) )
|
||
._toggleClass( this.menuWrap, "ui-selectmenu-open", null, this.isOpen );
|
||
|
||
this.menu.attr( "aria-hidden", !this.isOpen );
|
||
},
|
||
|
||
_resizeButton: function() {
|
||
var width = this.options.width;
|
||
|
||
// For `width: false`, just remove inline style and stop
|
||
if ( width === false ) {
|
||
this.button.css( "width", "" );
|
||
return;
|
||
}
|
||
|
||
// For `width: null`, match the width of the original element
|
||
if ( width === null ) {
|
||
width = this.element.show().outerWidth();
|
||
this.element.hide();
|
||
}
|
||
|
||
this.button.outerWidth( width );
|
||
},
|
||
|
||
_resizeMenu: function() {
|
||
this.menu.outerWidth( Math.max(
|
||
this.button.outerWidth(),
|
||
|
||
// Support: IE10
|
||
// IE10 wraps long text (possibly a rounding bug)
|
||
// so we add 1px to avoid the wrapping
|
||
this.menu.width( "" ).outerWidth() + 1
|
||
) );
|
||
},
|
||
|
||
_getCreateOptions: function() {
|
||
var options = this._super();
|
||
|
||
options.disabled = this.element.prop( "disabled" );
|
||
|
||
return options;
|
||
},
|
||
|
||
_parseOptions: function( options ) {
|
||
var that = this,
|
||
data = [];
|
||
options.each( function( index, item ) {
|
||
data.push( that._parseOption( $( item ), index ) );
|
||
} );
|
||
this.items = data;
|
||
},
|
||
|
||
_parseOption: function( option, index ) {
|
||
var optgroup = option.parent( "optgroup" );
|
||
|
||
return {
|
||
element: option,
|
||
index: index,
|
||
value: option.val(),
|
||
label: option.text(),
|
||
optgroup: optgroup.attr( "label" ) || "",
|
||
disabled: optgroup.prop( "disabled" ) || option.prop( "disabled" )
|
||
};
|
||
},
|
||
|
||
_destroy: function() {
|
||
this._unbindFormResetHandler();
|
||
this.menuWrap.remove();
|
||
this.button.remove();
|
||
this.element.show();
|
||
this.element.removeUniqueId();
|
||
this.labels.attr( "for", this.ids.element );
|
||
}
|
||
} ] );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Slider 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Slider
|
||
//>>group: Widgets
|
||
//>>description: Displays a flexible slider with ranges and accessibility via keyboard.
|
||
//>>docs: http://api.jqueryui.com/slider/
|
||
//>>demos: http://jqueryui.com/slider/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/slider.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
var widgetsSlider = $.widget( "ui.slider", $.ui.mouse, {
|
||
version: "1.12.1",
|
||
widgetEventPrefix: "slide",
|
||
|
||
options: {
|
||
animate: false,
|
||
classes: {
|
||
"ui-slider": "ui-corner-all",
|
||
"ui-slider-handle": "ui-corner-all",
|
||
|
||
// Note: ui-widget-header isn't the most fittingly semantic framework class for this
|
||
// element, but worked best visually with a variety of themes
|
||
"ui-slider-range": "ui-corner-all ui-widget-header"
|
||
},
|
||
distance: 0,
|
||
max: 100,
|
||
min: 0,
|
||
orientation: "horizontal",
|
||
range: false,
|
||
step: 1,
|
||
value: 0,
|
||
values: null,
|
||
|
||
// Callbacks
|
||
change: null,
|
||
slide: null,
|
||
start: null,
|
||
stop: null
|
||
},
|
||
|
||
// Number of pages in a slider
|
||
// (how many times can you page up/down to go through the whole range)
|
||
numPages: 5,
|
||
|
||
_create: function() {
|
||
this._keySliding = false;
|
||
this._mouseSliding = false;
|
||
this._animateOff = true;
|
||
this._handleIndex = null;
|
||
this._detectOrientation();
|
||
this._mouseInit();
|
||
this._calculateNewMax();
|
||
|
||
this._addClass( "ui-slider ui-slider-" + this.orientation,
|
||
"ui-widget ui-widget-content" );
|
||
|
||
this._refresh();
|
||
|
||
this._animateOff = false;
|
||
},
|
||
|
||
_refresh: function() {
|
||
this._createRange();
|
||
this._createHandles();
|
||
this._setupEvents();
|
||
this._refreshValue();
|
||
},
|
||
|
||
_createHandles: function() {
|
||
var i, handleCount,
|
||
options = this.options,
|
||
existingHandles = this.element.find( ".ui-slider-handle" ),
|
||
handle = "<span tabindex='0'></span>",
|
||
handles = [];
|
||
|
||
handleCount = ( options.values && options.values.length ) || 1;
|
||
|
||
if ( existingHandles.length > handleCount ) {
|
||
existingHandles.slice( handleCount ).remove();
|
||
existingHandles = existingHandles.slice( 0, handleCount );
|
||
}
|
||
|
||
for ( i = existingHandles.length; i < handleCount; i++ ) {
|
||
handles.push( handle );
|
||
}
|
||
|
||
this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
|
||
|
||
this._addClass( this.handles, "ui-slider-handle", "ui-state-default" );
|
||
|
||
this.handle = this.handles.eq( 0 );
|
||
|
||
this.handles.each( function( i ) {
|
||
$( this )
|
||
.data( "ui-slider-handle-index", i )
|
||
.attr( "tabIndex", 0 );
|
||
} );
|
||
},
|
||
|
||
_createRange: function() {
|
||
var options = this.options;
|
||
|
||
if ( options.range ) {
|
||
if ( options.range === true ) {
|
||
if ( !options.values ) {
|
||
options.values = [ this._valueMin(), this._valueMin() ];
|
||
} else if ( options.values.length && options.values.length !== 2 ) {
|
||
options.values = [ options.values[ 0 ], options.values[ 0 ] ];
|
||
} else if ( $.isArray( options.values ) ) {
|
||
options.values = options.values.slice( 0 );
|
||
}
|
||
}
|
||
|
||
if ( !this.range || !this.range.length ) {
|
||
this.range = $( "<div>" )
|
||
.appendTo( this.element );
|
||
|
||
this._addClass( this.range, "ui-slider-range" );
|
||
} else {
|
||
this._removeClass( this.range, "ui-slider-range-min ui-slider-range-max" );
|
||
|
||
// Handle range switching from true to min/max
|
||
this.range.css( {
|
||
"left": "",
|
||
"bottom": ""
|
||
} );
|
||
}
|
||
if ( options.range === "min" || options.range === "max" ) {
|
||
this._addClass( this.range, "ui-slider-range-" + options.range );
|
||
}
|
||
} else {
|
||
if ( this.range ) {
|
||
this.range.remove();
|
||
}
|
||
this.range = null;
|
||
}
|
||
},
|
||
|
||
_setupEvents: function() {
|
||
this._off( this.handles );
|
||
this._on( this.handles, this._handleEvents );
|
||
this._hoverable( this.handles );
|
||
this._focusable( this.handles );
|
||
},
|
||
|
||
_destroy: function() {
|
||
this.handles.remove();
|
||
if ( this.range ) {
|
||
this.range.remove();
|
||
}
|
||
|
||
this._mouseDestroy();
|
||
},
|
||
|
||
_mouseCapture: function( event ) {
|
||
var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
|
||
that = this,
|
||
o = this.options;
|
||
|
||
if ( o.disabled ) {
|
||
return false;
|
||
}
|
||
|
||
this.elementSize = {
|
||
width: this.element.outerWidth(),
|
||
height: this.element.outerHeight()
|
||
};
|
||
this.elementOffset = this.element.offset();
|
||
|
||
position = { x: event.pageX, y: event.pageY };
|
||
normValue = this._normValueFromMouse( position );
|
||
distance = this._valueMax() - this._valueMin() + 1;
|
||
this.handles.each( function( i ) {
|
||
var thisDistance = Math.abs( normValue - that.values( i ) );
|
||
if ( ( distance > thisDistance ) ||
|
||
( distance === thisDistance &&
|
||
( i === that._lastChangedValue || that.values( i ) === o.min ) ) ) {
|
||
distance = thisDistance;
|
||
closestHandle = $( this );
|
||
index = i;
|
||
}
|
||
} );
|
||
|
||
allowed = this._start( event, index );
|
||
if ( allowed === false ) {
|
||
return false;
|
||
}
|
||
this._mouseSliding = true;
|
||
|
||
this._handleIndex = index;
|
||
|
||
this._addClass( closestHandle, null, "ui-state-active" );
|
||
closestHandle.trigger( "focus" );
|
||
|
||
offset = closestHandle.offset();
|
||
mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
|
||
this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
|
||
left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
|
||
top: event.pageY - offset.top -
|
||
( closestHandle.height() / 2 ) -
|
||
( parseInt( closestHandle.css( "borderTopWidth" ), 10 ) || 0 ) -
|
||
( parseInt( closestHandle.css( "borderBottomWidth" ), 10 ) || 0 ) +
|
||
( parseInt( closestHandle.css( "marginTop" ), 10 ) || 0 )
|
||
};
|
||
|
||
if ( !this.handles.hasClass( "ui-state-hover" ) ) {
|
||
this._slide( event, index, normValue );
|
||
}
|
||
this._animateOff = true;
|
||
return true;
|
||
},
|
||
|
||
_mouseStart: function() {
|
||
return true;
|
||
},
|
||
|
||
_mouseDrag: function( event ) {
|
||
var position = { x: event.pageX, y: event.pageY },
|
||
normValue = this._normValueFromMouse( position );
|
||
|
||
this._slide( event, this._handleIndex, normValue );
|
||
|
||
return false;
|
||
},
|
||
|
||
_mouseStop: function( event ) {
|
||
this._removeClass( this.handles, null, "ui-state-active" );
|
||
this._mouseSliding = false;
|
||
|
||
this._stop( event, this._handleIndex );
|
||
this._change( event, this._handleIndex );
|
||
|
||
this._handleIndex = null;
|
||
this._clickOffset = null;
|
||
this._animateOff = false;
|
||
|
||
return false;
|
||
},
|
||
|
||
_detectOrientation: function() {
|
||
this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
|
||
},
|
||
|
||
_normValueFromMouse: function( position ) {
|
||
var pixelTotal,
|
||
pixelMouse,
|
||
percentMouse,
|
||
valueTotal,
|
||
valueMouse;
|
||
|
||
if ( this.orientation === "horizontal" ) {
|
||
pixelTotal = this.elementSize.width;
|
||
pixelMouse = position.x - this.elementOffset.left -
|
||
( this._clickOffset ? this._clickOffset.left : 0 );
|
||
} else {
|
||
pixelTotal = this.elementSize.height;
|
||
pixelMouse = position.y - this.elementOffset.top -
|
||
( this._clickOffset ? this._clickOffset.top : 0 );
|
||
}
|
||
|
||
percentMouse = ( pixelMouse / pixelTotal );
|
||
if ( percentMouse > 1 ) {
|
||
percentMouse = 1;
|
||
}
|
||
if ( percentMouse < 0 ) {
|
||
percentMouse = 0;
|
||
}
|
||
if ( this.orientation === "vertical" ) {
|
||
percentMouse = 1 - percentMouse;
|
||
}
|
||
|
||
valueTotal = this._valueMax() - this._valueMin();
|
||
valueMouse = this._valueMin() + percentMouse * valueTotal;
|
||
|
||
return this._trimAlignValue( valueMouse );
|
||
},
|
||
|
||
_uiHash: function( index, value, values ) {
|
||
var uiHash = {
|
||
handle: this.handles[ index ],
|
||
handleIndex: index,
|
||
value: value !== undefined ? value : this.value()
|
||
};
|
||
|
||
if ( this._hasMultipleValues() ) {
|
||
uiHash.value = value !== undefined ? value : this.values( index );
|
||
uiHash.values = values || this.values();
|
||
}
|
||
|
||
return uiHash;
|
||
},
|
||
|
||
_hasMultipleValues: function() {
|
||
return this.options.values && this.options.values.length;
|
||
},
|
||
|
||
_start: function( event, index ) {
|
||
return this._trigger( "start", event, this._uiHash( index ) );
|
||
},
|
||
|
||
_slide: function( event, index, newVal ) {
|
||
var allowed, otherVal,
|
||
currentValue = this.value(),
|
||
newValues = this.values();
|
||
|
||
if ( this._hasMultipleValues() ) {
|
||
otherVal = this.values( index ? 0 : 1 );
|
||
currentValue = this.values( index );
|
||
|
||
if ( this.options.values.length === 2 && this.options.range === true ) {
|
||
newVal = index === 0 ? Math.min( otherVal, newVal ) : Math.max( otherVal, newVal );
|
||
}
|
||
|
||
newValues[ index ] = newVal;
|
||
}
|
||
|
||
if ( newVal === currentValue ) {
|
||
return;
|
||
}
|
||
|
||
allowed = this._trigger( "slide", event, this._uiHash( index, newVal, newValues ) );
|
||
|
||
// A slide can be canceled by returning false from the slide callback
|
||
if ( allowed === false ) {
|
||
return;
|
||
}
|
||
|
||
if ( this._hasMultipleValues() ) {
|
||
this.values( index, newVal );
|
||
} else {
|
||
this.value( newVal );
|
||
}
|
||
},
|
||
|
||
_stop: function( event, index ) {
|
||
this._trigger( "stop", event, this._uiHash( index ) );
|
||
},
|
||
|
||
_change: function( event, index ) {
|
||
if ( !this._keySliding && !this._mouseSliding ) {
|
||
|
||
//store the last changed value index for reference when handles overlap
|
||
this._lastChangedValue = index;
|
||
this._trigger( "change", event, this._uiHash( index ) );
|
||
}
|
||
},
|
||
|
||
value: function( newValue ) {
|
||
if ( arguments.length ) {
|
||
this.options.value = this._trimAlignValue( newValue );
|
||
this._refreshValue();
|
||
this._change( null, 0 );
|
||
return;
|
||
}
|
||
|
||
return this._value();
|
||
},
|
||
|
||
values: function( index, newValue ) {
|
||
var vals,
|
||
newValues,
|
||
i;
|
||
|
||
if ( arguments.length > 1 ) {
|
||
this.options.values[ index ] = this._trimAlignValue( newValue );
|
||
this._refreshValue();
|
||
this._change( null, index );
|
||
return;
|
||
}
|
||
|
||
if ( arguments.length ) {
|
||
if ( $.isArray( arguments[ 0 ] ) ) {
|
||
vals = this.options.values;
|
||
newValues = arguments[ 0 ];
|
||
for ( i = 0; i < vals.length; i += 1 ) {
|
||
vals[ i ] = this._trimAlignValue( newValues[ i ] );
|
||
this._change( null, i );
|
||
}
|
||
this._refreshValue();
|
||
} else {
|
||
if ( this._hasMultipleValues() ) {
|
||
return this._values( index );
|
||
} else {
|
||
return this.value();
|
||
}
|
||
}
|
||
} else {
|
||
return this._values();
|
||
}
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
var i,
|
||
valsLength = 0;
|
||
|
||
if ( key === "range" && this.options.range === true ) {
|
||
if ( value === "min" ) {
|
||
this.options.value = this._values( 0 );
|
||
this.options.values = null;
|
||
} else if ( value === "max" ) {
|
||
this.options.value = this._values( this.options.values.length - 1 );
|
||
this.options.values = null;
|
||
}
|
||
}
|
||
|
||
if ( $.isArray( this.options.values ) ) {
|
||
valsLength = this.options.values.length;
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
switch ( key ) {
|
||
case "orientation":
|
||
this._detectOrientation();
|
||
this._removeClass( "ui-slider-horizontal ui-slider-vertical" )
|
||
._addClass( "ui-slider-" + this.orientation );
|
||
this._refreshValue();
|
||
if ( this.options.range ) {
|
||
this._refreshRange( value );
|
||
}
|
||
|
||
// Reset positioning from previous orientation
|
||
this.handles.css( value === "horizontal" ? "bottom" : "left", "" );
|
||
break;
|
||
case "value":
|
||
this._animateOff = true;
|
||
this._refreshValue();
|
||
this._change( null, 0 );
|
||
this._animateOff = false;
|
||
break;
|
||
case "values":
|
||
this._animateOff = true;
|
||
this._refreshValue();
|
||
|
||
// Start from the last handle to prevent unreachable handles (#9046)
|
||
for ( i = valsLength - 1; i >= 0; i-- ) {
|
||
this._change( null, i );
|
||
}
|
||
this._animateOff = false;
|
||
break;
|
||
case "step":
|
||
case "min":
|
||
case "max":
|
||
this._animateOff = true;
|
||
this._calculateNewMax();
|
||
this._refreshValue();
|
||
this._animateOff = false;
|
||
break;
|
||
case "range":
|
||
this._animateOff = true;
|
||
this._refresh();
|
||
this._animateOff = false;
|
||
break;
|
||
}
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this._toggleClass( null, "ui-state-disabled", !!value );
|
||
},
|
||
|
||
//internal value getter
|
||
// _value() returns value trimmed by min and max, aligned by step
|
||
_value: function() {
|
||
var val = this.options.value;
|
||
val = this._trimAlignValue( val );
|
||
|
||
return val;
|
||
},
|
||
|
||
//internal values getter
|
||
// _values() returns array of values trimmed by min and max, aligned by step
|
||
// _values( index ) returns single value trimmed by min and max, aligned by step
|
||
_values: function( index ) {
|
||
var val,
|
||
vals,
|
||
i;
|
||
|
||
if ( arguments.length ) {
|
||
val = this.options.values[ index ];
|
||
val = this._trimAlignValue( val );
|
||
|
||
return val;
|
||
} else if ( this._hasMultipleValues() ) {
|
||
|
||
// .slice() creates a copy of the array
|
||
// this copy gets trimmed by min and max and then returned
|
||
vals = this.options.values.slice();
|
||
for ( i = 0; i < vals.length; i += 1 ) {
|
||
vals[ i ] = this._trimAlignValue( vals[ i ] );
|
||
}
|
||
|
||
return vals;
|
||
} else {
|
||
return [];
|
||
}
|
||
},
|
||
|
||
// Returns the step-aligned value that val is closest to, between (inclusive) min and max
|
||
_trimAlignValue: function( val ) {
|
||
if ( val <= this._valueMin() ) {
|
||
return this._valueMin();
|
||
}
|
||
if ( val >= this._valueMax() ) {
|
||
return this._valueMax();
|
||
}
|
||
var step = ( this.options.step > 0 ) ? this.options.step : 1,
|
||
valModStep = ( val - this._valueMin() ) % step,
|
||
alignValue = val - valModStep;
|
||
|
||
if ( Math.abs( valModStep ) * 2 >= step ) {
|
||
alignValue += ( valModStep > 0 ) ? step : ( -step );
|
||
}
|
||
|
||
// Since JavaScript has problems with large floats, round
|
||
// the final value to 5 digits after the decimal point (see #4124)
|
||
return parseFloat( alignValue.toFixed( 5 ) );
|
||
},
|
||
|
||
_calculateNewMax: function() {
|
||
var max = this.options.max,
|
||
min = this._valueMin(),
|
||
step = this.options.step,
|
||
aboveMin = Math.round( ( max - min ) / step ) * step;
|
||
max = aboveMin + min;
|
||
if ( max > this.options.max ) {
|
||
|
||
//If max is not divisible by step, rounding off may increase its value
|
||
max -= step;
|
||
}
|
||
this.max = parseFloat( max.toFixed( this._precision() ) );
|
||
},
|
||
|
||
_precision: function() {
|
||
var precision = this._precisionOf( this.options.step );
|
||
if ( this.options.min !== null ) {
|
||
precision = Math.max( precision, this._precisionOf( this.options.min ) );
|
||
}
|
||
return precision;
|
||
},
|
||
|
||
_precisionOf: function( num ) {
|
||
var str = num.toString(),
|
||
decimal = str.indexOf( "." );
|
||
return decimal === -1 ? 0 : str.length - decimal - 1;
|
||
},
|
||
|
||
_valueMin: function() {
|
||
return this.options.min;
|
||
},
|
||
|
||
_valueMax: function() {
|
||
return this.max;
|
||
},
|
||
|
||
_refreshRange: function( orientation ) {
|
||
if ( orientation === "vertical" ) {
|
||
this.range.css( { "width": "", "left": "" } );
|
||
}
|
||
if ( orientation === "horizontal" ) {
|
||
this.range.css( { "height": "", "bottom": "" } );
|
||
}
|
||
},
|
||
|
||
_refreshValue: function() {
|
||
var lastValPercent, valPercent, value, valueMin, valueMax,
|
||
oRange = this.options.range,
|
||
o = this.options,
|
||
that = this,
|
||
animate = ( !this._animateOff ) ? o.animate : false,
|
||
_set = {};
|
||
|
||
if ( this._hasMultipleValues() ) {
|
||
this.handles.each( function( i ) {
|
||
valPercent = ( that.values( i ) - that._valueMin() ) / ( that._valueMax() -
|
||
that._valueMin() ) * 100;
|
||
_set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
|
||
$( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
|
||
if ( that.options.range === true ) {
|
||
if ( that.orientation === "horizontal" ) {
|
||
if ( i === 0 ) {
|
||
that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
left: valPercent + "%"
|
||
}, o.animate );
|
||
}
|
||
if ( i === 1 ) {
|
||
that.range[ animate ? "animate" : "css" ]( {
|
||
width: ( valPercent - lastValPercent ) + "%"
|
||
}, {
|
||
queue: false,
|
||
duration: o.animate
|
||
} );
|
||
}
|
||
} else {
|
||
if ( i === 0 ) {
|
||
that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
bottom: ( valPercent ) + "%"
|
||
}, o.animate );
|
||
}
|
||
if ( i === 1 ) {
|
||
that.range[ animate ? "animate" : "css" ]( {
|
||
height: ( valPercent - lastValPercent ) + "%"
|
||
}, {
|
||
queue: false,
|
||
duration: o.animate
|
||
} );
|
||
}
|
||
}
|
||
}
|
||
lastValPercent = valPercent;
|
||
} );
|
||
} else {
|
||
value = this.value();
|
||
valueMin = this._valueMin();
|
||
valueMax = this._valueMax();
|
||
valPercent = ( valueMax !== valueMin ) ?
|
||
( value - valueMin ) / ( valueMax - valueMin ) * 100 :
|
||
0;
|
||
_set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
|
||
this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
|
||
|
||
if ( oRange === "min" && this.orientation === "horizontal" ) {
|
||
this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
width: valPercent + "%"
|
||
}, o.animate );
|
||
}
|
||
if ( oRange === "max" && this.orientation === "horizontal" ) {
|
||
this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
width: ( 100 - valPercent ) + "%"
|
||
}, o.animate );
|
||
}
|
||
if ( oRange === "min" && this.orientation === "vertical" ) {
|
||
this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
height: valPercent + "%"
|
||
}, o.animate );
|
||
}
|
||
if ( oRange === "max" && this.orientation === "vertical" ) {
|
||
this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( {
|
||
height: ( 100 - valPercent ) + "%"
|
||
}, o.animate );
|
||
}
|
||
}
|
||
},
|
||
|
||
_handleEvents: {
|
||
keydown: function( event ) {
|
||
var allowed, curVal, newVal, step,
|
||
index = $( event.target ).data( "ui-slider-handle-index" );
|
||
|
||
switch ( event.keyCode ) {
|
||
case $.ui.keyCode.HOME:
|
||
case $.ui.keyCode.END:
|
||
case $.ui.keyCode.PAGE_UP:
|
||
case $.ui.keyCode.PAGE_DOWN:
|
||
case $.ui.keyCode.UP:
|
||
case $.ui.keyCode.RIGHT:
|
||
case $.ui.keyCode.DOWN:
|
||
case $.ui.keyCode.LEFT:
|
||
event.preventDefault();
|
||
if ( !this._keySliding ) {
|
||
this._keySliding = true;
|
||
this._addClass( $( event.target ), null, "ui-state-active" );
|
||
allowed = this._start( event, index );
|
||
if ( allowed === false ) {
|
||
return;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
step = this.options.step;
|
||
if ( this._hasMultipleValues() ) {
|
||
curVal = newVal = this.values( index );
|
||
} else {
|
||
curVal = newVal = this.value();
|
||
}
|
||
|
||
switch ( event.keyCode ) {
|
||
case $.ui.keyCode.HOME:
|
||
newVal = this._valueMin();
|
||
break;
|
||
case $.ui.keyCode.END:
|
||
newVal = this._valueMax();
|
||
break;
|
||
case $.ui.keyCode.PAGE_UP:
|
||
newVal = this._trimAlignValue(
|
||
curVal + ( ( this._valueMax() - this._valueMin() ) / this.numPages )
|
||
);
|
||
break;
|
||
case $.ui.keyCode.PAGE_DOWN:
|
||
newVal = this._trimAlignValue(
|
||
curVal - ( ( this._valueMax() - this._valueMin() ) / this.numPages ) );
|
||
break;
|
||
case $.ui.keyCode.UP:
|
||
case $.ui.keyCode.RIGHT:
|
||
if ( curVal === this._valueMax() ) {
|
||
return;
|
||
}
|
||
newVal = this._trimAlignValue( curVal + step );
|
||
break;
|
||
case $.ui.keyCode.DOWN:
|
||
case $.ui.keyCode.LEFT:
|
||
if ( curVal === this._valueMin() ) {
|
||
return;
|
||
}
|
||
newVal = this._trimAlignValue( curVal - step );
|
||
break;
|
||
}
|
||
|
||
this._slide( event, index, newVal );
|
||
},
|
||
keyup: function( event ) {
|
||
var index = $( event.target ).data( "ui-slider-handle-index" );
|
||
|
||
if ( this._keySliding ) {
|
||
this._keySliding = false;
|
||
this._stop( event, index );
|
||
this._change( event, index );
|
||
this._removeClass( $( event.target ), null, "ui-state-active" );
|
||
}
|
||
}
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Spinner 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Spinner
|
||
//>>group: Widgets
|
||
//>>description: Displays buttons to easily input numbers via the keyboard or mouse.
|
||
//>>docs: http://api.jqueryui.com/spinner/
|
||
//>>demos: http://jqueryui.com/spinner/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/spinner.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
function spinnerModifer( fn ) {
|
||
return function() {
|
||
var previous = this.element.val();
|
||
fn.apply( this, arguments );
|
||
this._refresh();
|
||
if ( previous !== this.element.val() ) {
|
||
this._trigger( "change" );
|
||
}
|
||
};
|
||
}
|
||
|
||
$.widget( "ui.spinner", {
|
||
version: "1.12.1",
|
||
defaultElement: "<input>",
|
||
widgetEventPrefix: "spin",
|
||
options: {
|
||
classes: {
|
||
"ui-spinner": "ui-corner-all",
|
||
"ui-spinner-down": "ui-corner-br",
|
||
"ui-spinner-up": "ui-corner-tr"
|
||
},
|
||
culture: null,
|
||
icons: {
|
||
down: "ui-icon-triangle-1-s",
|
||
up: "ui-icon-triangle-1-n"
|
||
},
|
||
incremental: true,
|
||
max: null,
|
||
min: null,
|
||
numberFormat: null,
|
||
page: 10,
|
||
step: 1,
|
||
|
||
change: null,
|
||
spin: null,
|
||
start: null,
|
||
stop: null
|
||
},
|
||
|
||
_create: function() {
|
||
|
||
// handle string values that need to be parsed
|
||
this._setOption( "max", this.options.max );
|
||
this._setOption( "min", this.options.min );
|
||
this._setOption( "step", this.options.step );
|
||
|
||
// Only format if there is a value, prevents the field from being marked
|
||
// as invalid in Firefox, see #9573.
|
||
if ( this.value() !== "" ) {
|
||
|
||
// Format the value, but don't constrain.
|
||
this._value( this.element.val(), true );
|
||
}
|
||
|
||
this._draw();
|
||
this._on( this._events );
|
||
this._refresh();
|
||
|
||
// Turning off autocomplete prevents the browser from remembering the
|
||
// value when navigating through history, so we re-enable autocomplete
|
||
// if the page is unloaded before the widget is destroyed. #7790
|
||
this._on( this.window, {
|
||
beforeunload: function() {
|
||
this.element.removeAttr( "autocomplete" );
|
||
}
|
||
} );
|
||
},
|
||
|
||
_getCreateOptions: function() {
|
||
var options = this._super();
|
||
var element = this.element;
|
||
|
||
$.each( [ "min", "max", "step" ], function( i, option ) {
|
||
var value = element.attr( option );
|
||
if ( value != null && value.length ) {
|
||
options[ option ] = value;
|
||
}
|
||
} );
|
||
|
||
return options;
|
||
},
|
||
|
||
_events: {
|
||
keydown: function( event ) {
|
||
if ( this._start( event ) && this._keydown( event ) ) {
|
||
event.preventDefault();
|
||
}
|
||
},
|
||
keyup: "_stop",
|
||
focus: function() {
|
||
this.previous = this.element.val();
|
||
},
|
||
blur: function( event ) {
|
||
if ( this.cancelBlur ) {
|
||
delete this.cancelBlur;
|
||
return;
|
||
}
|
||
|
||
this._stop();
|
||
this._refresh();
|
||
if ( this.previous !== this.element.val() ) {
|
||
this._trigger( "change", event );
|
||
}
|
||
},
|
||
mousewheel: function( event, delta ) {
|
||
if ( !delta ) {
|
||
return;
|
||
}
|
||
if ( !this.spinning && !this._start( event ) ) {
|
||
return false;
|
||
}
|
||
|
||
this._spin( ( delta > 0 ? 1 : -1 ) * this.options.step, event );
|
||
clearTimeout( this.mousewheelTimer );
|
||
this.mousewheelTimer = this._delay( function() {
|
||
if ( this.spinning ) {
|
||
this._stop( event );
|
||
}
|
||
}, 100 );
|
||
event.preventDefault();
|
||
},
|
||
"mousedown .ui-spinner-button": function( event ) {
|
||
var previous;
|
||
|
||
// We never want the buttons to have focus; whenever the user is
|
||
// interacting with the spinner, the focus should be on the input.
|
||
// If the input is focused then this.previous is properly set from
|
||
// when the input first received focus. If the input is not focused
|
||
// then we need to set this.previous based on the value before spinning.
|
||
previous = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] ) ?
|
||
this.previous : this.element.val();
|
||
function checkFocus() {
|
||
var isActive = this.element[ 0 ] === $.ui.safeActiveElement( this.document[ 0 ] );
|
||
if ( !isActive ) {
|
||
this.element.trigger( "focus" );
|
||
this.previous = previous;
|
||
|
||
// support: IE
|
||
// IE sets focus asynchronously, so we need to check if focus
|
||
// moved off of the input because the user clicked on the button.
|
||
this._delay( function() {
|
||
this.previous = previous;
|
||
} );
|
||
}
|
||
}
|
||
|
||
// Ensure focus is on (or stays on) the text field
|
||
event.preventDefault();
|
||
checkFocus.call( this );
|
||
|
||
// Support: IE
|
||
// IE doesn't prevent moving focus even with event.preventDefault()
|
||
// so we set a flag to know when we should ignore the blur event
|
||
// and check (again) if focus moved off of the input.
|
||
this.cancelBlur = true;
|
||
this._delay( function() {
|
||
delete this.cancelBlur;
|
||
checkFocus.call( this );
|
||
} );
|
||
|
||
if ( this._start( event ) === false ) {
|
||
return;
|
||
}
|
||
|
||
this._repeat( null, $( event.currentTarget )
|
||
.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
|
||
},
|
||
"mouseup .ui-spinner-button": "_stop",
|
||
"mouseenter .ui-spinner-button": function( event ) {
|
||
|
||
// button will add ui-state-active if mouse was down while mouseleave and kept down
|
||
if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
|
||
return;
|
||
}
|
||
|
||
if ( this._start( event ) === false ) {
|
||
return false;
|
||
}
|
||
this._repeat( null, $( event.currentTarget )
|
||
.hasClass( "ui-spinner-up" ) ? 1 : -1, event );
|
||
},
|
||
|
||
// TODO: do we really want to consider this a stop?
|
||
// shouldn't we just stop the repeater and wait until mouseup before
|
||
// we trigger the stop event?
|
||
"mouseleave .ui-spinner-button": "_stop"
|
||
},
|
||
|
||
// Support mobile enhanced option and make backcompat more sane
|
||
_enhance: function() {
|
||
this.uiSpinner = this.element
|
||
.attr( "autocomplete", "off" )
|
||
.wrap( "<span>" )
|
||
.parent()
|
||
|
||
// Add buttons
|
||
.append(
|
||
"<a></a><a></a>"
|
||
);
|
||
},
|
||
|
||
_draw: function() {
|
||
this._enhance();
|
||
|
||
this._addClass( this.uiSpinner, "ui-spinner", "ui-widget ui-widget-content" );
|
||
this._addClass( "ui-spinner-input" );
|
||
|
||
this.element.attr( "role", "spinbutton" );
|
||
|
||
// Button bindings
|
||
this.buttons = this.uiSpinner.children( "a" )
|
||
.attr( "tabIndex", -1 )
|
||
.attr( "aria-hidden", true )
|
||
.button( {
|
||
classes: {
|
||
"ui-button": ""
|
||
}
|
||
} );
|
||
|
||
// TODO: Right now button does not support classes this is already updated in button PR
|
||
this._removeClass( this.buttons, "ui-corner-all" );
|
||
|
||
this._addClass( this.buttons.first(), "ui-spinner-button ui-spinner-up" );
|
||
this._addClass( this.buttons.last(), "ui-spinner-button ui-spinner-down" );
|
||
this.buttons.first().button( {
|
||
"icon": this.options.icons.up,
|
||
"showLabel": false
|
||
} );
|
||
this.buttons.last().button( {
|
||
"icon": this.options.icons.down,
|
||
"showLabel": false
|
||
} );
|
||
|
||
// IE 6 doesn't understand height: 50% for the buttons
|
||
// unless the wrapper has an explicit height
|
||
if ( this.buttons.height() > Math.ceil( this.uiSpinner.height() * 0.5 ) &&
|
||
this.uiSpinner.height() > 0 ) {
|
||
this.uiSpinner.height( this.uiSpinner.height() );
|
||
}
|
||
},
|
||
|
||
_keydown: function( event ) {
|
||
var options = this.options,
|
||
keyCode = $.ui.keyCode;
|
||
|
||
switch ( event.keyCode ) {
|
||
case keyCode.UP:
|
||
this._repeat( null, 1, event );
|
||
return true;
|
||
case keyCode.DOWN:
|
||
this._repeat( null, -1, event );
|
||
return true;
|
||
case keyCode.PAGE_UP:
|
||
this._repeat( null, options.page, event );
|
||
return true;
|
||
case keyCode.PAGE_DOWN:
|
||
this._repeat( null, -options.page, event );
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
_start: function( event ) {
|
||
if ( !this.spinning && this._trigger( "start", event ) === false ) {
|
||
return false;
|
||
}
|
||
|
||
if ( !this.counter ) {
|
||
this.counter = 1;
|
||
}
|
||
this.spinning = true;
|
||
return true;
|
||
},
|
||
|
||
_repeat: function( i, steps, event ) {
|
||
i = i || 500;
|
||
|
||
clearTimeout( this.timer );
|
||
this.timer = this._delay( function() {
|
||
this._repeat( 40, steps, event );
|
||
}, i );
|
||
|
||
this._spin( steps * this.options.step, event );
|
||
},
|
||
|
||
_spin: function( step, event ) {
|
||
var value = this.value() || 0;
|
||
|
||
if ( !this.counter ) {
|
||
this.counter = 1;
|
||
}
|
||
|
||
value = this._adjustValue( value + step * this._increment( this.counter ) );
|
||
|
||
if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false ) {
|
||
this._value( value );
|
||
this.counter++;
|
||
}
|
||
},
|
||
|
||
_increment: function( i ) {
|
||
var incremental = this.options.incremental;
|
||
|
||
if ( incremental ) {
|
||
return $.isFunction( incremental ) ?
|
||
incremental( i ) :
|
||
Math.floor( i * i * i / 50000 - i * i / 500 + 17 * i / 200 + 1 );
|
||
}
|
||
|
||
return 1;
|
||
},
|
||
|
||
_precision: function() {
|
||
var precision = this._precisionOf( this.options.step );
|
||
if ( this.options.min !== null ) {
|
||
precision = Math.max( precision, this._precisionOf( this.options.min ) );
|
||
}
|
||
return precision;
|
||
},
|
||
|
||
_precisionOf: function( num ) {
|
||
var str = num.toString(),
|
||
decimal = str.indexOf( "." );
|
||
return decimal === -1 ? 0 : str.length - decimal - 1;
|
||
},
|
||
|
||
_adjustValue: function( value ) {
|
||
var base, aboveMin,
|
||
options = this.options;
|
||
|
||
// Make sure we're at a valid step
|
||
// - find out where we are relative to the base (min or 0)
|
||
base = options.min !== null ? options.min : 0;
|
||
aboveMin = value - base;
|
||
|
||
// - round to the nearest step
|
||
aboveMin = Math.round( aboveMin / options.step ) * options.step;
|
||
|
||
// - rounding is based on 0, so adjust back to our base
|
||
value = base + aboveMin;
|
||
|
||
// Fix precision from bad JS floating point math
|
||
value = parseFloat( value.toFixed( this._precision() ) );
|
||
|
||
// Clamp the value
|
||
if ( options.max !== null && value > options.max ) {
|
||
return options.max;
|
||
}
|
||
if ( options.min !== null && value < options.min ) {
|
||
return options.min;
|
||
}
|
||
|
||
return value;
|
||
},
|
||
|
||
_stop: function( event ) {
|
||
if ( !this.spinning ) {
|
||
return;
|
||
}
|
||
|
||
clearTimeout( this.timer );
|
||
clearTimeout( this.mousewheelTimer );
|
||
this.counter = 0;
|
||
this.spinning = false;
|
||
this._trigger( "stop", event );
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
var prevValue, first, last;
|
||
|
||
if ( key === "culture" || key === "numberFormat" ) {
|
||
prevValue = this._parse( this.element.val() );
|
||
this.options[ key ] = value;
|
||
this.element.val( this._format( prevValue ) );
|
||
return;
|
||
}
|
||
|
||
if ( key === "max" || key === "min" || key === "step" ) {
|
||
if ( typeof value === "string" ) {
|
||
value = this._parse( value );
|
||
}
|
||
}
|
||
if ( key === "icons" ) {
|
||
first = this.buttons.first().find( ".ui-icon" );
|
||
this._removeClass( first, null, this.options.icons.up );
|
||
this._addClass( first, null, value.up );
|
||
last = this.buttons.last().find( ".ui-icon" );
|
||
this._removeClass( last, null, this.options.icons.down );
|
||
this._addClass( last, null, value.down );
|
||
}
|
||
|
||
this._super( key, value );
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this._super( value );
|
||
|
||
this._toggleClass( this.uiSpinner, null, "ui-state-disabled", !!value );
|
||
this.element.prop( "disabled", !!value );
|
||
this.buttons.button( value ? "disable" : "enable" );
|
||
},
|
||
|
||
_setOptions: spinnerModifer( function( options ) {
|
||
this._super( options );
|
||
} ),
|
||
|
||
_parse: function( val ) {
|
||
if ( typeof val === "string" && val !== "" ) {
|
||
val = window.Globalize && this.options.numberFormat ?
|
||
Globalize.parseFloat( val, 10, this.options.culture ) : +val;
|
||
}
|
||
return val === "" || isNaN( val ) ? null : val;
|
||
},
|
||
|
||
_format: function( value ) {
|
||
if ( value === "" ) {
|
||
return "";
|
||
}
|
||
return window.Globalize && this.options.numberFormat ?
|
||
Globalize.format( value, this.options.numberFormat, this.options.culture ) :
|
||
value;
|
||
},
|
||
|
||
_refresh: function() {
|
||
this.element.attr( {
|
||
"aria-valuemin": this.options.min,
|
||
"aria-valuemax": this.options.max,
|
||
|
||
// TODO: what should we do with values that can't be parsed?
|
||
"aria-valuenow": this._parse( this.element.val() )
|
||
} );
|
||
},
|
||
|
||
isValid: function() {
|
||
var value = this.value();
|
||
|
||
// Null is invalid
|
||
if ( value === null ) {
|
||
return false;
|
||
}
|
||
|
||
// If value gets adjusted, it's invalid
|
||
return value === this._adjustValue( value );
|
||
},
|
||
|
||
// Update the value without triggering change
|
||
_value: function( value, allowAny ) {
|
||
var parsed;
|
||
if ( value !== "" ) {
|
||
parsed = this._parse( value );
|
||
if ( parsed !== null ) {
|
||
if ( !allowAny ) {
|
||
parsed = this._adjustValue( parsed );
|
||
}
|
||
value = this._format( parsed );
|
||
}
|
||
}
|
||
this.element.val( value );
|
||
this._refresh();
|
||
},
|
||
|
||
_destroy: function() {
|
||
this.element
|
||
.prop( "disabled", false )
|
||
.removeAttr( "autocomplete role aria-valuemin aria-valuemax aria-valuenow" );
|
||
|
||
this.uiSpinner.replaceWith( this.element );
|
||
},
|
||
|
||
stepUp: spinnerModifer( function( steps ) {
|
||
this._stepUp( steps );
|
||
} ),
|
||
_stepUp: function( steps ) {
|
||
if ( this._start() ) {
|
||
this._spin( ( steps || 1 ) * this.options.step );
|
||
this._stop();
|
||
}
|
||
},
|
||
|
||
stepDown: spinnerModifer( function( steps ) {
|
||
this._stepDown( steps );
|
||
} ),
|
||
_stepDown: function( steps ) {
|
||
if ( this._start() ) {
|
||
this._spin( ( steps || 1 ) * -this.options.step );
|
||
this._stop();
|
||
}
|
||
},
|
||
|
||
pageUp: spinnerModifer( function( pages ) {
|
||
this._stepUp( ( pages || 1 ) * this.options.page );
|
||
} ),
|
||
|
||
pageDown: spinnerModifer( function( pages ) {
|
||
this._stepDown( ( pages || 1 ) * this.options.page );
|
||
} ),
|
||
|
||
value: function( newVal ) {
|
||
if ( !arguments.length ) {
|
||
return this._parse( this.element.val() );
|
||
}
|
||
spinnerModifer( this._value ).call( this, newVal );
|
||
},
|
||
|
||
widget: function() {
|
||
return this.uiSpinner;
|
||
}
|
||
} );
|
||
|
||
// DEPRECATED
|
||
// TODO: switch return back to widget declaration at top of file when this is removed
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Backcompat for spinner html extension points
|
||
$.widget( "ui.spinner", $.ui.spinner, {
|
||
_enhance: function() {
|
||
this.uiSpinner = this.element
|
||
.attr( "autocomplete", "off" )
|
||
.wrap( this._uiSpinnerHtml() )
|
||
.parent()
|
||
|
||
// Add buttons
|
||
.append( this._buttonHtml() );
|
||
},
|
||
_uiSpinnerHtml: function() {
|
||
return "<span>";
|
||
},
|
||
|
||
_buttonHtml: function() {
|
||
return "<a></a><a></a>";
|
||
}
|
||
} );
|
||
}
|
||
|
||
var widgetsSpinner = $.ui.spinner;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Tabs 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Tabs
|
||
//>>group: Widgets
|
||
//>>description: Transforms a set of container elements into a tab structure.
|
||
//>>docs: http://api.jqueryui.com/tabs/
|
||
//>>demos: http://jqueryui.com/tabs/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/tabs.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.tabs", {
|
||
version: "1.12.1",
|
||
delay: 300,
|
||
options: {
|
||
active: null,
|
||
classes: {
|
||
"ui-tabs": "ui-corner-all",
|
||
"ui-tabs-nav": "ui-corner-all",
|
||
"ui-tabs-panel": "ui-corner-bottom",
|
||
"ui-tabs-tab": "ui-corner-top"
|
||
},
|
||
collapsible: false,
|
||
event: "click",
|
||
heightStyle: "content",
|
||
hide: null,
|
||
show: null,
|
||
|
||
// Callbacks
|
||
activate: null,
|
||
beforeActivate: null,
|
||
beforeLoad: null,
|
||
load: null
|
||
},
|
||
|
||
_isLocal: ( function() {
|
||
var rhash = /#.*$/;
|
||
|
||
return function( anchor ) {
|
||
var anchorUrl, locationUrl;
|
||
|
||
anchorUrl = anchor.href.replace( rhash, "" );
|
||
locationUrl = location.href.replace( rhash, "" );
|
||
|
||
// Decoding may throw an error if the URL isn't UTF-8 (#9518)
|
||
try {
|
||
anchorUrl = decodeURIComponent( anchorUrl );
|
||
} catch ( error ) {}
|
||
try {
|
||
locationUrl = decodeURIComponent( locationUrl );
|
||
} catch ( error ) {}
|
||
|
||
return anchor.hash.length > 1 && anchorUrl === locationUrl;
|
||
};
|
||
} )(),
|
||
|
||
_create: function() {
|
||
var that = this,
|
||
options = this.options;
|
||
|
||
this.running = false;
|
||
|
||
this._addClass( "ui-tabs", "ui-widget ui-widget-content" );
|
||
this._toggleClass( "ui-tabs-collapsible", null, options.collapsible );
|
||
|
||
this._processTabs();
|
||
options.active = this._initialActive();
|
||
|
||
// Take disabling tabs via class attribute from HTML
|
||
// into account and update option properly.
|
||
if ( $.isArray( options.disabled ) ) {
|
||
options.disabled = $.unique( options.disabled.concat(
|
||
$.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
|
||
return that.tabs.index( li );
|
||
} )
|
||
) ).sort();
|
||
}
|
||
|
||
// Check for length avoids error when initializing empty list
|
||
if ( this.options.active !== false && this.anchors.length ) {
|
||
this.active = this._findActive( options.active );
|
||
} else {
|
||
this.active = $();
|
||
}
|
||
|
||
this._refresh();
|
||
|
||
if ( this.active.length ) {
|
||
this.load( options.active );
|
||
}
|
||
},
|
||
|
||
_initialActive: function() {
|
||
var active = this.options.active,
|
||
collapsible = this.options.collapsible,
|
||
locationHash = location.hash.substring( 1 );
|
||
|
||
if ( active === null ) {
|
||
|
||
// check the fragment identifier in the URL
|
||
if ( locationHash ) {
|
||
this.tabs.each( function( i, tab ) {
|
||
if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
|
||
active = i;
|
||
return false;
|
||
}
|
||
} );
|
||
}
|
||
|
||
// Check for a tab marked active via a class
|
||
if ( active === null ) {
|
||
active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
|
||
}
|
||
|
||
// No active tab, set to false
|
||
if ( active === null || active === -1 ) {
|
||
active = this.tabs.length ? 0 : false;
|
||
}
|
||
}
|
||
|
||
// Handle numbers: negative, out of range
|
||
if ( active !== false ) {
|
||
active = this.tabs.index( this.tabs.eq( active ) );
|
||
if ( active === -1 ) {
|
||
active = collapsible ? false : 0;
|
||
}
|
||
}
|
||
|
||
// Don't allow collapsible: false and active: false
|
||
if ( !collapsible && active === false && this.anchors.length ) {
|
||
active = 0;
|
||
}
|
||
|
||
return active;
|
||
},
|
||
|
||
_getCreateEventData: function() {
|
||
return {
|
||
tab: this.active,
|
||
panel: !this.active.length ? $() : this._getPanelForTab( this.active )
|
||
};
|
||
},
|
||
|
||
_tabKeydown: function( event ) {
|
||
var focusedTab = $( $.ui.safeActiveElement( this.document[ 0 ] ) ).closest( "li" ),
|
||
selectedIndex = this.tabs.index( focusedTab ),
|
||
goingForward = true;
|
||
|
||
if ( this._handlePageNav( event ) ) {
|
||
return;
|
||
}
|
||
|
||
switch ( event.keyCode ) {
|
||
case $.ui.keyCode.RIGHT:
|
||
case $.ui.keyCode.DOWN:
|
||
selectedIndex++;
|
||
break;
|
||
case $.ui.keyCode.UP:
|
||
case $.ui.keyCode.LEFT:
|
||
goingForward = false;
|
||
selectedIndex--;
|
||
break;
|
||
case $.ui.keyCode.END:
|
||
selectedIndex = this.anchors.length - 1;
|
||
break;
|
||
case $.ui.keyCode.HOME:
|
||
selectedIndex = 0;
|
||
break;
|
||
case $.ui.keyCode.SPACE:
|
||
|
||
// Activate only, no collapsing
|
||
event.preventDefault();
|
||
clearTimeout( this.activating );
|
||
this._activate( selectedIndex );
|
||
return;
|
||
case $.ui.keyCode.ENTER:
|
||
|
||
// Toggle (cancel delayed activation, allow collapsing)
|
||
event.preventDefault();
|
||
clearTimeout( this.activating );
|
||
|
||
// Determine if we should collapse or activate
|
||
this._activate( selectedIndex === this.options.active ? false : selectedIndex );
|
||
return;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
// Focus the appropriate tab, based on which key was pressed
|
||
event.preventDefault();
|
||
clearTimeout( this.activating );
|
||
selectedIndex = this._focusNextTab( selectedIndex, goingForward );
|
||
|
||
// Navigating with control/command key will prevent automatic activation
|
||
if ( !event.ctrlKey && !event.metaKey ) {
|
||
|
||
// Update aria-selected immediately so that AT think the tab is already selected.
|
||
// Otherwise AT may confuse the user by stating that they need to activate the tab,
|
||
// but the tab will already be activated by the time the announcement finishes.
|
||
focusedTab.attr( "aria-selected", "false" );
|
||
this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
|
||
|
||
this.activating = this._delay( function() {
|
||
this.option( "active", selectedIndex );
|
||
}, this.delay );
|
||
}
|
||
},
|
||
|
||
_panelKeydown: function( event ) {
|
||
if ( this._handlePageNav( event ) ) {
|
||
return;
|
||
}
|
||
|
||
// Ctrl+up moves focus to the current tab
|
||
if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
|
||
event.preventDefault();
|
||
this.active.trigger( "focus" );
|
||
}
|
||
},
|
||
|
||
// Alt+page up/down moves focus to the previous/next tab (and activates)
|
||
_handlePageNav: function( event ) {
|
||
if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
|
||
this._activate( this._focusNextTab( this.options.active - 1, false ) );
|
||
return true;
|
||
}
|
||
if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
|
||
this._activate( this._focusNextTab( this.options.active + 1, true ) );
|
||
return true;
|
||
}
|
||
},
|
||
|
||
_findNextTab: function( index, goingForward ) {
|
||
var lastTabIndex = this.tabs.length - 1;
|
||
|
||
function constrain() {
|
||
if ( index > lastTabIndex ) {
|
||
index = 0;
|
||
}
|
||
if ( index < 0 ) {
|
||
index = lastTabIndex;
|
||
}
|
||
return index;
|
||
}
|
||
|
||
while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
|
||
index = goingForward ? index + 1 : index - 1;
|
||
}
|
||
|
||
return index;
|
||
},
|
||
|
||
_focusNextTab: function( index, goingForward ) {
|
||
index = this._findNextTab( index, goingForward );
|
||
this.tabs.eq( index ).trigger( "focus" );
|
||
return index;
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
if ( key === "active" ) {
|
||
|
||
// _activate() will handle invalid values and update this.options
|
||
this._activate( value );
|
||
return;
|
||
}
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "collapsible" ) {
|
||
this._toggleClass( "ui-tabs-collapsible", null, value );
|
||
|
||
// Setting collapsible: false while collapsed; open first panel
|
||
if ( !value && this.options.active === false ) {
|
||
this._activate( 0 );
|
||
}
|
||
}
|
||
|
||
if ( key === "event" ) {
|
||
this._setupEvents( value );
|
||
}
|
||
|
||
if ( key === "heightStyle" ) {
|
||
this._setupHeightStyle( value );
|
||
}
|
||
},
|
||
|
||
_sanitizeSelector: function( hash ) {
|
||
return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
|
||
},
|
||
|
||
refresh: function() {
|
||
var options = this.options,
|
||
lis = this.tablist.children( ":has(a[href])" );
|
||
|
||
// Get disabled tabs from class attribute from HTML
|
||
// this will get converted to a boolean if needed in _refresh()
|
||
options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
|
||
return lis.index( tab );
|
||
} );
|
||
|
||
this._processTabs();
|
||
|
||
// Was collapsed or no tabs
|
||
if ( options.active === false || !this.anchors.length ) {
|
||
options.active = false;
|
||
this.active = $();
|
||
|
||
// was active, but active tab is gone
|
||
} else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
|
||
|
||
// all remaining tabs are disabled
|
||
if ( this.tabs.length === options.disabled.length ) {
|
||
options.active = false;
|
||
this.active = $();
|
||
|
||
// activate previous tab
|
||
} else {
|
||
this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
|
||
}
|
||
|
||
// was active, active tab still exists
|
||
} else {
|
||
|
||
// make sure active index is correct
|
||
options.active = this.tabs.index( this.active );
|
||
}
|
||
|
||
this._refresh();
|
||
},
|
||
|
||
_refresh: function() {
|
||
this._setOptionDisabled( this.options.disabled );
|
||
this._setupEvents( this.options.event );
|
||
this._setupHeightStyle( this.options.heightStyle );
|
||
|
||
this.tabs.not( this.active ).attr( {
|
||
"aria-selected": "false",
|
||
"aria-expanded": "false",
|
||
tabIndex: -1
|
||
} );
|
||
this.panels.not( this._getPanelForTab( this.active ) )
|
||
.hide()
|
||
.attr( {
|
||
"aria-hidden": "true"
|
||
} );
|
||
|
||
// Make sure one tab is in the tab order
|
||
if ( !this.active.length ) {
|
||
this.tabs.eq( 0 ).attr( "tabIndex", 0 );
|
||
} else {
|
||
this.active
|
||
.attr( {
|
||
"aria-selected": "true",
|
||
"aria-expanded": "true",
|
||
tabIndex: 0
|
||
} );
|
||
this._addClass( this.active, "ui-tabs-active", "ui-state-active" );
|
||
this._getPanelForTab( this.active )
|
||
.show()
|
||
.attr( {
|
||
"aria-hidden": "false"
|
||
} );
|
||
}
|
||
},
|
||
|
||
_processTabs: function() {
|
||
var that = this,
|
||
prevTabs = this.tabs,
|
||
prevAnchors = this.anchors,
|
||
prevPanels = this.panels;
|
||
|
||
this.tablist = this._getList().attr( "role", "tablist" );
|
||
this._addClass( this.tablist, "ui-tabs-nav",
|
||
"ui-helper-reset ui-helper-clearfix ui-widget-header" );
|
||
|
||
// Prevent users from focusing disabled tabs via click
|
||
this.tablist
|
||
.on( "mousedown" + this.eventNamespace, "> li", function( event ) {
|
||
if ( $( this ).is( ".ui-state-disabled" ) ) {
|
||
event.preventDefault();
|
||
}
|
||
} )
|
||
|
||
// Support: IE <9
|
||
// Preventing the default action in mousedown doesn't prevent IE
|
||
// from focusing the element, so if the anchor gets focused, blur.
|
||
// We don't have to worry about focusing the previously focused
|
||
// element since clicking on a non-focusable element should focus
|
||
// the body anyway.
|
||
.on( "focus" + this.eventNamespace, ".ui-tabs-anchor", function() {
|
||
if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
|
||
this.blur();
|
||
}
|
||
} );
|
||
|
||
this.tabs = this.tablist.find( "> li:has(a[href])" )
|
||
.attr( {
|
||
role: "tab",
|
||
tabIndex: -1
|
||
} );
|
||
this._addClass( this.tabs, "ui-tabs-tab", "ui-state-default" );
|
||
|
||
this.anchors = this.tabs.map( function() {
|
||
return $( "a", this )[ 0 ];
|
||
} )
|
||
.attr( {
|
||
role: "presentation",
|
||
tabIndex: -1
|
||
} );
|
||
this._addClass( this.anchors, "ui-tabs-anchor" );
|
||
|
||
this.panels = $();
|
||
|
||
this.anchors.each( function( i, anchor ) {
|
||
var selector, panel, panelId,
|
||
anchorId = $( anchor ).uniqueId().attr( "id" ),
|
||
tab = $( anchor ).closest( "li" ),
|
||
originalAriaControls = tab.attr( "aria-controls" );
|
||
|
||
// Inline tab
|
||
if ( that._isLocal( anchor ) ) {
|
||
selector = anchor.hash;
|
||
panelId = selector.substring( 1 );
|
||
panel = that.element.find( that._sanitizeSelector( selector ) );
|
||
|
||
// remote tab
|
||
} else {
|
||
|
||
// If the tab doesn't already have aria-controls,
|
||
// generate an id by using a throw-away element
|
||
panelId = tab.attr( "aria-controls" ) || $( {} ).uniqueId()[ 0 ].id;
|
||
selector = "#" + panelId;
|
||
panel = that.element.find( selector );
|
||
if ( !panel.length ) {
|
||
panel = that._createPanel( panelId );
|
||
panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
|
||
}
|
||
panel.attr( "aria-live", "polite" );
|
||
}
|
||
|
||
if ( panel.length ) {
|
||
that.panels = that.panels.add( panel );
|
||
}
|
||
if ( originalAriaControls ) {
|
||
tab.data( "ui-tabs-aria-controls", originalAriaControls );
|
||
}
|
||
tab.attr( {
|
||
"aria-controls": panelId,
|
||
"aria-labelledby": anchorId
|
||
} );
|
||
panel.attr( "aria-labelledby", anchorId );
|
||
} );
|
||
|
||
this.panels.attr( "role", "tabpanel" );
|
||
this._addClass( this.panels, "ui-tabs-panel", "ui-widget-content" );
|
||
|
||
// Avoid memory leaks (#10056)
|
||
if ( prevTabs ) {
|
||
this._off( prevTabs.not( this.tabs ) );
|
||
this._off( prevAnchors.not( this.anchors ) );
|
||
this._off( prevPanels.not( this.panels ) );
|
||
}
|
||
},
|
||
|
||
// Allow overriding how to find the list for rare usage scenarios (#7715)
|
||
_getList: function() {
|
||
return this.tablist || this.element.find( "ol, ul" ).eq( 0 );
|
||
},
|
||
|
||
_createPanel: function( id ) {
|
||
return $( "<div>" )
|
||
.attr( "id", id )
|
||
.data( "ui-tabs-destroy", true );
|
||
},
|
||
|
||
_setOptionDisabled: function( disabled ) {
|
||
var currentItem, li, i;
|
||
|
||
if ( $.isArray( disabled ) ) {
|
||
if ( !disabled.length ) {
|
||
disabled = false;
|
||
} else if ( disabled.length === this.anchors.length ) {
|
||
disabled = true;
|
||
}
|
||
}
|
||
|
||
// Disable tabs
|
||
for ( i = 0; ( li = this.tabs[ i ] ); i++ ) {
|
||
currentItem = $( li );
|
||
if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
|
||
currentItem.attr( "aria-disabled", "true" );
|
||
this._addClass( currentItem, null, "ui-state-disabled" );
|
||
} else {
|
||
currentItem.removeAttr( "aria-disabled" );
|
||
this._removeClass( currentItem, null, "ui-state-disabled" );
|
||
}
|
||
}
|
||
|
||
this.options.disabled = disabled;
|
||
|
||
this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null,
|
||
disabled === true );
|
||
},
|
||
|
||
_setupEvents: function( event ) {
|
||
var events = {};
|
||
if ( event ) {
|
||
$.each( event.split( " " ), function( index, eventName ) {
|
||
events[ eventName ] = "_eventHandler";
|
||
} );
|
||
}
|
||
|
||
this._off( this.anchors.add( this.tabs ).add( this.panels ) );
|
||
|
||
// Always prevent the default action, even when disabled
|
||
this._on( true, this.anchors, {
|
||
click: function( event ) {
|
||
event.preventDefault();
|
||
}
|
||
} );
|
||
this._on( this.anchors, events );
|
||
this._on( this.tabs, { keydown: "_tabKeydown" } );
|
||
this._on( this.panels, { keydown: "_panelKeydown" } );
|
||
|
||
this._focusable( this.tabs );
|
||
this._hoverable( this.tabs );
|
||
},
|
||
|
||
_setupHeightStyle: function( heightStyle ) {
|
||
var maxHeight,
|
||
parent = this.element.parent();
|
||
|
||
if ( heightStyle === "fill" ) {
|
||
maxHeight = parent.height();
|
||
maxHeight -= this.element.outerHeight() - this.element.height();
|
||
|
||
this.element.siblings( ":visible" ).each( function() {
|
||
var elem = $( this ),
|
||
position = elem.css( "position" );
|
||
|
||
if ( position === "absolute" || position === "fixed" ) {
|
||
return;
|
||
}
|
||
maxHeight -= elem.outerHeight( true );
|
||
} );
|
||
|
||
this.element.children().not( this.panels ).each( function() {
|
||
maxHeight -= $( this ).outerHeight( true );
|
||
} );
|
||
|
||
this.panels.each( function() {
|
||
$( this ).height( Math.max( 0, maxHeight -
|
||
$( this ).innerHeight() + $( this ).height() ) );
|
||
} )
|
||
.css( "overflow", "auto" );
|
||
} else if ( heightStyle === "auto" ) {
|
||
maxHeight = 0;
|
||
this.panels.each( function() {
|
||
maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
|
||
} ).height( maxHeight );
|
||
}
|
||
},
|
||
|
||
_eventHandler: function( event ) {
|
||
var options = this.options,
|
||
active = this.active,
|
||
anchor = $( event.currentTarget ),
|
||
tab = anchor.closest( "li" ),
|
||
clickedIsActive = tab[ 0 ] === active[ 0 ],
|
||
collapsing = clickedIsActive && options.collapsible,
|
||
toShow = collapsing ? $() : this._getPanelForTab( tab ),
|
||
toHide = !active.length ? $() : this._getPanelForTab( active ),
|
||
eventData = {
|
||
oldTab: active,
|
||
oldPanel: toHide,
|
||
newTab: collapsing ? $() : tab,
|
||
newPanel: toShow
|
||
};
|
||
|
||
event.preventDefault();
|
||
|
||
if ( tab.hasClass( "ui-state-disabled" ) ||
|
||
|
||
// tab is already loading
|
||
tab.hasClass( "ui-tabs-loading" ) ||
|
||
|
||
// can't switch durning an animation
|
||
this.running ||
|
||
|
||
// click on active header, but not collapsible
|
||
( clickedIsActive && !options.collapsible ) ||
|
||
|
||
// allow canceling activation
|
||
( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
|
||
return;
|
||
}
|
||
|
||
options.active = collapsing ? false : this.tabs.index( tab );
|
||
|
||
this.active = clickedIsActive ? $() : tab;
|
||
if ( this.xhr ) {
|
||
this.xhr.abort();
|
||
}
|
||
|
||
if ( !toHide.length && !toShow.length ) {
|
||
$.error( "jQuery UI Tabs: Mismatching fragment identifier." );
|
||
}
|
||
|
||
if ( toShow.length ) {
|
||
this.load( this.tabs.index( tab ), event );
|
||
}
|
||
this._toggle( event, eventData );
|
||
},
|
||
|
||
// Handles show/hide for selecting tabs
|
||
_toggle: function( event, eventData ) {
|
||
var that = this,
|
||
toShow = eventData.newPanel,
|
||
toHide = eventData.oldPanel;
|
||
|
||
this.running = true;
|
||
|
||
function complete() {
|
||
that.running = false;
|
||
that._trigger( "activate", event, eventData );
|
||
}
|
||
|
||
function show() {
|
||
that._addClass( eventData.newTab.closest( "li" ), "ui-tabs-active", "ui-state-active" );
|
||
|
||
if ( toShow.length && that.options.show ) {
|
||
that._show( toShow, that.options.show, complete );
|
||
} else {
|
||
toShow.show();
|
||
complete();
|
||
}
|
||
}
|
||
|
||
// Start out by hiding, then showing, then completing
|
||
if ( toHide.length && this.options.hide ) {
|
||
this._hide( toHide, this.options.hide, function() {
|
||
that._removeClass( eventData.oldTab.closest( "li" ),
|
||
"ui-tabs-active", "ui-state-active" );
|
||
show();
|
||
} );
|
||
} else {
|
||
this._removeClass( eventData.oldTab.closest( "li" ),
|
||
"ui-tabs-active", "ui-state-active" );
|
||
toHide.hide();
|
||
show();
|
||
}
|
||
|
||
toHide.attr( "aria-hidden", "true" );
|
||
eventData.oldTab.attr( {
|
||
"aria-selected": "false",
|
||
"aria-expanded": "false"
|
||
} );
|
||
|
||
// If we're switching tabs, remove the old tab from the tab order.
|
||
// If we're opening from collapsed state, remove the previous tab from the tab order.
|
||
// If we're collapsing, then keep the collapsing tab in the tab order.
|
||
if ( toShow.length && toHide.length ) {
|
||
eventData.oldTab.attr( "tabIndex", -1 );
|
||
} else if ( toShow.length ) {
|
||
this.tabs.filter( function() {
|
||
return $( this ).attr( "tabIndex" ) === 0;
|
||
} )
|
||
.attr( "tabIndex", -1 );
|
||
}
|
||
|
||
toShow.attr( "aria-hidden", "false" );
|
||
eventData.newTab.attr( {
|
||
"aria-selected": "true",
|
||
"aria-expanded": "true",
|
||
tabIndex: 0
|
||
} );
|
||
},
|
||
|
||
_activate: function( index ) {
|
||
var anchor,
|
||
active = this._findActive( index );
|
||
|
||
// Trying to activate the already active panel
|
||
if ( active[ 0 ] === this.active[ 0 ] ) {
|
||
return;
|
||
}
|
||
|
||
// Trying to collapse, simulate a click on the current active header
|
||
if ( !active.length ) {
|
||
active = this.active;
|
||
}
|
||
|
||
anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
|
||
this._eventHandler( {
|
||
target: anchor,
|
||
currentTarget: anchor,
|
||
preventDefault: $.noop
|
||
} );
|
||
},
|
||
|
||
_findActive: function( index ) {
|
||
return index === false ? $() : this.tabs.eq( index );
|
||
},
|
||
|
||
_getIndex: function( index ) {
|
||
|
||
// meta-function to give users option to provide a href string instead of a numerical index.
|
||
if ( typeof index === "string" ) {
|
||
index = this.anchors.index( this.anchors.filter( "[href$='" +
|
||
$.ui.escapeSelector( index ) + "']" ) );
|
||
}
|
||
|
||
return index;
|
||
},
|
||
|
||
_destroy: function() {
|
||
if ( this.xhr ) {
|
||
this.xhr.abort();
|
||
}
|
||
|
||
this.tablist
|
||
.removeAttr( "role" )
|
||
.off( this.eventNamespace );
|
||
|
||
this.anchors
|
||
.removeAttr( "role tabIndex" )
|
||
.removeUniqueId();
|
||
|
||
this.tabs.add( this.panels ).each( function() {
|
||
if ( $.data( this, "ui-tabs-destroy" ) ) {
|
||
$( this ).remove();
|
||
} else {
|
||
$( this ).removeAttr( "role tabIndex " +
|
||
"aria-live aria-busy aria-selected aria-labelledby aria-hidden aria-expanded" );
|
||
}
|
||
} );
|
||
|
||
this.tabs.each( function() {
|
||
var li = $( this ),
|
||
prev = li.data( "ui-tabs-aria-controls" );
|
||
if ( prev ) {
|
||
li
|
||
.attr( "aria-controls", prev )
|
||
.removeData( "ui-tabs-aria-controls" );
|
||
} else {
|
||
li.removeAttr( "aria-controls" );
|
||
}
|
||
} );
|
||
|
||
this.panels.show();
|
||
|
||
if ( this.options.heightStyle !== "content" ) {
|
||
this.panels.css( "height", "" );
|
||
}
|
||
},
|
||
|
||
enable: function( index ) {
|
||
var disabled = this.options.disabled;
|
||
if ( disabled === false ) {
|
||
return;
|
||
}
|
||
|
||
if ( index === undefined ) {
|
||
disabled = false;
|
||
} else {
|
||
index = this._getIndex( index );
|
||
if ( $.isArray( disabled ) ) {
|
||
disabled = $.map( disabled, function( num ) {
|
||
return num !== index ? num : null;
|
||
} );
|
||
} else {
|
||
disabled = $.map( this.tabs, function( li, num ) {
|
||
return num !== index ? num : null;
|
||
} );
|
||
}
|
||
}
|
||
this._setOptionDisabled( disabled );
|
||
},
|
||
|
||
disable: function( index ) {
|
||
var disabled = this.options.disabled;
|
||
if ( disabled === true ) {
|
||
return;
|
||
}
|
||
|
||
if ( index === undefined ) {
|
||
disabled = true;
|
||
} else {
|
||
index = this._getIndex( index );
|
||
if ( $.inArray( index, disabled ) !== -1 ) {
|
||
return;
|
||
}
|
||
if ( $.isArray( disabled ) ) {
|
||
disabled = $.merge( [ index ], disabled ).sort();
|
||
} else {
|
||
disabled = [ index ];
|
||
}
|
||
}
|
||
this._setOptionDisabled( disabled );
|
||
},
|
||
|
||
load: function( index, event ) {
|
||
index = this._getIndex( index );
|
||
var that = this,
|
||
tab = this.tabs.eq( index ),
|
||
anchor = tab.find( ".ui-tabs-anchor" ),
|
||
panel = this._getPanelForTab( tab ),
|
||
eventData = {
|
||
tab: tab,
|
||
panel: panel
|
||
},
|
||
complete = function( jqXHR, status ) {
|
||
if ( status === "abort" ) {
|
||
that.panels.stop( false, true );
|
||
}
|
||
|
||
that._removeClass( tab, "ui-tabs-loading" );
|
||
panel.removeAttr( "aria-busy" );
|
||
|
||
if ( jqXHR === that.xhr ) {
|
||
delete that.xhr;
|
||
}
|
||
};
|
||
|
||
// Not remote
|
||
if ( this._isLocal( anchor[ 0 ] ) ) {
|
||
return;
|
||
}
|
||
|
||
this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
|
||
|
||
// Support: jQuery <1.8
|
||
// jQuery <1.8 returns false if the request is canceled in beforeSend,
|
||
// but as of 1.8, $.ajax() always returns a jqXHR object.
|
||
if ( this.xhr && this.xhr.statusText !== "canceled" ) {
|
||
this._addClass( tab, "ui-tabs-loading" );
|
||
panel.attr( "aria-busy", "true" );
|
||
|
||
this.xhr
|
||
.done( function( response, status, jqXHR ) {
|
||
|
||
// support: jQuery <1.8
|
||
// http://bugs.jquery.com/ticket/11778
|
||
setTimeout( function() {
|
||
panel.html( response );
|
||
that._trigger( "load", event, eventData );
|
||
|
||
complete( jqXHR, status );
|
||
}, 1 );
|
||
} )
|
||
.fail( function( jqXHR, status ) {
|
||
|
||
// support: jQuery <1.8
|
||
// http://bugs.jquery.com/ticket/11778
|
||
setTimeout( function() {
|
||
complete( jqXHR, status );
|
||
}, 1 );
|
||
} );
|
||
}
|
||
},
|
||
|
||
_ajaxSettings: function( anchor, event, eventData ) {
|
||
var that = this;
|
||
return {
|
||
|
||
// Support: IE <11 only
|
||
// Strip any hash that exists to prevent errors with the Ajax request
|
||
url: anchor.attr( "href" ).replace( /#.*$/, "" ),
|
||
beforeSend: function( jqXHR, settings ) {
|
||
return that._trigger( "beforeLoad", event,
|
||
$.extend( { jqXHR: jqXHR, ajaxSettings: settings }, eventData ) );
|
||
}
|
||
};
|
||
},
|
||
|
||
_getPanelForTab: function( tab ) {
|
||
var id = $( tab ).attr( "aria-controls" );
|
||
return this.element.find( this._sanitizeSelector( "#" + id ) );
|
||
}
|
||
} );
|
||
|
||
// DEPRECATED
|
||
// TODO: Switch return back to widget declaration at top of file when this is removed
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Backcompat for ui-tab class (now ui-tabs-tab)
|
||
$.widget( "ui.tabs", $.ui.tabs, {
|
||
_processTabs: function() {
|
||
this._superApply( arguments );
|
||
this._addClass( this.tabs, "ui-tab" );
|
||
}
|
||
} );
|
||
}
|
||
|
||
var widgetsTabs = $.ui.tabs;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Tooltip 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Tooltip
|
||
//>>group: Widgets
|
||
//>>description: Shows additional information for any element on hover or focus.
|
||
//>>docs: http://api.jqueryui.com/tooltip/
|
||
//>>demos: http://jqueryui.com/tooltip/
|
||
//>>css.structure: ../../themes/base/core.css
|
||
//>>css.structure: ../../themes/base/tooltip.css
|
||
//>>css.theme: ../../themes/base/theme.css
|
||
|
||
|
||
|
||
$.widget( "ui.tooltip", {
|
||
version: "1.12.1",
|
||
options: {
|
||
classes: {
|
||
"ui-tooltip": "ui-corner-all ui-widget-shadow"
|
||
},
|
||
content: function() {
|
||
|
||
// support: IE<9, Opera in jQuery <1.7
|
||
// .text() can't accept undefined, so coerce to a string
|
||
var title = $( this ).attr( "title" ) || "";
|
||
|
||
// Escape title, since we're going from an attribute to raw HTML
|
||
return $( "<a>" ).text( title ).html();
|
||
},
|
||
hide: true,
|
||
|
||
// Disabled elements have inconsistent behavior across browsers (#8661)
|
||
items: "[title]:not([disabled])",
|
||
position: {
|
||
my: "left top+15",
|
||
at: "left bottom",
|
||
collision: "flipfit flip"
|
||
},
|
||
show: true,
|
||
track: false,
|
||
|
||
// Callbacks
|
||
close: null,
|
||
open: null
|
||
},
|
||
|
||
_addDescribedBy: function( elem, id ) {
|
||
var describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ );
|
||
describedby.push( id );
|
||
elem
|
||
.data( "ui-tooltip-id", id )
|
||
.attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
|
||
},
|
||
|
||
_removeDescribedBy: function( elem ) {
|
||
var id = elem.data( "ui-tooltip-id" ),
|
||
describedby = ( elem.attr( "aria-describedby" ) || "" ).split( /\s+/ ),
|
||
index = $.inArray( id, describedby );
|
||
|
||
if ( index !== -1 ) {
|
||
describedby.splice( index, 1 );
|
||
}
|
||
|
||
elem.removeData( "ui-tooltip-id" );
|
||
describedby = $.trim( describedby.join( " " ) );
|
||
if ( describedby ) {
|
||
elem.attr( "aria-describedby", describedby );
|
||
} else {
|
||
elem.removeAttr( "aria-describedby" );
|
||
}
|
||
},
|
||
|
||
_create: function() {
|
||
this._on( {
|
||
mouseover: "open",
|
||
focusin: "open"
|
||
} );
|
||
|
||
// IDs of generated tooltips, needed for destroy
|
||
this.tooltips = {};
|
||
|
||
// IDs of parent tooltips where we removed the title attribute
|
||
this.parents = {};
|
||
|
||
// Append the aria-live region so tooltips announce correctly
|
||
this.liveRegion = $( "<div>" )
|
||
.attr( {
|
||
role: "log",
|
||
"aria-live": "assertive",
|
||
"aria-relevant": "additions"
|
||
} )
|
||
.appendTo( this.document[ 0 ].body );
|
||
this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" );
|
||
|
||
this.disabledTitles = $( [] );
|
||
},
|
||
|
||
_setOption: function( key, value ) {
|
||
var that = this;
|
||
|
||
this._super( key, value );
|
||
|
||
if ( key === "content" ) {
|
||
$.each( this.tooltips, function( id, tooltipData ) {
|
||
that._updateContent( tooltipData.element );
|
||
} );
|
||
}
|
||
},
|
||
|
||
_setOptionDisabled: function( value ) {
|
||
this[ value ? "_disable" : "_enable" ]();
|
||
},
|
||
|
||
_disable: function() {
|
||
var that = this;
|
||
|
||
// Close open tooltips
|
||
$.each( this.tooltips, function( id, tooltipData ) {
|
||
var event = $.Event( "blur" );
|
||
event.target = event.currentTarget = tooltipData.element[ 0 ];
|
||
that.close( event, true );
|
||
} );
|
||
|
||
// Remove title attributes to prevent native tooltips
|
||
this.disabledTitles = this.disabledTitles.add(
|
||
this.element.find( this.options.items ).addBack()
|
||
.filter( function() {
|
||
var element = $( this );
|
||
if ( element.is( "[title]" ) ) {
|
||
return element
|
||
.data( "ui-tooltip-title", element.attr( "title" ) )
|
||
.removeAttr( "title" );
|
||
}
|
||
} )
|
||
);
|
||
},
|
||
|
||
_enable: function() {
|
||
|
||
// restore title attributes
|
||
this.disabledTitles.each( function() {
|
||
var element = $( this );
|
||
if ( element.data( "ui-tooltip-title" ) ) {
|
||
element.attr( "title", element.data( "ui-tooltip-title" ) );
|
||
}
|
||
} );
|
||
this.disabledTitles = $( [] );
|
||
},
|
||
|
||
open: function( event ) {
|
||
var that = this,
|
||
target = $( event ? event.target : this.element )
|
||
|
||
// we need closest here due to mouseover bubbling,
|
||
// but always pointing at the same event target
|
||
.closest( this.options.items );
|
||
|
||
// No element to show a tooltip for or the tooltip is already open
|
||
if ( !target.length || target.data( "ui-tooltip-id" ) ) {
|
||
return;
|
||
}
|
||
|
||
if ( target.attr( "title" ) ) {
|
||
target.data( "ui-tooltip-title", target.attr( "title" ) );
|
||
}
|
||
|
||
target.data( "ui-tooltip-open", true );
|
||
|
||
// Kill parent tooltips, custom or native, for hover
|
||
if ( event && event.type === "mouseover" ) {
|
||
target.parents().each( function() {
|
||
var parent = $( this ),
|
||
blurEvent;
|
||
if ( parent.data( "ui-tooltip-open" ) ) {
|
||
blurEvent = $.Event( "blur" );
|
||
blurEvent.target = blurEvent.currentTarget = this;
|
||
that.close( blurEvent, true );
|
||
}
|
||
if ( parent.attr( "title" ) ) {
|
||
parent.uniqueId();
|
||
that.parents[ this.id ] = {
|
||
element: this,
|
||
title: parent.attr( "title" )
|
||
};
|
||
parent.attr( "title", "" );
|
||
}
|
||
} );
|
||
}
|
||
|
||
this._registerCloseHandlers( event, target );
|
||
this._updateContent( target, event );
|
||
},
|
||
|
||
_updateContent: function( target, event ) {
|
||
var content,
|
||
contentOption = this.options.content,
|
||
that = this,
|
||
eventType = event ? event.type : null;
|
||
|
||
if ( typeof contentOption === "string" || contentOption.nodeType ||
|
||
contentOption.jquery ) {
|
||
return this._open( event, target, contentOption );
|
||
}
|
||
|
||
content = contentOption.call( target[ 0 ], function( response ) {
|
||
|
||
// IE may instantly serve a cached response for ajax requests
|
||
// delay this call to _open so the other call to _open runs first
|
||
that._delay( function() {
|
||
|
||
// Ignore async response if tooltip was closed already
|
||
if ( !target.data( "ui-tooltip-open" ) ) {
|
||
return;
|
||
}
|
||
|
||
// JQuery creates a special event for focusin when it doesn't
|
||
// exist natively. To improve performance, the native event
|
||
// object is reused and the type is changed. Therefore, we can't
|
||
// rely on the type being correct after the event finished
|
||
// bubbling, so we set it back to the previous value. (#8740)
|
||
if ( event ) {
|
||
event.type = eventType;
|
||
}
|
||
this._open( event, target, response );
|
||
} );
|
||
} );
|
||
if ( content ) {
|
||
this._open( event, target, content );
|
||
}
|
||
},
|
||
|
||
_open: function( event, target, content ) {
|
||
var tooltipData, tooltip, delayedShow, a11yContent,
|
||
positionOption = $.extend( {}, this.options.position );
|
||
|
||
if ( !content ) {
|
||
return;
|
||
}
|
||
|
||
// Content can be updated multiple times. If the tooltip already
|
||
// exists, then just update the content and bail.
|
||
tooltipData = this._find( target );
|
||
if ( tooltipData ) {
|
||
tooltipData.tooltip.find( ".ui-tooltip-content" ).html( content );
|
||
return;
|
||
}
|
||
|
||
// If we have a title, clear it to prevent the native tooltip
|
||
// we have to check first to avoid defining a title if none exists
|
||
// (we don't want to cause an element to start matching [title])
|
||
//
|
||
// We use removeAttr only for key events, to allow IE to export the correct
|
||
// accessible attributes. For mouse events, set to empty string to avoid
|
||
// native tooltip showing up (happens only when removing inside mouseover).
|
||
if ( target.is( "[title]" ) ) {
|
||
if ( event && event.type === "mouseover" ) {
|
||
target.attr( "title", "" );
|
||
} else {
|
||
target.removeAttr( "title" );
|
||
}
|
||
}
|
||
|
||
tooltipData = this._tooltip( target );
|
||
tooltip = tooltipData.tooltip;
|
||
this._addDescribedBy( target, tooltip.attr( "id" ) );
|
||
tooltip.find( ".ui-tooltip-content" ).html( content );
|
||
|
||
// Support: Voiceover on OS X, JAWS on IE <= 9
|
||
// JAWS announces deletions even when aria-relevant="additions"
|
||
// Voiceover will sometimes re-read the entire log region's contents from the beginning
|
||
this.liveRegion.children().hide();
|
||
a11yContent = $( "<div>" ).html( tooltip.find( ".ui-tooltip-content" ).html() );
|
||
a11yContent.removeAttr( "name" ).find( "[name]" ).removeAttr( "name" );
|
||
a11yContent.removeAttr( "id" ).find( "[id]" ).removeAttr( "id" );
|
||
a11yContent.appendTo( this.liveRegion );
|
||
|
||
function position( event ) {
|
||
positionOption.of = event;
|
||
if ( tooltip.is( ":hidden" ) ) {
|
||
return;
|
||
}
|
||
tooltip.position( positionOption );
|
||
}
|
||
if ( this.options.track && event && /^mouse/.test( event.type ) ) {
|
||
this._on( this.document, {
|
||
mousemove: position
|
||
} );
|
||
|
||
// trigger once to override element-relative positioning
|
||
position( event );
|
||
} else {
|
||
tooltip.position( $.extend( {
|
||
of: target
|
||
}, this.options.position ) );
|
||
}
|
||
|
||
tooltip.hide();
|
||
|
||
this._show( tooltip, this.options.show );
|
||
|
||
// Handle tracking tooltips that are shown with a delay (#8644). As soon
|
||
// as the tooltip is visible, position the tooltip using the most recent
|
||
// event.
|
||
// Adds the check to add the timers only when both delay and track options are set (#14682)
|
||
if ( this.options.track && this.options.show && this.options.show.delay ) {
|
||
delayedShow = this.delayedShow = setInterval( function() {
|
||
if ( tooltip.is( ":visible" ) ) {
|
||
position( positionOption.of );
|
||
clearInterval( delayedShow );
|
||
}
|
||
}, $.fx.interval );
|
||
}
|
||
|
||
this._trigger( "open", event, { tooltip: tooltip } );
|
||
},
|
||
|
||
_registerCloseHandlers: function( event, target ) {
|
||
var events = {
|
||
keyup: function( event ) {
|
||
if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
|
||
var fakeEvent = $.Event( event );
|
||
fakeEvent.currentTarget = target[ 0 ];
|
||
this.close( fakeEvent, true );
|
||
}
|
||
}
|
||
};
|
||
|
||
// Only bind remove handler for delegated targets. Non-delegated
|
||
// tooltips will handle this in destroy.
|
||
if ( target[ 0 ] !== this.element[ 0 ] ) {
|
||
events.remove = function() {
|
||
this._removeTooltip( this._find( target ).tooltip );
|
||
};
|
||
}
|
||
|
||
if ( !event || event.type === "mouseover" ) {
|
||
events.mouseleave = "close";
|
||
}
|
||
if ( !event || event.type === "focusin" ) {
|
||
events.focusout = "close";
|
||
}
|
||
this._on( true, target, events );
|
||
},
|
||
|
||
close: function( event ) {
|
||
var tooltip,
|
||
that = this,
|
||
target = $( event ? event.currentTarget : this.element ),
|
||
tooltipData = this._find( target );
|
||
|
||
// The tooltip may already be closed
|
||
if ( !tooltipData ) {
|
||
|
||
// We set ui-tooltip-open immediately upon open (in open()), but only set the
|
||
// additional data once there's actually content to show (in _open()). So even if the
|
||
// tooltip doesn't have full data, we always remove ui-tooltip-open in case we're in
|
||
// the period between open() and _open().
|
||
target.removeData( "ui-tooltip-open" );
|
||
return;
|
||
}
|
||
|
||
tooltip = tooltipData.tooltip;
|
||
|
||
// Disabling closes the tooltip, so we need to track when we're closing
|
||
// to avoid an infinite loop in case the tooltip becomes disabled on close
|
||
if ( tooltipData.closing ) {
|
||
return;
|
||
}
|
||
|
||
// Clear the interval for delayed tracking tooltips
|
||
clearInterval( this.delayedShow );
|
||
|
||
// Only set title if we had one before (see comment in _open())
|
||
// If the title attribute has changed since open(), don't restore
|
||
if ( target.data( "ui-tooltip-title" ) && !target.attr( "title" ) ) {
|
||
target.attr( "title", target.data( "ui-tooltip-title" ) );
|
||
}
|
||
|
||
this._removeDescribedBy( target );
|
||
|
||
tooltipData.hiding = true;
|
||
tooltip.stop( true );
|
||
this._hide( tooltip, this.options.hide, function() {
|
||
that._removeTooltip( $( this ) );
|
||
} );
|
||
|
||
target.removeData( "ui-tooltip-open" );
|
||
this._off( target, "mouseleave focusout keyup" );
|
||
|
||
// Remove 'remove' binding only on delegated targets
|
||
if ( target[ 0 ] !== this.element[ 0 ] ) {
|
||
this._off( target, "remove" );
|
||
}
|
||
this._off( this.document, "mousemove" );
|
||
|
||
if ( event && event.type === "mouseleave" ) {
|
||
$.each( this.parents, function( id, parent ) {
|
||
$( parent.element ).attr( "title", parent.title );
|
||
delete that.parents[ id ];
|
||
} );
|
||
}
|
||
|
||
tooltipData.closing = true;
|
||
this._trigger( "close", event, { tooltip: tooltip } );
|
||
if ( !tooltipData.hiding ) {
|
||
tooltipData.closing = false;
|
||
}
|
||
},
|
||
|
||
_tooltip: function( element ) {
|
||
var tooltip = $( "<div>" ).attr( "role", "tooltip" ),
|
||
content = $( "<div>" ).appendTo( tooltip ),
|
||
id = tooltip.uniqueId().attr( "id" );
|
||
|
||
this._addClass( content, "ui-tooltip-content" );
|
||
this._addClass( tooltip, "ui-tooltip", "ui-widget ui-widget-content" );
|
||
|
||
tooltip.appendTo( this._appendTo( element ) );
|
||
|
||
return this.tooltips[ id ] = {
|
||
element: element,
|
||
tooltip: tooltip
|
||
};
|
||
},
|
||
|
||
_find: function( target ) {
|
||
var id = target.data( "ui-tooltip-id" );
|
||
return id ? this.tooltips[ id ] : null;
|
||
},
|
||
|
||
_removeTooltip: function( tooltip ) {
|
||
tooltip.remove();
|
||
delete this.tooltips[ tooltip.attr( "id" ) ];
|
||
},
|
||
|
||
_appendTo: function( target ) {
|
||
var element = target.closest( ".ui-front, dialog" );
|
||
|
||
if ( !element.length ) {
|
||
element = this.document[ 0 ].body;
|
||
}
|
||
|
||
return element;
|
||
},
|
||
|
||
_destroy: function() {
|
||
var that = this;
|
||
|
||
// Close open tooltips
|
||
$.each( this.tooltips, function( id, tooltipData ) {
|
||
|
||
// Delegate to close method to handle common cleanup
|
||
var event = $.Event( "blur" ),
|
||
element = tooltipData.element;
|
||
event.target = event.currentTarget = element[ 0 ];
|
||
that.close( event, true );
|
||
|
||
// Remove immediately; destroying an open tooltip doesn't use the
|
||
// hide animation
|
||
$( "#" + id ).remove();
|
||
|
||
// Restore the title
|
||
if ( element.data( "ui-tooltip-title" ) ) {
|
||
|
||
// If the title attribute has changed since open(), don't restore
|
||
if ( !element.attr( "title" ) ) {
|
||
element.attr( "title", element.data( "ui-tooltip-title" ) );
|
||
}
|
||
element.removeData( "ui-tooltip-title" );
|
||
}
|
||
} );
|
||
this.liveRegion.remove();
|
||
}
|
||
} );
|
||
|
||
// DEPRECATED
|
||
// TODO: Switch return back to widget declaration at top of file when this is removed
|
||
if ( $.uiBackCompat !== false ) {
|
||
|
||
// Backcompat for tooltipClass option
|
||
$.widget( "ui.tooltip", $.ui.tooltip, {
|
||
options: {
|
||
tooltipClass: null
|
||
},
|
||
_tooltip: function() {
|
||
var tooltipData = this._superApply( arguments );
|
||
if ( this.options.tooltipClass ) {
|
||
tooltipData.tooltip.addClass( this.options.tooltipClass );
|
||
}
|
||
return tooltipData;
|
||
}
|
||
} );
|
||
}
|
||
|
||
var widgetsTooltip = $.ui.tooltip;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Effects Core
|
||
//>>group: Effects
|
||
// jscs:disable maximumLineLength
|
||
//>>description: Extends the internal jQuery effects. Includes morphing and easing. Required by all other effects.
|
||
// jscs:enable maximumLineLength
|
||
//>>docs: http://api.jqueryui.com/category/effects-core/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var dataSpace = "ui-effects-",
|
||
dataSpaceStyle = "ui-effects-style",
|
||
dataSpaceAnimated = "ui-effects-animated",
|
||
|
||
// Create a local jQuery because jQuery Color relies on it and the
|
||
// global may not exist with AMD and a custom build (#10199)
|
||
jQuery = $;
|
||
|
||
$.effects = {
|
||
effect: {}
|
||
};
|
||
|
||
/*!
|
||
* jQuery Color Animations v2.1.2
|
||
* https://github.com/jquery/jquery-color
|
||
*
|
||
* Copyright 2014 jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*
|
||
* Date: Wed Jan 16 08:47:09 2013 -0600
|
||
*/
|
||
( function( jQuery, undefined ) {
|
||
|
||
var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor " +
|
||
"borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
|
||
|
||
// Plusequals test for += 100 -= 100
|
||
rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
|
||
|
||
// A set of RE's that can match strings and generate color tuples.
|
||
stringParsers = [ {
|
||
re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
|
||
parse: function( execResult ) {
|
||
return [
|
||
execResult[ 1 ],
|
||
execResult[ 2 ],
|
||
execResult[ 3 ],
|
||
execResult[ 4 ]
|
||
];
|
||
}
|
||
}, {
|
||
re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
|
||
parse: function( execResult ) {
|
||
return [
|
||
execResult[ 1 ] * 2.55,
|
||
execResult[ 2 ] * 2.55,
|
||
execResult[ 3 ] * 2.55,
|
||
execResult[ 4 ]
|
||
];
|
||
}
|
||
}, {
|
||
|
||
// This regex ignores A-F because it's compared against an already lowercased string
|
||
re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
|
||
parse: function( execResult ) {
|
||
return [
|
||
parseInt( execResult[ 1 ], 16 ),
|
||
parseInt( execResult[ 2 ], 16 ),
|
||
parseInt( execResult[ 3 ], 16 )
|
||
];
|
||
}
|
||
}, {
|
||
|
||
// This regex ignores A-F because it's compared against an already lowercased string
|
||
re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
|
||
parse: function( execResult ) {
|
||
return [
|
||
parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
|
||
parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
|
||
parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
|
||
];
|
||
}
|
||
}, {
|
||
re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
|
||
space: "hsla",
|
||
parse: function( execResult ) {
|
||
return [
|
||
execResult[ 1 ],
|
||
execResult[ 2 ] / 100,
|
||
execResult[ 3 ] / 100,
|
||
execResult[ 4 ]
|
||
];
|
||
}
|
||
} ],
|
||
|
||
// JQuery.Color( )
|
||
color = jQuery.Color = function( color, green, blue, alpha ) {
|
||
return new jQuery.Color.fn.parse( color, green, blue, alpha );
|
||
},
|
||
spaces = {
|
||
rgba: {
|
||
props: {
|
||
red: {
|
||
idx: 0,
|
||
type: "byte"
|
||
},
|
||
green: {
|
||
idx: 1,
|
||
type: "byte"
|
||
},
|
||
blue: {
|
||
idx: 2,
|
||
type: "byte"
|
||
}
|
||
}
|
||
},
|
||
|
||
hsla: {
|
||
props: {
|
||
hue: {
|
||
idx: 0,
|
||
type: "degrees"
|
||
},
|
||
saturation: {
|
||
idx: 1,
|
||
type: "percent"
|
||
},
|
||
lightness: {
|
||
idx: 2,
|
||
type: "percent"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
propTypes = {
|
||
"byte": {
|
||
floor: true,
|
||
max: 255
|
||
},
|
||
"percent": {
|
||
max: 1
|
||
},
|
||
"degrees": {
|
||
mod: 360,
|
||
floor: true
|
||
}
|
||
},
|
||
support = color.support = {},
|
||
|
||
// Element for support tests
|
||
supportElem = jQuery( "<p>" )[ 0 ],
|
||
|
||
// Colors = jQuery.Color.names
|
||
colors,
|
||
|
||
// Local aliases of functions called often
|
||
each = jQuery.each;
|
||
|
||
// Determine rgba support immediately
|
||
supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
|
||
support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
|
||
|
||
// Define cache name and alpha properties
|
||
// for rgba and hsla spaces
|
||
each( spaces, function( spaceName, space ) {
|
||
space.cache = "_" + spaceName;
|
||
space.props.alpha = {
|
||
idx: 3,
|
||
type: "percent",
|
||
def: 1
|
||
};
|
||
} );
|
||
|
||
function clamp( value, prop, allowEmpty ) {
|
||
var type = propTypes[ prop.type ] || {};
|
||
|
||
if ( value == null ) {
|
||
return ( allowEmpty || !prop.def ) ? null : prop.def;
|
||
}
|
||
|
||
// ~~ is an short way of doing floor for positive numbers
|
||
value = type.floor ? ~~value : parseFloat( value );
|
||
|
||
// IE will pass in empty strings as value for alpha,
|
||
// which will hit this case
|
||
if ( isNaN( value ) ) {
|
||
return prop.def;
|
||
}
|
||
|
||
if ( type.mod ) {
|
||
|
||
// We add mod before modding to make sure that negatives values
|
||
// get converted properly: -10 -> 350
|
||
return ( value + type.mod ) % type.mod;
|
||
}
|
||
|
||
// For now all property types without mod have min and max
|
||
return 0 > value ? 0 : type.max < value ? type.max : value;
|
||
}
|
||
|
||
function stringParse( string ) {
|
||
var inst = color(),
|
||
rgba = inst._rgba = [];
|
||
|
||
string = string.toLowerCase();
|
||
|
||
each( stringParsers, function( i, parser ) {
|
||
var parsed,
|
||
match = parser.re.exec( string ),
|
||
values = match && parser.parse( match ),
|
||
spaceName = parser.space || "rgba";
|
||
|
||
if ( values ) {
|
||
parsed = inst[ spaceName ]( values );
|
||
|
||
// If this was an rgba parse the assignment might happen twice
|
||
// oh well....
|
||
inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
|
||
rgba = inst._rgba = parsed._rgba;
|
||
|
||
// Exit each( stringParsers ) here because we matched
|
||
return false;
|
||
}
|
||
} );
|
||
|
||
// Found a stringParser that handled it
|
||
if ( rgba.length ) {
|
||
|
||
// If this came from a parsed string, force "transparent" when alpha is 0
|
||
// chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
|
||
if ( rgba.join() === "0,0,0,0" ) {
|
||
jQuery.extend( rgba, colors.transparent );
|
||
}
|
||
return inst;
|
||
}
|
||
|
||
// Named colors
|
||
return colors[ string ];
|
||
}
|
||
|
||
color.fn = jQuery.extend( color.prototype, {
|
||
parse: function( red, green, blue, alpha ) {
|
||
if ( red === undefined ) {
|
||
this._rgba = [ null, null, null, null ];
|
||
return this;
|
||
}
|
||
if ( red.jquery || red.nodeType ) {
|
||
red = jQuery( red ).css( green );
|
||
green = undefined;
|
||
}
|
||
|
||
var inst = this,
|
||
type = jQuery.type( red ),
|
||
rgba = this._rgba = [];
|
||
|
||
// More than 1 argument specified - assume ( red, green, blue, alpha )
|
||
if ( green !== undefined ) {
|
||
red = [ red, green, blue, alpha ];
|
||
type = "array";
|
||
}
|
||
|
||
if ( type === "string" ) {
|
||
return this.parse( stringParse( red ) || colors._default );
|
||
}
|
||
|
||
if ( type === "array" ) {
|
||
each( spaces.rgba.props, function( key, prop ) {
|
||
rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
|
||
} );
|
||
return this;
|
||
}
|
||
|
||
if ( type === "object" ) {
|
||
if ( red instanceof color ) {
|
||
each( spaces, function( spaceName, space ) {
|
||
if ( red[ space.cache ] ) {
|
||
inst[ space.cache ] = red[ space.cache ].slice();
|
||
}
|
||
} );
|
||
} else {
|
||
each( spaces, function( spaceName, space ) {
|
||
var cache = space.cache;
|
||
each( space.props, function( key, prop ) {
|
||
|
||
// If the cache doesn't exist, and we know how to convert
|
||
if ( !inst[ cache ] && space.to ) {
|
||
|
||
// If the value was null, we don't need to copy it
|
||
// if the key was alpha, we don't need to copy it either
|
||
if ( key === "alpha" || red[ key ] == null ) {
|
||
return;
|
||
}
|
||
inst[ cache ] = space.to( inst._rgba );
|
||
}
|
||
|
||
// This is the only case where we allow nulls for ALL properties.
|
||
// call clamp with alwaysAllowEmpty
|
||
inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
|
||
} );
|
||
|
||
// Everything defined but alpha?
|
||
if ( inst[ cache ] &&
|
||
jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
|
||
|
||
// Use the default of 1
|
||
inst[ cache ][ 3 ] = 1;
|
||
if ( space.from ) {
|
||
inst._rgba = space.from( inst[ cache ] );
|
||
}
|
||
}
|
||
} );
|
||
}
|
||
return this;
|
||
}
|
||
},
|
||
is: function( compare ) {
|
||
var is = color( compare ),
|
||
same = true,
|
||
inst = this;
|
||
|
||
each( spaces, function( _, space ) {
|
||
var localCache,
|
||
isCache = is[ space.cache ];
|
||
if ( isCache ) {
|
||
localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
|
||
each( space.props, function( _, prop ) {
|
||
if ( isCache[ prop.idx ] != null ) {
|
||
same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
|
||
return same;
|
||
}
|
||
} );
|
||
}
|
||
return same;
|
||
} );
|
||
return same;
|
||
},
|
||
_space: function() {
|
||
var used = [],
|
||
inst = this;
|
||
each( spaces, function( spaceName, space ) {
|
||
if ( inst[ space.cache ] ) {
|
||
used.push( spaceName );
|
||
}
|
||
} );
|
||
return used.pop();
|
||
},
|
||
transition: function( other, distance ) {
|
||
var end = color( other ),
|
||
spaceName = end._space(),
|
||
space = spaces[ spaceName ],
|
||
startColor = this.alpha() === 0 ? color( "transparent" ) : this,
|
||
start = startColor[ space.cache ] || space.to( startColor._rgba ),
|
||
result = start.slice();
|
||
|
||
end = end[ space.cache ];
|
||
each( space.props, function( key, prop ) {
|
||
var index = prop.idx,
|
||
startValue = start[ index ],
|
||
endValue = end[ index ],
|
||
type = propTypes[ prop.type ] || {};
|
||
|
||
// If null, don't override start value
|
||
if ( endValue === null ) {
|
||
return;
|
||
}
|
||
|
||
// If null - use end
|
||
if ( startValue === null ) {
|
||
result[ index ] = endValue;
|
||
} else {
|
||
if ( type.mod ) {
|
||
if ( endValue - startValue > type.mod / 2 ) {
|
||
startValue += type.mod;
|
||
} else if ( startValue - endValue > type.mod / 2 ) {
|
||
startValue -= type.mod;
|
||
}
|
||
}
|
||
result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
|
||
}
|
||
} );
|
||
return this[ spaceName ]( result );
|
||
},
|
||
blend: function( opaque ) {
|
||
|
||
// If we are already opaque - return ourself
|
||
if ( this._rgba[ 3 ] === 1 ) {
|
||
return this;
|
||
}
|
||
|
||
var rgb = this._rgba.slice(),
|
||
a = rgb.pop(),
|
||
blend = color( opaque )._rgba;
|
||
|
||
return color( jQuery.map( rgb, function( v, i ) {
|
||
return ( 1 - a ) * blend[ i ] + a * v;
|
||
} ) );
|
||
},
|
||
toRgbaString: function() {
|
||
var prefix = "rgba(",
|
||
rgba = jQuery.map( this._rgba, function( v, i ) {
|
||
return v == null ? ( i > 2 ? 1 : 0 ) : v;
|
||
} );
|
||
|
||
if ( rgba[ 3 ] === 1 ) {
|
||
rgba.pop();
|
||
prefix = "rgb(";
|
||
}
|
||
|
||
return prefix + rgba.join() + ")";
|
||
},
|
||
toHslaString: function() {
|
||
var prefix = "hsla(",
|
||
hsla = jQuery.map( this.hsla(), function( v, i ) {
|
||
if ( v == null ) {
|
||
v = i > 2 ? 1 : 0;
|
||
}
|
||
|
||
// Catch 1 and 2
|
||
if ( i && i < 3 ) {
|
||
v = Math.round( v * 100 ) + "%";
|
||
}
|
||
return v;
|
||
} );
|
||
|
||
if ( hsla[ 3 ] === 1 ) {
|
||
hsla.pop();
|
||
prefix = "hsl(";
|
||
}
|
||
return prefix + hsla.join() + ")";
|
||
},
|
||
toHexString: function( includeAlpha ) {
|
||
var rgba = this._rgba.slice(),
|
||
alpha = rgba.pop();
|
||
|
||
if ( includeAlpha ) {
|
||
rgba.push( ~~( alpha * 255 ) );
|
||
}
|
||
|
||
return "#" + jQuery.map( rgba, function( v ) {
|
||
|
||
// Default to 0 when nulls exist
|
||
v = ( v || 0 ).toString( 16 );
|
||
return v.length === 1 ? "0" + v : v;
|
||
} ).join( "" );
|
||
},
|
||
toString: function() {
|
||
return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
|
||
}
|
||
} );
|
||
color.fn.parse.prototype = color.fn;
|
||
|
||
// Hsla conversions adapted from:
|
||
// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
|
||
|
||
function hue2rgb( p, q, h ) {
|
||
h = ( h + 1 ) % 1;
|
||
if ( h * 6 < 1 ) {
|
||
return p + ( q - p ) * h * 6;
|
||
}
|
||
if ( h * 2 < 1 ) {
|
||
return q;
|
||
}
|
||
if ( h * 3 < 2 ) {
|
||
return p + ( q - p ) * ( ( 2 / 3 ) - h ) * 6;
|
||
}
|
||
return p;
|
||
}
|
||
|
||
spaces.hsla.to = function( rgba ) {
|
||
if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
|
||
return [ null, null, null, rgba[ 3 ] ];
|
||
}
|
||
var r = rgba[ 0 ] / 255,
|
||
g = rgba[ 1 ] / 255,
|
||
b = rgba[ 2 ] / 255,
|
||
a = rgba[ 3 ],
|
||
max = Math.max( r, g, b ),
|
||
min = Math.min( r, g, b ),
|
||
diff = max - min,
|
||
add = max + min,
|
||
l = add * 0.5,
|
||
h, s;
|
||
|
||
if ( min === max ) {
|
||
h = 0;
|
||
} else if ( r === max ) {
|
||
h = ( 60 * ( g - b ) / diff ) + 360;
|
||
} else if ( g === max ) {
|
||
h = ( 60 * ( b - r ) / diff ) + 120;
|
||
} else {
|
||
h = ( 60 * ( r - g ) / diff ) + 240;
|
||
}
|
||
|
||
// Chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
|
||
// otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
|
||
if ( diff === 0 ) {
|
||
s = 0;
|
||
} else if ( l <= 0.5 ) {
|
||
s = diff / add;
|
||
} else {
|
||
s = diff / ( 2 - add );
|
||
}
|
||
return [ Math.round( h ) % 360, s, l, a == null ? 1 : a ];
|
||
};
|
||
|
||
spaces.hsla.from = function( hsla ) {
|
||
if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
|
||
return [ null, null, null, hsla[ 3 ] ];
|
||
}
|
||
var h = hsla[ 0 ] / 360,
|
||
s = hsla[ 1 ],
|
||
l = hsla[ 2 ],
|
||
a = hsla[ 3 ],
|
||
q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
|
||
p = 2 * l - q;
|
||
|
||
return [
|
||
Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
|
||
Math.round( hue2rgb( p, q, h ) * 255 ),
|
||
Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
|
||
a
|
||
];
|
||
};
|
||
|
||
each( spaces, function( spaceName, space ) {
|
||
var props = space.props,
|
||
cache = space.cache,
|
||
to = space.to,
|
||
from = space.from;
|
||
|
||
// Makes rgba() and hsla()
|
||
color.fn[ spaceName ] = function( value ) {
|
||
|
||
// Generate a cache for this space if it doesn't exist
|
||
if ( to && !this[ cache ] ) {
|
||
this[ cache ] = to( this._rgba );
|
||
}
|
||
if ( value === undefined ) {
|
||
return this[ cache ].slice();
|
||
}
|
||
|
||
var ret,
|
||
type = jQuery.type( value ),
|
||
arr = ( type === "array" || type === "object" ) ? value : arguments,
|
||
local = this[ cache ].slice();
|
||
|
||
each( props, function( key, prop ) {
|
||
var val = arr[ type === "object" ? key : prop.idx ];
|
||
if ( val == null ) {
|
||
val = local[ prop.idx ];
|
||
}
|
||
local[ prop.idx ] = clamp( val, prop );
|
||
} );
|
||
|
||
if ( from ) {
|
||
ret = color( from( local ) );
|
||
ret[ cache ] = local;
|
||
return ret;
|
||
} else {
|
||
return color( local );
|
||
}
|
||
};
|
||
|
||
// Makes red() green() blue() alpha() hue() saturation() lightness()
|
||
each( props, function( key, prop ) {
|
||
|
||
// Alpha is included in more than one space
|
||
if ( color.fn[ key ] ) {
|
||
return;
|
||
}
|
||
color.fn[ key ] = function( value ) {
|
||
var vtype = jQuery.type( value ),
|
||
fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
|
||
local = this[ fn ](),
|
||
cur = local[ prop.idx ],
|
||
match;
|
||
|
||
if ( vtype === "undefined" ) {
|
||
return cur;
|
||
}
|
||
|
||
if ( vtype === "function" ) {
|
||
value = value.call( this, cur );
|
||
vtype = jQuery.type( value );
|
||
}
|
||
if ( value == null && prop.empty ) {
|
||
return this;
|
||
}
|
||
if ( vtype === "string" ) {
|
||
match = rplusequals.exec( value );
|
||
if ( match ) {
|
||
value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
|
||
}
|
||
}
|
||
local[ prop.idx ] = value;
|
||
return this[ fn ]( local );
|
||
};
|
||
} );
|
||
} );
|
||
|
||
// Add cssHook and .fx.step function for each named hook.
|
||
// accept a space separated string of properties
|
||
color.hook = function( hook ) {
|
||
var hooks = hook.split( " " );
|
||
each( hooks, function( i, hook ) {
|
||
jQuery.cssHooks[ hook ] = {
|
||
set: function( elem, value ) {
|
||
var parsed, curElem,
|
||
backgroundColor = "";
|
||
|
||
if ( value !== "transparent" && ( jQuery.type( value ) !== "string" ||
|
||
( parsed = stringParse( value ) ) ) ) {
|
||
value = color( parsed || value );
|
||
if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
|
||
curElem = hook === "backgroundColor" ? elem.parentNode : elem;
|
||
while (
|
||
( backgroundColor === "" || backgroundColor === "transparent" ) &&
|
||
curElem && curElem.style
|
||
) {
|
||
try {
|
||
backgroundColor = jQuery.css( curElem, "backgroundColor" );
|
||
curElem = curElem.parentNode;
|
||
} catch ( e ) {
|
||
}
|
||
}
|
||
|
||
value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
|
||
backgroundColor :
|
||
"_default" );
|
||
}
|
||
|
||
value = value.toRgbaString();
|
||
}
|
||
try {
|
||
elem.style[ hook ] = value;
|
||
} catch ( e ) {
|
||
|
||
// Wrapped to prevent IE from throwing errors on "invalid" values like
|
||
// 'auto' or 'inherit'
|
||
}
|
||
}
|
||
};
|
||
jQuery.fx.step[ hook ] = function( fx ) {
|
||
if ( !fx.colorInit ) {
|
||
fx.start = color( fx.elem, hook );
|
||
fx.end = color( fx.end );
|
||
fx.colorInit = true;
|
||
}
|
||
jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
|
||
};
|
||
} );
|
||
|
||
};
|
||
|
||
color.hook( stepHooks );
|
||
|
||
jQuery.cssHooks.borderColor = {
|
||
expand: function( value ) {
|
||
var expanded = {};
|
||
|
||
each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
|
||
expanded[ "border" + part + "Color" ] = value;
|
||
} );
|
||
return expanded;
|
||
}
|
||
};
|
||
|
||
// Basic color names only.
|
||
// Usage of any of the other color names requires adding yourself or including
|
||
// jquery.color.svg-names.js.
|
||
colors = jQuery.Color.names = {
|
||
|
||
// 4.1. Basic color keywords
|
||
aqua: "#00ffff",
|
||
black: "#000000",
|
||
blue: "#0000ff",
|
||
fuchsia: "#ff00ff",
|
||
gray: "#808080",
|
||
green: "#008000",
|
||
lime: "#00ff00",
|
||
maroon: "#800000",
|
||
navy: "#000080",
|
||
olive: "#808000",
|
||
purple: "#800080",
|
||
red: "#ff0000",
|
||
silver: "#c0c0c0",
|
||
teal: "#008080",
|
||
white: "#ffffff",
|
||
yellow: "#ffff00",
|
||
|
||
// 4.2.3. "transparent" color keyword
|
||
transparent: [ null, null, null, 0 ],
|
||
|
||
_default: "#ffffff"
|
||
};
|
||
|
||
} )( jQuery );
|
||
|
||
/******************************************************************************/
|
||
/****************************** CLASS ANIMATIONS ******************************/
|
||
/******************************************************************************/
|
||
( function() {
|
||
|
||
var classAnimationActions = [ "add", "remove", "toggle" ],
|
||
shorthandStyles = {
|
||
border: 1,
|
||
borderBottom: 1,
|
||
borderColor: 1,
|
||
borderLeft: 1,
|
||
borderRight: 1,
|
||
borderTop: 1,
|
||
borderWidth: 1,
|
||
margin: 1,
|
||
padding: 1
|
||
};
|
||
|
||
$.each(
|
||
[ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ],
|
||
function( _, prop ) {
|
||
$.fx.step[ prop ] = function( fx ) {
|
||
if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
|
||
jQuery.style( fx.elem, prop, fx.end );
|
||
fx.setAttr = true;
|
||
}
|
||
};
|
||
}
|
||
);
|
||
|
||
function getElementStyles( elem ) {
|
||
var key, len,
|
||
style = elem.ownerDocument.defaultView ?
|
||
elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
|
||
elem.currentStyle,
|
||
styles = {};
|
||
|
||
if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
|
||
len = style.length;
|
||
while ( len-- ) {
|
||
key = style[ len ];
|
||
if ( typeof style[ key ] === "string" ) {
|
||
styles[ $.camelCase( key ) ] = style[ key ];
|
||
}
|
||
}
|
||
|
||
// Support: Opera, IE <9
|
||
} else {
|
||
for ( key in style ) {
|
||
if ( typeof style[ key ] === "string" ) {
|
||
styles[ key ] = style[ key ];
|
||
}
|
||
}
|
||
}
|
||
|
||
return styles;
|
||
}
|
||
|
||
function styleDifference( oldStyle, newStyle ) {
|
||
var diff = {},
|
||
name, value;
|
||
|
||
for ( name in newStyle ) {
|
||
value = newStyle[ name ];
|
||
if ( oldStyle[ name ] !== value ) {
|
||
if ( !shorthandStyles[ name ] ) {
|
||
if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
|
||
diff[ name ] = value;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return diff;
|
||
}
|
||
|
||
// Support: jQuery <1.8
|
||
if ( !$.fn.addBack ) {
|
||
$.fn.addBack = function( selector ) {
|
||
return this.add( selector == null ?
|
||
this.prevObject : this.prevObject.filter( selector )
|
||
);
|
||
};
|
||
}
|
||
|
||
$.effects.animateClass = function( value, duration, easing, callback ) {
|
||
var o = $.speed( duration, easing, callback );
|
||
|
||
return this.queue( function() {
|
||
var animated = $( this ),
|
||
baseClass = animated.attr( "class" ) || "",
|
||
applyClassChange,
|
||
allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
|
||
|
||
// Map the animated objects to store the original styles.
|
||
allAnimations = allAnimations.map( function() {
|
||
var el = $( this );
|
||
return {
|
||
el: el,
|
||
start: getElementStyles( this )
|
||
};
|
||
} );
|
||
|
||
// Apply class change
|
||
applyClassChange = function() {
|
||
$.each( classAnimationActions, function( i, action ) {
|
||
if ( value[ action ] ) {
|
||
animated[ action + "Class" ]( value[ action ] );
|
||
}
|
||
} );
|
||
};
|
||
applyClassChange();
|
||
|
||
// Map all animated objects again - calculate new styles and diff
|
||
allAnimations = allAnimations.map( function() {
|
||
this.end = getElementStyles( this.el[ 0 ] );
|
||
this.diff = styleDifference( this.start, this.end );
|
||
return this;
|
||
} );
|
||
|
||
// Apply original class
|
||
animated.attr( "class", baseClass );
|
||
|
||
// Map all animated objects again - this time collecting a promise
|
||
allAnimations = allAnimations.map( function() {
|
||
var styleInfo = this,
|
||
dfd = $.Deferred(),
|
||
opts = $.extend( {}, o, {
|
||
queue: false,
|
||
complete: function() {
|
||
dfd.resolve( styleInfo );
|
||
}
|
||
} );
|
||
|
||
this.el.animate( this.diff, opts );
|
||
return dfd.promise();
|
||
} );
|
||
|
||
// Once all animations have completed:
|
||
$.when.apply( $, allAnimations.get() ).done( function() {
|
||
|
||
// Set the final class
|
||
applyClassChange();
|
||
|
||
// For each animated element,
|
||
// clear all css properties that were animated
|
||
$.each( arguments, function() {
|
||
var el = this.el;
|
||
$.each( this.diff, function( key ) {
|
||
el.css( key, "" );
|
||
} );
|
||
} );
|
||
|
||
// This is guarnteed to be there if you use jQuery.speed()
|
||
// it also handles dequeuing the next anim...
|
||
o.complete.call( animated[ 0 ] );
|
||
} );
|
||
} );
|
||
};
|
||
|
||
$.fn.extend( {
|
||
addClass: ( function( orig ) {
|
||
return function( classNames, speed, easing, callback ) {
|
||
return speed ?
|
||
$.effects.animateClass.call( this,
|
||
{ add: classNames }, speed, easing, callback ) :
|
||
orig.apply( this, arguments );
|
||
};
|
||
} )( $.fn.addClass ),
|
||
|
||
removeClass: ( function( orig ) {
|
||
return function( classNames, speed, easing, callback ) {
|
||
return arguments.length > 1 ?
|
||
$.effects.animateClass.call( this,
|
||
{ remove: classNames }, speed, easing, callback ) :
|
||
orig.apply( this, arguments );
|
||
};
|
||
} )( $.fn.removeClass ),
|
||
|
||
toggleClass: ( function( orig ) {
|
||
return function( classNames, force, speed, easing, callback ) {
|
||
if ( typeof force === "boolean" || force === undefined ) {
|
||
if ( !speed ) {
|
||
|
||
// Without speed parameter
|
||
return orig.apply( this, arguments );
|
||
} else {
|
||
return $.effects.animateClass.call( this,
|
||
( force ? { add: classNames } : { remove: classNames } ),
|
||
speed, easing, callback );
|
||
}
|
||
} else {
|
||
|
||
// Without force parameter
|
||
return $.effects.animateClass.call( this,
|
||
{ toggle: classNames }, force, speed, easing );
|
||
}
|
||
};
|
||
} )( $.fn.toggleClass ),
|
||
|
||
switchClass: function( remove, add, speed, easing, callback ) {
|
||
return $.effects.animateClass.call( this, {
|
||
add: add,
|
||
remove: remove
|
||
}, speed, easing, callback );
|
||
}
|
||
} );
|
||
|
||
} )();
|
||
|
||
/******************************************************************************/
|
||
/*********************************** EFFECTS **********************************/
|
||
/******************************************************************************/
|
||
|
||
( function() {
|
||
|
||
if ( $.expr && $.expr.filters && $.expr.filters.animated ) {
|
||
$.expr.filters.animated = ( function( orig ) {
|
||
return function( elem ) {
|
||
return !!$( elem ).data( dataSpaceAnimated ) || orig( elem );
|
||
};
|
||
} )( $.expr.filters.animated );
|
||
}
|
||
|
||
if ( $.uiBackCompat !== false ) {
|
||
$.extend( $.effects, {
|
||
|
||
// Saves a set of properties in a data storage
|
||
save: function( element, set ) {
|
||
var i = 0, length = set.length;
|
||
for ( ; i < length; i++ ) {
|
||
if ( set[ i ] !== null ) {
|
||
element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
|
||
}
|
||
}
|
||
},
|
||
|
||
// Restores a set of previously saved properties from a data storage
|
||
restore: function( element, set ) {
|
||
var val, i = 0, length = set.length;
|
||
for ( ; i < length; i++ ) {
|
||
if ( set[ i ] !== null ) {
|
||
val = element.data( dataSpace + set[ i ] );
|
||
element.css( set[ i ], val );
|
||
}
|
||
}
|
||
},
|
||
|
||
setMode: function( el, mode ) {
|
||
if ( mode === "toggle" ) {
|
||
mode = el.is( ":hidden" ) ? "show" : "hide";
|
||
}
|
||
return mode;
|
||
},
|
||
|
||
// Wraps the element around a wrapper that copies position properties
|
||
createWrapper: function( element ) {
|
||
|
||
// If the element is already wrapped, return it
|
||
if ( element.parent().is( ".ui-effects-wrapper" ) ) {
|
||
return element.parent();
|
||
}
|
||
|
||
// Wrap the element
|
||
var props = {
|
||
width: element.outerWidth( true ),
|
||
height: element.outerHeight( true ),
|
||
"float": element.css( "float" )
|
||
},
|
||
wrapper = $( "<div></div>" )
|
||
.addClass( "ui-effects-wrapper" )
|
||
.css( {
|
||
fontSize: "100%",
|
||
background: "transparent",
|
||
border: "none",
|
||
margin: 0,
|
||
padding: 0
|
||
} ),
|
||
|
||
// Store the size in case width/height are defined in % - Fixes #5245
|
||
size = {
|
||
width: element.width(),
|
||
height: element.height()
|
||
},
|
||
active = document.activeElement;
|
||
|
||
// Support: Firefox
|
||
// Firefox incorrectly exposes anonymous content
|
||
// https://bugzilla.mozilla.org/show_bug.cgi?id=561664
|
||
try {
|
||
active.id;
|
||
} catch ( e ) {
|
||
active = document.body;
|
||
}
|
||
|
||
element.wrap( wrapper );
|
||
|
||
// Fixes #7595 - Elements lose focus when wrapped.
|
||
if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
|
||
$( active ).trigger( "focus" );
|
||
}
|
||
|
||
// Hotfix for jQuery 1.4 since some change in wrap() seems to actually
|
||
// lose the reference to the wrapped element
|
||
wrapper = element.parent();
|
||
|
||
// Transfer positioning properties to the wrapper
|
||
if ( element.css( "position" ) === "static" ) {
|
||
wrapper.css( { position: "relative" } );
|
||
element.css( { position: "relative" } );
|
||
} else {
|
||
$.extend( props, {
|
||
position: element.css( "position" ),
|
||
zIndex: element.css( "z-index" )
|
||
} );
|
||
$.each( [ "top", "left", "bottom", "right" ], function( i, pos ) {
|
||
props[ pos ] = element.css( pos );
|
||
if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
|
||
props[ pos ] = "auto";
|
||
}
|
||
} );
|
||
element.css( {
|
||
position: "relative",
|
||
top: 0,
|
||
left: 0,
|
||
right: "auto",
|
||
bottom: "auto"
|
||
} );
|
||
}
|
||
element.css( size );
|
||
|
||
return wrapper.css( props ).show();
|
||
},
|
||
|
||
removeWrapper: function( element ) {
|
||
var active = document.activeElement;
|
||
|
||
if ( element.parent().is( ".ui-effects-wrapper" ) ) {
|
||
element.parent().replaceWith( element );
|
||
|
||
// Fixes #7595 - Elements lose focus when wrapped.
|
||
if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
|
||
$( active ).trigger( "focus" );
|
||
}
|
||
}
|
||
|
||
return element;
|
||
}
|
||
} );
|
||
}
|
||
|
||
$.extend( $.effects, {
|
||
version: "1.12.1",
|
||
|
||
define: function( name, mode, effect ) {
|
||
if ( !effect ) {
|
||
effect = mode;
|
||
mode = "effect";
|
||
}
|
||
|
||
$.effects.effect[ name ] = effect;
|
||
$.effects.effect[ name ].mode = mode;
|
||
|
||
return effect;
|
||
},
|
||
|
||
scaledDimensions: function( element, percent, direction ) {
|
||
if ( percent === 0 ) {
|
||
return {
|
||
height: 0,
|
||
width: 0,
|
||
outerHeight: 0,
|
||
outerWidth: 0
|
||
};
|
||
}
|
||
|
||
var x = direction !== "horizontal" ? ( ( percent || 100 ) / 100 ) : 1,
|
||
y = direction !== "vertical" ? ( ( percent || 100 ) / 100 ) : 1;
|
||
|
||
return {
|
||
height: element.height() * y,
|
||
width: element.width() * x,
|
||
outerHeight: element.outerHeight() * y,
|
||
outerWidth: element.outerWidth() * x
|
||
};
|
||
|
||
},
|
||
|
||
clipToBox: function( animation ) {
|
||
return {
|
||
width: animation.clip.right - animation.clip.left,
|
||
height: animation.clip.bottom - animation.clip.top,
|
||
left: animation.clip.left,
|
||
top: animation.clip.top
|
||
};
|
||
},
|
||
|
||
// Injects recently queued functions to be first in line (after "inprogress")
|
||
unshift: function( element, queueLength, count ) {
|
||
var queue = element.queue();
|
||
|
||
if ( queueLength > 1 ) {
|
||
queue.splice.apply( queue,
|
||
[ 1, 0 ].concat( queue.splice( queueLength, count ) ) );
|
||
}
|
||
element.dequeue();
|
||
},
|
||
|
||
saveStyle: function( element ) {
|
||
element.data( dataSpaceStyle, element[ 0 ].style.cssText );
|
||
},
|
||
|
||
restoreStyle: function( element ) {
|
||
element[ 0 ].style.cssText = element.data( dataSpaceStyle ) || "";
|
||
element.removeData( dataSpaceStyle );
|
||
},
|
||
|
||
mode: function( element, mode ) {
|
||
var hidden = element.is( ":hidden" );
|
||
|
||
if ( mode === "toggle" ) {
|
||
mode = hidden ? "show" : "hide";
|
||
}
|
||
if ( hidden ? mode === "hide" : mode === "show" ) {
|
||
mode = "none";
|
||
}
|
||
return mode;
|
||
},
|
||
|
||
// Translates a [top,left] array into a baseline value
|
||
getBaseline: function( origin, original ) {
|
||
var y, x;
|
||
|
||
switch ( origin[ 0 ] ) {
|
||
case "top":
|
||
y = 0;
|
||
break;
|
||
case "middle":
|
||
y = 0.5;
|
||
break;
|
||
case "bottom":
|
||
y = 1;
|
||
break;
|
||
default:
|
||
y = origin[ 0 ] / original.height;
|
||
}
|
||
|
||
switch ( origin[ 1 ] ) {
|
||
case "left":
|
||
x = 0;
|
||
break;
|
||
case "center":
|
||
x = 0.5;
|
||
break;
|
||
case "right":
|
||
x = 1;
|
||
break;
|
||
default:
|
||
x = origin[ 1 ] / original.width;
|
||
}
|
||
|
||
return {
|
||
x: x,
|
||
y: y
|
||
};
|
||
},
|
||
|
||
// Creates a placeholder element so that the original element can be made absolute
|
||
createPlaceholder: function( element ) {
|
||
var placeholder,
|
||
cssPosition = element.css( "position" ),
|
||
position = element.position();
|
||
|
||
// Lock in margins first to account for form elements, which
|
||
// will change margin if you explicitly set height
|
||
// see: http://jsfiddle.net/JZSMt/3/ https://bugs.webkit.org/show_bug.cgi?id=107380
|
||
// Support: Safari
|
||
element.css( {
|
||
marginTop: element.css( "marginTop" ),
|
||
marginBottom: element.css( "marginBottom" ),
|
||
marginLeft: element.css( "marginLeft" ),
|
||
marginRight: element.css( "marginRight" )
|
||
} )
|
||
.outerWidth( element.outerWidth() )
|
||
.outerHeight( element.outerHeight() );
|
||
|
||
if ( /^(static|relative)/.test( cssPosition ) ) {
|
||
cssPosition = "absolute";
|
||
|
||
placeholder = $( "<" + element[ 0 ].nodeName + ">" ).insertAfter( element ).css( {
|
||
|
||
// Convert inline to inline block to account for inline elements
|
||
// that turn to inline block based on content (like img)
|
||
display: /^(inline|ruby)/.test( element.css( "display" ) ) ?
|
||
"inline-block" :
|
||
"block",
|
||
visibility: "hidden",
|
||
|
||
// Margins need to be set to account for margin collapse
|
||
marginTop: element.css( "marginTop" ),
|
||
marginBottom: element.css( "marginBottom" ),
|
||
marginLeft: element.css( "marginLeft" ),
|
||
marginRight: element.css( "marginRight" ),
|
||
"float": element.css( "float" )
|
||
} )
|
||
.outerWidth( element.outerWidth() )
|
||
.outerHeight( element.outerHeight() )
|
||
.addClass( "ui-effects-placeholder" );
|
||
|
||
element.data( dataSpace + "placeholder", placeholder );
|
||
}
|
||
|
||
element.css( {
|
||
position: cssPosition,
|
||
left: position.left,
|
||
top: position.top
|
||
} );
|
||
|
||
return placeholder;
|
||
},
|
||
|
||
removePlaceholder: function( element ) {
|
||
var dataKey = dataSpace + "placeholder",
|
||
placeholder = element.data( dataKey );
|
||
|
||
if ( placeholder ) {
|
||
placeholder.remove();
|
||
element.removeData( dataKey );
|
||
}
|
||
},
|
||
|
||
// Removes a placeholder if it exists and restores
|
||
// properties that were modified during placeholder creation
|
||
cleanUp: function( element ) {
|
||
$.effects.restoreStyle( element );
|
||
$.effects.removePlaceholder( element );
|
||
},
|
||
|
||
setTransition: function( element, list, factor, value ) {
|
||
value = value || {};
|
||
$.each( list, function( i, x ) {
|
||
var unit = element.cssUnit( x );
|
||
if ( unit[ 0 ] > 0 ) {
|
||
value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
|
||
}
|
||
} );
|
||
return value;
|
||
}
|
||
} );
|
||
|
||
// Return an effect options object for the given parameters:
|
||
function _normalizeArguments( effect, options, speed, callback ) {
|
||
|
||
// Allow passing all options as the first parameter
|
||
if ( $.isPlainObject( effect ) ) {
|
||
options = effect;
|
||
effect = effect.effect;
|
||
}
|
||
|
||
// Convert to an object
|
||
effect = { effect: effect };
|
||
|
||
// Catch (effect, null, ...)
|
||
if ( options == null ) {
|
||
options = {};
|
||
}
|
||
|
||
// Catch (effect, callback)
|
||
if ( $.isFunction( options ) ) {
|
||
callback = options;
|
||
speed = null;
|
||
options = {};
|
||
}
|
||
|
||
// Catch (effect, speed, ?)
|
||
if ( typeof options === "number" || $.fx.speeds[ options ] ) {
|
||
callback = speed;
|
||
speed = options;
|
||
options = {};
|
||
}
|
||
|
||
// Catch (effect, options, callback)
|
||
if ( $.isFunction( speed ) ) {
|
||
callback = speed;
|
||
speed = null;
|
||
}
|
||
|
||
// Add options to effect
|
||
if ( options ) {
|
||
$.extend( effect, options );
|
||
}
|
||
|
||
speed = speed || options.duration;
|
||
effect.duration = $.fx.off ? 0 :
|
||
typeof speed === "number" ? speed :
|
||
speed in $.fx.speeds ? $.fx.speeds[ speed ] :
|
||
$.fx.speeds._default;
|
||
|
||
effect.complete = callback || options.complete;
|
||
|
||
return effect;
|
||
}
|
||
|
||
function standardAnimationOption( option ) {
|
||
|
||
// Valid standard speeds (nothing, number, named speed)
|
||
if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
|
||
return true;
|
||
}
|
||
|
||
// Invalid strings - treat as "normal" speed
|
||
if ( typeof option === "string" && !$.effects.effect[ option ] ) {
|
||
return true;
|
||
}
|
||
|
||
// Complete callback
|
||
if ( $.isFunction( option ) ) {
|
||
return true;
|
||
}
|
||
|
||
// Options hash (but not naming an effect)
|
||
if ( typeof option === "object" && !option.effect ) {
|
||
return true;
|
||
}
|
||
|
||
// Didn't match any standard API
|
||
return false;
|
||
}
|
||
|
||
$.fn.extend( {
|
||
effect: function( /* effect, options, speed, callback */ ) {
|
||
var args = _normalizeArguments.apply( this, arguments ),
|
||
effectMethod = $.effects.effect[ args.effect ],
|
||
defaultMode = effectMethod.mode,
|
||
queue = args.queue,
|
||
queueName = queue || "fx",
|
||
complete = args.complete,
|
||
mode = args.mode,
|
||
modes = [],
|
||
prefilter = function( next ) {
|
||
var el = $( this ),
|
||
normalizedMode = $.effects.mode( el, mode ) || defaultMode;
|
||
|
||
// Sentinel for duck-punching the :animated psuedo-selector
|
||
el.data( dataSpaceAnimated, true );
|
||
|
||
// Save effect mode for later use,
|
||
// we can't just call $.effects.mode again later,
|
||
// as the .show() below destroys the initial state
|
||
modes.push( normalizedMode );
|
||
|
||
// See $.uiBackCompat inside of run() for removal of defaultMode in 1.13
|
||
if ( defaultMode && ( normalizedMode === "show" ||
|
||
( normalizedMode === defaultMode && normalizedMode === "hide" ) ) ) {
|
||
el.show();
|
||
}
|
||
|
||
if ( !defaultMode || normalizedMode !== "none" ) {
|
||
$.effects.saveStyle( el );
|
||
}
|
||
|
||
if ( $.isFunction( next ) ) {
|
||
next();
|
||
}
|
||
};
|
||
|
||
if ( $.fx.off || !effectMethod ) {
|
||
|
||
// Delegate to the original method (e.g., .show()) if possible
|
||
if ( mode ) {
|
||
return this[ mode ]( args.duration, complete );
|
||
} else {
|
||
return this.each( function() {
|
||
if ( complete ) {
|
||
complete.call( this );
|
||
}
|
||
} );
|
||
}
|
||
}
|
||
|
||
function run( next ) {
|
||
var elem = $( this );
|
||
|
||
function cleanup() {
|
||
elem.removeData( dataSpaceAnimated );
|
||
|
||
$.effects.cleanUp( elem );
|
||
|
||
if ( args.mode === "hide" ) {
|
||
elem.hide();
|
||
}
|
||
|
||
done();
|
||
}
|
||
|
||
function done() {
|
||
if ( $.isFunction( complete ) ) {
|
||
complete.call( elem[ 0 ] );
|
||
}
|
||
|
||
if ( $.isFunction( next ) ) {
|
||
next();
|
||
}
|
||
}
|
||
|
||
// Override mode option on a per element basis,
|
||
// as toggle can be either show or hide depending on element state
|
||
args.mode = modes.shift();
|
||
|
||
if ( $.uiBackCompat !== false && !defaultMode ) {
|
||
if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
|
||
|
||
// Call the core method to track "olddisplay" properly
|
||
elem[ mode ]();
|
||
done();
|
||
} else {
|
||
effectMethod.call( elem[ 0 ], args, done );
|
||
}
|
||
} else {
|
||
if ( args.mode === "none" ) {
|
||
|
||
// Call the core method to track "olddisplay" properly
|
||
elem[ mode ]();
|
||
done();
|
||
} else {
|
||
effectMethod.call( elem[ 0 ], args, cleanup );
|
||
}
|
||
}
|
||
}
|
||
|
||
// Run prefilter on all elements first to ensure that
|
||
// any showing or hiding happens before placeholder creation,
|
||
// which ensures that any layout changes are correctly captured.
|
||
return queue === false ?
|
||
this.each( prefilter ).each( run ) :
|
||
this.queue( queueName, prefilter ).queue( queueName, run );
|
||
},
|
||
|
||
show: ( function( orig ) {
|
||
return function( option ) {
|
||
if ( standardAnimationOption( option ) ) {
|
||
return orig.apply( this, arguments );
|
||
} else {
|
||
var args = _normalizeArguments.apply( this, arguments );
|
||
args.mode = "show";
|
||
return this.effect.call( this, args );
|
||
}
|
||
};
|
||
} )( $.fn.show ),
|
||
|
||
hide: ( function( orig ) {
|
||
return function( option ) {
|
||
if ( standardAnimationOption( option ) ) {
|
||
return orig.apply( this, arguments );
|
||
} else {
|
||
var args = _normalizeArguments.apply( this, arguments );
|
||
args.mode = "hide";
|
||
return this.effect.call( this, args );
|
||
}
|
||
};
|
||
} )( $.fn.hide ),
|
||
|
||
toggle: ( function( orig ) {
|
||
return function( option ) {
|
||
if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
|
||
return orig.apply( this, arguments );
|
||
} else {
|
||
var args = _normalizeArguments.apply( this, arguments );
|
||
args.mode = "toggle";
|
||
return this.effect.call( this, args );
|
||
}
|
||
};
|
||
} )( $.fn.toggle ),
|
||
|
||
cssUnit: function( key ) {
|
||
var style = this.css( key ),
|
||
val = [];
|
||
|
||
$.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
|
||
if ( style.indexOf( unit ) > 0 ) {
|
||
val = [ parseFloat( style ), unit ];
|
||
}
|
||
} );
|
||
return val;
|
||
},
|
||
|
||
cssClip: function( clipObj ) {
|
||
if ( clipObj ) {
|
||
return this.css( "clip", "rect(" + clipObj.top + "px " + clipObj.right + "px " +
|
||
clipObj.bottom + "px " + clipObj.left + "px)" );
|
||
}
|
||
return parseClip( this.css( "clip" ), this );
|
||
},
|
||
|
||
transfer: function( options, done ) {
|
||
var element = $( this ),
|
||
target = $( options.to ),
|
||
targetFixed = target.css( "position" ) === "fixed",
|
||
body = $( "body" ),
|
||
fixTop = targetFixed ? body.scrollTop() : 0,
|
||
fixLeft = targetFixed ? body.scrollLeft() : 0,
|
||
endPosition = target.offset(),
|
||
animation = {
|
||
top: endPosition.top - fixTop,
|
||
left: endPosition.left - fixLeft,
|
||
height: target.innerHeight(),
|
||
width: target.innerWidth()
|
||
},
|
||
startPosition = element.offset(),
|
||
transfer = $( "<div class='ui-effects-transfer'></div>" )
|
||
.appendTo( "body" )
|
||
.addClass( options.className )
|
||
.css( {
|
||
top: startPosition.top - fixTop,
|
||
left: startPosition.left - fixLeft,
|
||
height: element.innerHeight(),
|
||
width: element.innerWidth(),
|
||
position: targetFixed ? "fixed" : "absolute"
|
||
} )
|
||
.animate( animation, options.duration, options.easing, function() {
|
||
transfer.remove();
|
||
if ( $.isFunction( done ) ) {
|
||
done();
|
||
}
|
||
} );
|
||
}
|
||
} );
|
||
|
||
function parseClip( str, element ) {
|
||
var outerWidth = element.outerWidth(),
|
||
outerHeight = element.outerHeight(),
|
||
clipRegex = /^rect\((-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto),?\s*(-?\d*\.?\d*px|-?\d+%|auto)\)$/,
|
||
values = clipRegex.exec( str ) || [ "", 0, outerWidth, outerHeight, 0 ];
|
||
|
||
return {
|
||
top: parseFloat( values[ 1 ] ) || 0,
|
||
right: values[ 2 ] === "auto" ? outerWidth : parseFloat( values[ 2 ] ),
|
||
bottom: values[ 3 ] === "auto" ? outerHeight : parseFloat( values[ 3 ] ),
|
||
left: parseFloat( values[ 4 ] ) || 0
|
||
};
|
||
}
|
||
|
||
$.fx.step.clip = function( fx ) {
|
||
if ( !fx.clipInit ) {
|
||
fx.start = $( fx.elem ).cssClip();
|
||
if ( typeof fx.end === "string" ) {
|
||
fx.end = parseClip( fx.end, fx.elem );
|
||
}
|
||
fx.clipInit = true;
|
||
}
|
||
|
||
$( fx.elem ).cssClip( {
|
||
top: fx.pos * ( fx.end.top - fx.start.top ) + fx.start.top,
|
||
right: fx.pos * ( fx.end.right - fx.start.right ) + fx.start.right,
|
||
bottom: fx.pos * ( fx.end.bottom - fx.start.bottom ) + fx.start.bottom,
|
||
left: fx.pos * ( fx.end.left - fx.start.left ) + fx.start.left
|
||
} );
|
||
};
|
||
|
||
} )();
|
||
|
||
/******************************************************************************/
|
||
/*********************************** EASING ***********************************/
|
||
/******************************************************************************/
|
||
|
||
( function() {
|
||
|
||
// Based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
|
||
|
||
var baseEasings = {};
|
||
|
||
$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
|
||
baseEasings[ name ] = function( p ) {
|
||
return Math.pow( p, i + 2 );
|
||
};
|
||
} );
|
||
|
||
$.extend( baseEasings, {
|
||
Sine: function( p ) {
|
||
return 1 - Math.cos( p * Math.PI / 2 );
|
||
},
|
||
Circ: function( p ) {
|
||
return 1 - Math.sqrt( 1 - p * p );
|
||
},
|
||
Elastic: function( p ) {
|
||
return p === 0 || p === 1 ? p :
|
||
-Math.pow( 2, 8 * ( p - 1 ) ) * Math.sin( ( ( p - 1 ) * 80 - 7.5 ) * Math.PI / 15 );
|
||
},
|
||
Back: function( p ) {
|
||
return p * p * ( 3 * p - 2 );
|
||
},
|
||
Bounce: function( p ) {
|
||
var pow2,
|
||
bounce = 4;
|
||
|
||
while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
|
||
return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
|
||
}
|
||
} );
|
||
|
||
$.each( baseEasings, function( name, easeIn ) {
|
||
$.easing[ "easeIn" + name ] = easeIn;
|
||
$.easing[ "easeOut" + name ] = function( p ) {
|
||
return 1 - easeIn( 1 - p );
|
||
};
|
||
$.easing[ "easeInOut" + name ] = function( p ) {
|
||
return p < 0.5 ?
|
||
easeIn( p * 2 ) / 2 :
|
||
1 - easeIn( p * -2 + 2 ) / 2;
|
||
};
|
||
} );
|
||
|
||
} )();
|
||
|
||
var effect = $.effects;
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Blind 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Blind Effect
|
||
//>>group: Effects
|
||
//>>description: Blinds the element.
|
||
//>>docs: http://api.jqueryui.com/blind-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectBlind = $.effects.define( "blind", "hide", function( options, done ) {
|
||
var map = {
|
||
up: [ "bottom", "top" ],
|
||
vertical: [ "bottom", "top" ],
|
||
down: [ "top", "bottom" ],
|
||
left: [ "right", "left" ],
|
||
horizontal: [ "right", "left" ],
|
||
right: [ "left", "right" ]
|
||
},
|
||
element = $( this ),
|
||
direction = options.direction || "up",
|
||
start = element.cssClip(),
|
||
animate = { clip: $.extend( {}, start ) },
|
||
placeholder = $.effects.createPlaceholder( element );
|
||
|
||
animate.clip[ map[ direction ][ 0 ] ] = animate.clip[ map[ direction ][ 1 ] ];
|
||
|
||
if ( options.mode === "show" ) {
|
||
element.cssClip( animate.clip );
|
||
if ( placeholder ) {
|
||
placeholder.css( $.effects.clipToBox( animate ) );
|
||
}
|
||
|
||
animate.clip = start;
|
||
}
|
||
|
||
if ( placeholder ) {
|
||
placeholder.animate( $.effects.clipToBox( animate ), options.duration, options.easing );
|
||
}
|
||
|
||
element.animate( animate, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Bounce 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Bounce Effect
|
||
//>>group: Effects
|
||
//>>description: Bounces an element horizontally or vertically n times.
|
||
//>>docs: http://api.jqueryui.com/bounce-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectBounce = $.effects.define( "bounce", function( options, done ) {
|
||
var upAnim, downAnim, refValue,
|
||
element = $( this ),
|
||
|
||
// Defaults:
|
||
mode = options.mode,
|
||
hide = mode === "hide",
|
||
show = mode === "show",
|
||
direction = options.direction || "up",
|
||
distance = options.distance,
|
||
times = options.times || 5,
|
||
|
||
// Number of internal animations
|
||
anims = times * 2 + ( show || hide ? 1 : 0 ),
|
||
speed = options.duration / anims,
|
||
easing = options.easing,
|
||
|
||
// Utility:
|
||
ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
|
||
motion = ( direction === "up" || direction === "left" ),
|
||
i = 0,
|
||
|
||
queuelen = element.queue().length;
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
refValue = element.css( ref );
|
||
|
||
// Default distance for the BIGGEST bounce is the outer Distance / 3
|
||
if ( !distance ) {
|
||
distance = element[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
|
||
}
|
||
|
||
if ( show ) {
|
||
downAnim = { opacity: 1 };
|
||
downAnim[ ref ] = refValue;
|
||
|
||
// If we are showing, force opacity 0 and set the initial position
|
||
// then do the "first" animation
|
||
element
|
||
.css( "opacity", 0 )
|
||
.css( ref, motion ? -distance * 2 : distance * 2 )
|
||
.animate( downAnim, speed, easing );
|
||
}
|
||
|
||
// Start at the smallest distance if we are hiding
|
||
if ( hide ) {
|
||
distance = distance / Math.pow( 2, times - 1 );
|
||
}
|
||
|
||
downAnim = {};
|
||
downAnim[ ref ] = refValue;
|
||
|
||
// Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
|
||
for ( ; i < times; i++ ) {
|
||
upAnim = {};
|
||
upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
|
||
|
||
element
|
||
.animate( upAnim, speed, easing )
|
||
.animate( downAnim, speed, easing );
|
||
|
||
distance = hide ? distance * 2 : distance / 2;
|
||
}
|
||
|
||
// Last Bounce when Hiding
|
||
if ( hide ) {
|
||
upAnim = { opacity: 0 };
|
||
upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
|
||
|
||
element.animate( upAnim, speed, easing );
|
||
}
|
||
|
||
element.queue( done );
|
||
|
||
$.effects.unshift( element, queuelen, anims + 1 );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Clip 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Clip Effect
|
||
//>>group: Effects
|
||
//>>description: Clips the element on and off like an old TV.
|
||
//>>docs: http://api.jqueryui.com/clip-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectClip = $.effects.define( "clip", "hide", function( options, done ) {
|
||
var start,
|
||
animate = {},
|
||
element = $( this ),
|
||
direction = options.direction || "vertical",
|
||
both = direction === "both",
|
||
horizontal = both || direction === "horizontal",
|
||
vertical = both || direction === "vertical";
|
||
|
||
start = element.cssClip();
|
||
animate.clip = {
|
||
top: vertical ? ( start.bottom - start.top ) / 2 : start.top,
|
||
right: horizontal ? ( start.right - start.left ) / 2 : start.right,
|
||
bottom: vertical ? ( start.bottom - start.top ) / 2 : start.bottom,
|
||
left: horizontal ? ( start.right - start.left ) / 2 : start.left
|
||
};
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
if ( options.mode === "show" ) {
|
||
element.cssClip( animate.clip );
|
||
animate.clip = start;
|
||
}
|
||
|
||
element.animate( animate, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Drop 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Drop Effect
|
||
//>>group: Effects
|
||
//>>description: Moves an element in one direction and hides it at the same time.
|
||
//>>docs: http://api.jqueryui.com/drop-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectDrop = $.effects.define( "drop", "hide", function( options, done ) {
|
||
|
||
var distance,
|
||
element = $( this ),
|
||
mode = options.mode,
|
||
show = mode === "show",
|
||
direction = options.direction || "left",
|
||
ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
|
||
motion = ( direction === "up" || direction === "left" ) ? "-=" : "+=",
|
||
oppositeMotion = ( motion === "+=" ) ? "-=" : "+=",
|
||
animation = {
|
||
opacity: 0
|
||
};
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
distance = options.distance ||
|
||
element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ) / 2;
|
||
|
||
animation[ ref ] = motion + distance;
|
||
|
||
if ( show ) {
|
||
element.css( animation );
|
||
|
||
animation[ ref ] = oppositeMotion + distance;
|
||
animation.opacity = 1;
|
||
}
|
||
|
||
// Animate
|
||
element.animate( animation, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Explode 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Explode Effect
|
||
//>>group: Effects
|
||
// jscs:disable maximumLineLength
|
||
//>>description: Explodes an element in all directions into n pieces. Implodes an element to its original wholeness.
|
||
// jscs:enable maximumLineLength
|
||
//>>docs: http://api.jqueryui.com/explode-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectExplode = $.effects.define( "explode", "hide", function( options, done ) {
|
||
|
||
var i, j, left, top, mx, my,
|
||
rows = options.pieces ? Math.round( Math.sqrt( options.pieces ) ) : 3,
|
||
cells = rows,
|
||
element = $( this ),
|
||
mode = options.mode,
|
||
show = mode === "show",
|
||
|
||
// Show and then visibility:hidden the element before calculating offset
|
||
offset = element.show().css( "visibility", "hidden" ).offset(),
|
||
|
||
// Width and height of a piece
|
||
width = Math.ceil( element.outerWidth() / cells ),
|
||
height = Math.ceil( element.outerHeight() / rows ),
|
||
pieces = [];
|
||
|
||
// Children animate complete:
|
||
function childComplete() {
|
||
pieces.push( this );
|
||
if ( pieces.length === rows * cells ) {
|
||
animComplete();
|
||
}
|
||
}
|
||
|
||
// Clone the element for each row and cell.
|
||
for ( i = 0; i < rows; i++ ) { // ===>
|
||
top = offset.top + i * height;
|
||
my = i - ( rows - 1 ) / 2;
|
||
|
||
for ( j = 0; j < cells; j++ ) { // |||
|
||
left = offset.left + j * width;
|
||
mx = j - ( cells - 1 ) / 2;
|
||
|
||
// Create a clone of the now hidden main element that will be absolute positioned
|
||
// within a wrapper div off the -left and -top equal to size of our pieces
|
||
element
|
||
.clone()
|
||
.appendTo( "body" )
|
||
.wrap( "<div></div>" )
|
||
.css( {
|
||
position: "absolute",
|
||
visibility: "visible",
|
||
left: -j * width,
|
||
top: -i * height
|
||
} )
|
||
|
||
// Select the wrapper - make it overflow: hidden and absolute positioned based on
|
||
// where the original was located +left and +top equal to the size of pieces
|
||
.parent()
|
||
.addClass( "ui-effects-explode" )
|
||
.css( {
|
||
position: "absolute",
|
||
overflow: "hidden",
|
||
width: width,
|
||
height: height,
|
||
left: left + ( show ? mx * width : 0 ),
|
||
top: top + ( show ? my * height : 0 ),
|
||
opacity: show ? 0 : 1
|
||
} )
|
||
.animate( {
|
||
left: left + ( show ? 0 : mx * width ),
|
||
top: top + ( show ? 0 : my * height ),
|
||
opacity: show ? 1 : 0
|
||
}, options.duration || 500, options.easing, childComplete );
|
||
}
|
||
}
|
||
|
||
function animComplete() {
|
||
element.css( {
|
||
visibility: "visible"
|
||
} );
|
||
$( pieces ).remove();
|
||
done();
|
||
}
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Fade 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Fade Effect
|
||
//>>group: Effects
|
||
//>>description: Fades the element.
|
||
//>>docs: http://api.jqueryui.com/fade-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectFade = $.effects.define( "fade", "toggle", function( options, done ) {
|
||
var show = options.mode === "show";
|
||
|
||
$( this )
|
||
.css( "opacity", show ? 0 : 1 )
|
||
.animate( {
|
||
opacity: show ? 1 : 0
|
||
}, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Fold 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Fold Effect
|
||
//>>group: Effects
|
||
//>>description: Folds an element first horizontally and then vertically.
|
||
//>>docs: http://api.jqueryui.com/fold-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectFold = $.effects.define( "fold", "hide", function( options, done ) {
|
||
|
||
// Create element
|
||
var element = $( this ),
|
||
mode = options.mode,
|
||
show = mode === "show",
|
||
hide = mode === "hide",
|
||
size = options.size || 15,
|
||
percent = /([0-9]+)%/.exec( size ),
|
||
horizFirst = !!options.horizFirst,
|
||
ref = horizFirst ? [ "right", "bottom" ] : [ "bottom", "right" ],
|
||
duration = options.duration / 2,
|
||
|
||
placeholder = $.effects.createPlaceholder( element ),
|
||
|
||
start = element.cssClip(),
|
||
animation1 = { clip: $.extend( {}, start ) },
|
||
animation2 = { clip: $.extend( {}, start ) },
|
||
|
||
distance = [ start[ ref[ 0 ] ], start[ ref[ 1 ] ] ],
|
||
|
||
queuelen = element.queue().length;
|
||
|
||
if ( percent ) {
|
||
size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
|
||
}
|
||
animation1.clip[ ref[ 0 ] ] = size;
|
||
animation2.clip[ ref[ 0 ] ] = size;
|
||
animation2.clip[ ref[ 1 ] ] = 0;
|
||
|
||
if ( show ) {
|
||
element.cssClip( animation2.clip );
|
||
if ( placeholder ) {
|
||
placeholder.css( $.effects.clipToBox( animation2 ) );
|
||
}
|
||
|
||
animation2.clip = start;
|
||
}
|
||
|
||
// Animate
|
||
element
|
||
.queue( function( next ) {
|
||
if ( placeholder ) {
|
||
placeholder
|
||
.animate( $.effects.clipToBox( animation1 ), duration, options.easing )
|
||
.animate( $.effects.clipToBox( animation2 ), duration, options.easing );
|
||
}
|
||
|
||
next();
|
||
} )
|
||
.animate( animation1, duration, options.easing )
|
||
.animate( animation2, duration, options.easing )
|
||
.queue( done );
|
||
|
||
$.effects.unshift( element, queuelen, 4 );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Highlight 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Highlight Effect
|
||
//>>group: Effects
|
||
//>>description: Highlights the background of an element in a defined color for a custom duration.
|
||
//>>docs: http://api.jqueryui.com/highlight-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectHighlight = $.effects.define( "highlight", "show", function( options, done ) {
|
||
var element = $( this ),
|
||
animation = {
|
||
backgroundColor: element.css( "backgroundColor" )
|
||
};
|
||
|
||
if ( options.mode === "hide" ) {
|
||
animation.opacity = 0;
|
||
}
|
||
|
||
$.effects.saveStyle( element );
|
||
|
||
element
|
||
.css( {
|
||
backgroundImage: "none",
|
||
backgroundColor: options.color || "#ffff99"
|
||
} )
|
||
.animate( animation, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Size 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Size Effect
|
||
//>>group: Effects
|
||
//>>description: Resize an element to a specified width and height.
|
||
//>>docs: http://api.jqueryui.com/size-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectSize = $.effects.define( "size", function( options, done ) {
|
||
|
||
// Create element
|
||
var baseline, factor, temp,
|
||
element = $( this ),
|
||
|
||
// Copy for children
|
||
cProps = [ "fontSize" ],
|
||
vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
|
||
hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
|
||
|
||
// Set options
|
||
mode = options.mode,
|
||
restore = mode !== "effect",
|
||
scale = options.scale || "both",
|
||
origin = options.origin || [ "middle", "center" ],
|
||
position = element.css( "position" ),
|
||
pos = element.position(),
|
||
original = $.effects.scaledDimensions( element ),
|
||
from = options.from || original,
|
||
to = options.to || $.effects.scaledDimensions( element, 0 );
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
if ( mode === "show" ) {
|
||
temp = from;
|
||
from = to;
|
||
to = temp;
|
||
}
|
||
|
||
// Set scaling factor
|
||
factor = {
|
||
from: {
|
||
y: from.height / original.height,
|
||
x: from.width / original.width
|
||
},
|
||
to: {
|
||
y: to.height / original.height,
|
||
x: to.width / original.width
|
||
}
|
||
};
|
||
|
||
// Scale the css box
|
||
if ( scale === "box" || scale === "both" ) {
|
||
|
||
// Vertical props scaling
|
||
if ( factor.from.y !== factor.to.y ) {
|
||
from = $.effects.setTransition( element, vProps, factor.from.y, from );
|
||
to = $.effects.setTransition( element, vProps, factor.to.y, to );
|
||
}
|
||
|
||
// Horizontal props scaling
|
||
if ( factor.from.x !== factor.to.x ) {
|
||
from = $.effects.setTransition( element, hProps, factor.from.x, from );
|
||
to = $.effects.setTransition( element, hProps, factor.to.x, to );
|
||
}
|
||
}
|
||
|
||
// Scale the content
|
||
if ( scale === "content" || scale === "both" ) {
|
||
|
||
// Vertical props scaling
|
||
if ( factor.from.y !== factor.to.y ) {
|
||
from = $.effects.setTransition( element, cProps, factor.from.y, from );
|
||
to = $.effects.setTransition( element, cProps, factor.to.y, to );
|
||
}
|
||
}
|
||
|
||
// Adjust the position properties based on the provided origin points
|
||
if ( origin ) {
|
||
baseline = $.effects.getBaseline( origin, original );
|
||
from.top = ( original.outerHeight - from.outerHeight ) * baseline.y + pos.top;
|
||
from.left = ( original.outerWidth - from.outerWidth ) * baseline.x + pos.left;
|
||
to.top = ( original.outerHeight - to.outerHeight ) * baseline.y + pos.top;
|
||
to.left = ( original.outerWidth - to.outerWidth ) * baseline.x + pos.left;
|
||
}
|
||
element.css( from );
|
||
|
||
// Animate the children if desired
|
||
if ( scale === "content" || scale === "both" ) {
|
||
|
||
vProps = vProps.concat( [ "marginTop", "marginBottom" ] ).concat( cProps );
|
||
hProps = hProps.concat( [ "marginLeft", "marginRight" ] );
|
||
|
||
// Only animate children with width attributes specified
|
||
// TODO: is this right? should we include anything with css width specified as well
|
||
element.find( "*[width]" ).each( function() {
|
||
var child = $( this ),
|
||
childOriginal = $.effects.scaledDimensions( child ),
|
||
childFrom = {
|
||
height: childOriginal.height * factor.from.y,
|
||
width: childOriginal.width * factor.from.x,
|
||
outerHeight: childOriginal.outerHeight * factor.from.y,
|
||
outerWidth: childOriginal.outerWidth * factor.from.x
|
||
},
|
||
childTo = {
|
||
height: childOriginal.height * factor.to.y,
|
||
width: childOriginal.width * factor.to.x,
|
||
outerHeight: childOriginal.height * factor.to.y,
|
||
outerWidth: childOriginal.width * factor.to.x
|
||
};
|
||
|
||
// Vertical props scaling
|
||
if ( factor.from.y !== factor.to.y ) {
|
||
childFrom = $.effects.setTransition( child, vProps, factor.from.y, childFrom );
|
||
childTo = $.effects.setTransition( child, vProps, factor.to.y, childTo );
|
||
}
|
||
|
||
// Horizontal props scaling
|
||
if ( factor.from.x !== factor.to.x ) {
|
||
childFrom = $.effects.setTransition( child, hProps, factor.from.x, childFrom );
|
||
childTo = $.effects.setTransition( child, hProps, factor.to.x, childTo );
|
||
}
|
||
|
||
if ( restore ) {
|
||
$.effects.saveStyle( child );
|
||
}
|
||
|
||
// Animate children
|
||
child.css( childFrom );
|
||
child.animate( childTo, options.duration, options.easing, function() {
|
||
|
||
// Restore children
|
||
if ( restore ) {
|
||
$.effects.restoreStyle( child );
|
||
}
|
||
} );
|
||
} );
|
||
}
|
||
|
||
// Animate
|
||
element.animate( to, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: function() {
|
||
|
||
var offset = element.offset();
|
||
|
||
if ( to.opacity === 0 ) {
|
||
element.css( "opacity", from.opacity );
|
||
}
|
||
|
||
if ( !restore ) {
|
||
element
|
||
.css( "position", position === "static" ? "relative" : position )
|
||
.offset( offset );
|
||
|
||
// Need to save style here so that automatic style restoration
|
||
// doesn't restore to the original styles from before the animation.
|
||
$.effects.saveStyle( element );
|
||
}
|
||
|
||
done();
|
||
}
|
||
} );
|
||
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Scale 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Scale Effect
|
||
//>>group: Effects
|
||
//>>description: Grows or shrinks an element and its content.
|
||
//>>docs: http://api.jqueryui.com/scale-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectScale = $.effects.define( "scale", function( options, done ) {
|
||
|
||
// Create element
|
||
var el = $( this ),
|
||
mode = options.mode,
|
||
percent = parseInt( options.percent, 10 ) ||
|
||
( parseInt( options.percent, 10 ) === 0 ? 0 : ( mode !== "effect" ? 0 : 100 ) ),
|
||
|
||
newOptions = $.extend( true, {
|
||
from: $.effects.scaledDimensions( el ),
|
||
to: $.effects.scaledDimensions( el, percent, options.direction || "both" ),
|
||
origin: options.origin || [ "middle", "center" ]
|
||
}, options );
|
||
|
||
// Fade option to support puff
|
||
if ( options.fade ) {
|
||
newOptions.from.opacity = 1;
|
||
newOptions.to.opacity = 0;
|
||
}
|
||
|
||
$.effects.effect.size.call( this, newOptions, done );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Puff 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Puff Effect
|
||
//>>group: Effects
|
||
//>>description: Creates a puff effect by scaling the element up and hiding it at the same time.
|
||
//>>docs: http://api.jqueryui.com/puff-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectPuff = $.effects.define( "puff", "hide", function( options, done ) {
|
||
var newOptions = $.extend( true, {}, options, {
|
||
fade: true,
|
||
percent: parseInt( options.percent, 10 ) || 150
|
||
} );
|
||
|
||
$.effects.effect.scale.call( this, newOptions, done );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Pulsate 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Pulsate Effect
|
||
//>>group: Effects
|
||
//>>description: Pulsates an element n times by changing the opacity to zero and back.
|
||
//>>docs: http://api.jqueryui.com/pulsate-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectPulsate = $.effects.define( "pulsate", "show", function( options, done ) {
|
||
var element = $( this ),
|
||
mode = options.mode,
|
||
show = mode === "show",
|
||
hide = mode === "hide",
|
||
showhide = show || hide,
|
||
|
||
// Showing or hiding leaves off the "last" animation
|
||
anims = ( ( options.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
|
||
duration = options.duration / anims,
|
||
animateTo = 0,
|
||
i = 1,
|
||
queuelen = element.queue().length;
|
||
|
||
if ( show || !element.is( ":visible" ) ) {
|
||
element.css( "opacity", 0 ).show();
|
||
animateTo = 1;
|
||
}
|
||
|
||
// Anims - 1 opacity "toggles"
|
||
for ( ; i < anims; i++ ) {
|
||
element.animate( { opacity: animateTo }, duration, options.easing );
|
||
animateTo = 1 - animateTo;
|
||
}
|
||
|
||
element.animate( { opacity: animateTo }, duration, options.easing );
|
||
|
||
element.queue( done );
|
||
|
||
$.effects.unshift( element, queuelen, anims + 1 );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Shake 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Shake Effect
|
||
//>>group: Effects
|
||
//>>description: Shakes an element horizontally or vertically n times.
|
||
//>>docs: http://api.jqueryui.com/shake-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectShake = $.effects.define( "shake", function( options, done ) {
|
||
|
||
var i = 1,
|
||
element = $( this ),
|
||
direction = options.direction || "left",
|
||
distance = options.distance || 20,
|
||
times = options.times || 3,
|
||
anims = times * 2 + 1,
|
||
speed = Math.round( options.duration / anims ),
|
||
ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
|
||
positiveMotion = ( direction === "up" || direction === "left" ),
|
||
animation = {},
|
||
animation1 = {},
|
||
animation2 = {},
|
||
|
||
queuelen = element.queue().length;
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
// Animation
|
||
animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
|
||
animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
|
||
animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
|
||
|
||
// Animate
|
||
element.animate( animation, speed, options.easing );
|
||
|
||
// Shakes
|
||
for ( ; i < times; i++ ) {
|
||
element
|
||
.animate( animation1, speed, options.easing )
|
||
.animate( animation2, speed, options.easing );
|
||
}
|
||
|
||
element
|
||
.animate( animation1, speed, options.easing )
|
||
.animate( animation, speed / 2, options.easing )
|
||
.queue( done );
|
||
|
||
$.effects.unshift( element, queuelen, anims + 1 );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Slide 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Slide Effect
|
||
//>>group: Effects
|
||
//>>description: Slides an element in and out of the viewport.
|
||
//>>docs: http://api.jqueryui.com/slide-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effectsEffectSlide = $.effects.define( "slide", "show", function( options, done ) {
|
||
var startClip, startRef,
|
||
element = $( this ),
|
||
map = {
|
||
up: [ "bottom", "top" ],
|
||
down: [ "top", "bottom" ],
|
||
left: [ "right", "left" ],
|
||
right: [ "left", "right" ]
|
||
},
|
||
mode = options.mode,
|
||
direction = options.direction || "left",
|
||
ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
|
||
positiveMotion = ( direction === "up" || direction === "left" ),
|
||
distance = options.distance ||
|
||
element[ ref === "top" ? "outerHeight" : "outerWidth" ]( true ),
|
||
animation = {};
|
||
|
||
$.effects.createPlaceholder( element );
|
||
|
||
startClip = element.cssClip();
|
||
startRef = element.position()[ ref ];
|
||
|
||
// Define hide animation
|
||
animation[ ref ] = ( positiveMotion ? -1 : 1 ) * distance + startRef;
|
||
animation.clip = element.cssClip();
|
||
animation.clip[ map[ direction ][ 1 ] ] = animation.clip[ map[ direction ][ 0 ] ];
|
||
|
||
// Reverse the animation if we're showing
|
||
if ( mode === "show" ) {
|
||
element.cssClip( animation.clip );
|
||
element.css( ref, animation[ ref ] );
|
||
animation.clip = startClip;
|
||
animation[ ref ] = startRef;
|
||
}
|
||
|
||
// Actually animate
|
||
element.animate( animation, {
|
||
queue: false,
|
||
duration: options.duration,
|
||
easing: options.easing,
|
||
complete: done
|
||
} );
|
||
} );
|
||
|
||
|
||
/*!
|
||
* jQuery UI Effects Transfer 1.12.1
|
||
* http://jqueryui.com
|
||
*
|
||
* Copyright jQuery Foundation and other contributors
|
||
* Released under the MIT license.
|
||
* http://jquery.org/license
|
||
*/
|
||
|
||
//>>label: Transfer Effect
|
||
//>>group: Effects
|
||
//>>description: Displays a transfer effect from one element to another.
|
||
//>>docs: http://api.jqueryui.com/transfer-effect/
|
||
//>>demos: http://jqueryui.com/effect/
|
||
|
||
|
||
|
||
var effect;
|
||
if ( $.uiBackCompat !== false ) {
|
||
effect = $.effects.define( "transfer", function( options, done ) {
|
||
$( this ).transfer( options, done );
|
||
} );
|
||
}
|
||
var effectsEffectTransfer = effect;
|
||
|
||
|
||
|
||
|
||
}));
|
||
/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
|
||
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
|
||
* and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
|
||
*
|
||
* Version: 1.3.8
|
||
*
|
||
*/
|
||
(function($) {
|
||
|
||
$.fn.extend({
|
||
slimScroll: function(options) {
|
||
|
||
var defaults = {
|
||
|
||
// width in pixels of the visible scroll area
|
||
width : 'auto',
|
||
|
||
// height in pixels of the visible scroll area
|
||
height : '250px',
|
||
|
||
// width in pixels of the scrollbar and rail
|
||
size : '7px',
|
||
|
||
// scrollbar color, accepts any hex/color value
|
||
color: '#000',
|
||
|
||
// scrollbar position - left/right
|
||
position : 'right',
|
||
|
||
// distance in pixels between the side edge and the scrollbar
|
||
distance : '1px',
|
||
|
||
// default scroll position on load - top / bottom / $('selector')
|
||
start : 'top',
|
||
|
||
// sets scrollbar opacity
|
||
opacity : .4,
|
||
|
||
// enables always-on mode for the scrollbar
|
||
alwaysVisible : false,
|
||
|
||
// check if we should hide the scrollbar when user is hovering over
|
||
disableFadeOut : false,
|
||
|
||
// sets visibility of the rail
|
||
railVisible : false,
|
||
|
||
// sets rail color
|
||
railColor : '#333',
|
||
|
||
// sets rail opacity
|
||
railOpacity : .2,
|
||
|
||
// whether we should use jQuery UI Draggable to enable bar dragging
|
||
railDraggable : true,
|
||
|
||
// defautlt CSS class of the slimscroll rail
|
||
railClass : 'slimScrollRail',
|
||
|
||
// defautlt CSS class of the slimscroll bar
|
||
barClass : 'slimScrollBar',
|
||
|
||
// defautlt CSS class of the slimscroll wrapper
|
||
wrapperClass : 'slimScrollDiv',
|
||
|
||
// check if mousewheel should scroll the window if we reach top/bottom
|
||
allowPageScroll : false,
|
||
|
||
// scroll amount applied to each mouse wheel step
|
||
wheelStep : 20,
|
||
|
||
// scroll amount applied when user is using gestures
|
||
touchScrollStep : 200,
|
||
|
||
// sets border radius
|
||
borderRadius: '7px',
|
||
|
||
// sets border radius of the rail
|
||
railBorderRadius : '7px'
|
||
};
|
||
|
||
var o = $.extend(defaults, options);
|
||
|
||
// do it for every element that matches selector
|
||
this.each(function(){
|
||
|
||
var isOverPanel, isOverBar, isDragg, queueHide, touchDif,
|
||
barHeight, percentScroll, lastScroll,
|
||
divS = '<div></div>',
|
||
minBarHeight = 30,
|
||
releaseScroll = false;
|
||
|
||
// used in event handlers and for better minification
|
||
var me = $(this);
|
||
|
||
// ensure we are not binding it again
|
||
if (me.parent().hasClass(o.wrapperClass))
|
||
{
|
||
// start from last bar position
|
||
var offset = me.scrollTop();
|
||
|
||
// find bar and rail
|
||
bar = me.siblings('.' + o.barClass);
|
||
rail = me.siblings('.' + o.railClass);
|
||
|
||
getBarHeight();
|
||
|
||
// check if we should scroll existing instance
|
||
if ($.isPlainObject(options))
|
||
{
|
||
// Pass height: auto to an existing slimscroll object to force a resize after contents have changed
|
||
if ( 'height' in options && options.height == 'auto' ) {
|
||
me.parent().css('height', 'auto');
|
||
me.css('height', 'auto');
|
||
var height = me.parent().parent().height();
|
||
me.parent().css('height', height);
|
||
me.css('height', height);
|
||
} else if ('height' in options) {
|
||
var h = options.height;
|
||
me.parent().css('height', h);
|
||
me.css('height', h);
|
||
}
|
||
|
||
if ('scrollTo' in options)
|
||
{
|
||
// jump to a static point
|
||
offset = parseInt(o.scrollTo);
|
||
}
|
||
else if ('scrollBy' in options)
|
||
{
|
||
// jump by value pixels
|
||
offset += parseInt(o.scrollBy);
|
||
}
|
||
else if ('destroy' in options)
|
||
{
|
||
// remove slimscroll elements
|
||
bar.remove();
|
||
rail.remove();
|
||
me.unwrap();
|
||
return;
|
||
}
|
||
|
||
// scroll content by the given offset
|
||
scrollContent(offset, false, true);
|
||
}
|
||
|
||
return;
|
||
}
|
||
else if ($.isPlainObject(options))
|
||
{
|
||
if ('destroy' in options)
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
|
||
// optionally set height to the parent's height
|
||
o.height = (o.height == 'auto') ? me.parent().height() : o.height;
|
||
|
||
// wrap content
|
||
var wrapper = $(divS)
|
||
.addClass(o.wrapperClass)
|
||
.css({
|
||
position: 'relative',
|
||
overflow: 'hidden',
|
||
width: o.width,
|
||
height: o.height
|
||
});
|
||
|
||
// update style for the div
|
||
me.css({
|
||
overflow: 'hidden',
|
||
width: o.width,
|
||
height: o.height
|
||
});
|
||
|
||
// create scrollbar rail
|
||
var rail = $(divS)
|
||
.addClass(o.railClass)
|
||
.css({
|
||
width: o.size,
|
||
height: '100%',
|
||
position: 'absolute',
|
||
top: 0,
|
||
display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none',
|
||
'border-radius': o.railBorderRadius,
|
||
background: o.railColor,
|
||
opacity: o.railOpacity,
|
||
zIndex: 90
|
||
});
|
||
|
||
// create scrollbar
|
||
var bar = $(divS)
|
||
.addClass(o.barClass)
|
||
.css({
|
||
background: o.color,
|
||
width: o.size,
|
||
position: 'absolute',
|
||
top: 0,
|
||
opacity: o.opacity,
|
||
display: o.alwaysVisible ? 'block' : 'none',
|
||
'border-radius' : o.borderRadius,
|
||
BorderRadius: o.borderRadius,
|
||
MozBorderRadius: o.borderRadius,
|
||
WebkitBorderRadius: o.borderRadius,
|
||
zIndex: 99
|
||
});
|
||
|
||
// set position
|
||
var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance };
|
||
rail.css(posCss);
|
||
bar.css(posCss);
|
||
|
||
// wrap it
|
||
me.wrap(wrapper);
|
||
|
||
// append to parent div
|
||
me.parent().append(bar);
|
||
me.parent().append(rail);
|
||
|
||
// make it draggable and no longer dependent on the jqueryUI
|
||
if (o.railDraggable){
|
||
bar.bind("mousedown", function(e) {
|
||
var $doc = $(document);
|
||
isDragg = true;
|
||
t = parseFloat(bar.css('top'));
|
||
pageY = e.pageY;
|
||
|
||
$doc.bind("mousemove.slimscroll", function(e){
|
||
currTop = t + e.pageY - pageY;
|
||
bar.css('top', currTop);
|
||
scrollContent(0, bar.position().top, false);// scroll content
|
||
});
|
||
|
||
$doc.bind("mouseup.slimscroll", function(e) {
|
||
isDragg = false;hideBar();
|
||
$doc.unbind('.slimscroll');
|
||
});
|
||
return false;
|
||
}).bind("selectstart.slimscroll", function(e){
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
return false;
|
||
});
|
||
}
|
||
|
||
// on rail over
|
||
rail.hover(function(){
|
||
showBar();
|
||
}, function(){
|
||
hideBar();
|
||
});
|
||
|
||
// on bar over
|
||
bar.hover(function(){
|
||
isOverBar = true;
|
||
}, function(){
|
||
isOverBar = false;
|
||
});
|
||
|
||
// show on parent mouseover
|
||
me.hover(function(){
|
||
isOverPanel = true;
|
||
showBar();
|
||
hideBar();
|
||
}, function(){
|
||
isOverPanel = false;
|
||
hideBar();
|
||
});
|
||
|
||
// support for mobile
|
||
me.bind('touchstart', function(e,b){
|
||
if (e.originalEvent.touches.length)
|
||
{
|
||
// record where touch started
|
||
touchDif = e.originalEvent.touches[0].pageY;
|
||
}
|
||
});
|
||
|
||
me.bind('touchmove', function(e){
|
||
// prevent scrolling the page if necessary
|
||
if(!releaseScroll)
|
||
{
|
||
e.originalEvent.preventDefault();
|
||
}
|
||
if (e.originalEvent.touches.length)
|
||
{
|
||
// see how far user swiped
|
||
var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep;
|
||
// scroll content
|
||
scrollContent(diff, true);
|
||
touchDif = e.originalEvent.touches[0].pageY;
|
||
}
|
||
});
|
||
|
||
// set up initial height
|
||
getBarHeight();
|
||
|
||
// check start position
|
||
if (o.start === 'bottom')
|
||
{
|
||
// scroll content to bottom
|
||
bar.css({ top: me.outerHeight() - bar.outerHeight() });
|
||
scrollContent(0, true);
|
||
}
|
||
else if (o.start !== 'top')
|
||
{
|
||
// assume jQuery selector
|
||
scrollContent($(o.start).position().top, null, true);
|
||
|
||
// make sure bar stays hidden
|
||
if (!o.alwaysVisible) { bar.hide(); }
|
||
}
|
||
|
||
// attach scroll events
|
||
attachWheel(this);
|
||
|
||
function _onWheel(e)
|
||
{
|
||
// use mouse wheel only when mouse is over
|
||
if (!isOverPanel) { return; }
|
||
|
||
var e = e || window.event;
|
||
|
||
var delta = 0;
|
||
if (e.wheelDelta) { delta = -e.wheelDelta/120; }
|
||
if (e.detail) { delta = e.detail / 3; }
|
||
|
||
var target = e.target || e.srcTarget || e.srcElement;
|
||
if ($(target).closest('.' + o.wrapperClass).is(me.parent())) {
|
||
// scroll content
|
||
scrollContent(delta, true);
|
||
}
|
||
|
||
// stop window scroll
|
||
if (e.preventDefault && !releaseScroll) { e.preventDefault(); }
|
||
if (!releaseScroll) { e.returnValue = false; }
|
||
}
|
||
|
||
function scrollContent(y, isWheel, isJump)
|
||
{
|
||
releaseScroll = false;
|
||
var delta = y;
|
||
var maxTop = me.outerHeight() - bar.outerHeight();
|
||
|
||
if (isWheel)
|
||
{
|
||
// move bar with mouse wheel
|
||
delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
|
||
|
||
// move bar, make sure it doesn't go out
|
||
delta = Math.min(Math.max(delta, 0), maxTop);
|
||
|
||
// if scrolling down, make sure a fractional change to the
|
||
// scroll position isn't rounded away when the scrollbar's CSS is set
|
||
// this flooring of delta would happened automatically when
|
||
// bar.css is set below, but we floor here for clarity
|
||
delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
|
||
|
||
// scroll the scrollbar
|
||
bar.css({ top: delta + 'px' });
|
||
}
|
||
|
||
// calculate actual scroll amount
|
||
percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight());
|
||
delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
|
||
|
||
if (isJump)
|
||
{
|
||
delta = y;
|
||
var offsetTop = delta / me[0].scrollHeight * me.outerHeight();
|
||
offsetTop = Math.min(Math.max(offsetTop, 0), maxTop);
|
||
bar.css({ top: offsetTop + 'px' });
|
||
}
|
||
|
||
// scroll content
|
||
me.scrollTop(delta);
|
||
|
||
// fire scrolling event
|
||
me.trigger('slimscrolling', ~~delta);
|
||
|
||
// ensure bar is visible
|
||
showBar();
|
||
|
||
// trigger hide when scroll is stopped
|
||
hideBar();
|
||
}
|
||
|
||
function attachWheel(target)
|
||
{
|
||
if (window.addEventListener)
|
||
{
|
||
target.addEventListener('DOMMouseScroll', _onWheel, false );
|
||
target.addEventListener('mousewheel', _onWheel, false );
|
||
}
|
||
else
|
||
{
|
||
document.attachEvent("onmousewheel", _onWheel)
|
||
}
|
||
}
|
||
|
||
function getBarHeight()
|
||
{
|
||
// calculate scrollbar height and make sure it is not too small
|
||
barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight);
|
||
bar.css({ height: barHeight + 'px' });
|
||
|
||
// hide scrollbar if content is not long enough
|
||
var display = barHeight == me.outerHeight() ? 'none' : 'block';
|
||
bar.css({ display: display });
|
||
}
|
||
|
||
function showBar()
|
||
{
|
||
// recalculate bar height
|
||
getBarHeight();
|
||
clearTimeout(queueHide);
|
||
|
||
// when bar reached top or bottom
|
||
if (percentScroll == ~~percentScroll)
|
||
{
|
||
//release wheel
|
||
releaseScroll = o.allowPageScroll;
|
||
|
||
// publish approporiate event
|
||
if (lastScroll != percentScroll)
|
||
{
|
||
var msg = (~~percentScroll == 0) ? 'top' : 'bottom';
|
||
me.trigger('slimscroll', msg);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
releaseScroll = false;
|
||
}
|
||
lastScroll = percentScroll;
|
||
|
||
// show only when required
|
||
if(barHeight >= me.outerHeight()) {
|
||
//allow window scroll
|
||
releaseScroll = true;
|
||
return;
|
||
}
|
||
bar.stop(true,true).fadeIn('fast');
|
||
if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); }
|
||
}
|
||
|
||
function hideBar()
|
||
{
|
||
// only hide when options allow it
|
||
if (!o.alwaysVisible)
|
||
{
|
||
queueHide = setTimeout(function(){
|
||
if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg)
|
||
{
|
||
bar.fadeOut('slow');
|
||
rail.fadeOut('slow');
|
||
}
|
||
}, 1000);
|
||
}
|
||
}
|
||
|
||
});
|
||
|
||
// maintain chainability
|
||
return this;
|
||
}
|
||
});
|
||
|
||
$.fn.extend({
|
||
slimscroll: $.fn.slimScroll
|
||
});
|
||
|
||
})(jQuery);
|
||
|
||
// This [jQuery](https://jquery.com/) plugin implements an `<iframe>`
|
||
// [transport](https://api.jquery.com/jQuery.ajax/#extending-ajax) 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](https://github.com/cmlenz/jquery-iframe-transport)
|
||
// is available on [Github](https://github.com/) and licensed under the [MIT
|
||
// license](https://github.com/cmlenz/jquery-iframe-transport/blob/master/LICENSE).
|
||
|
||
// ## 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 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
|
||
|
||
(function($, undefined) {
|
||
"use strict";
|
||
|
||
// 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) {
|
||
options.originalURL = options.url;
|
||
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"),
|
||
markers = null,
|
||
accepts = 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() {
|
||
files.each(function(i, file) {
|
||
var $file = $(file);
|
||
$file.data("clone").replaceWith($file);
|
||
});
|
||
form.remove();
|
||
iframe.one("load", function() { iframe.remove(); });
|
||
iframe.attr("src", "javascript:false;");
|
||
}
|
||
|
||
// 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();
|
||
|
||
// Use the data from the original AJAX options, as it doesn't seem to be
|
||
// copied over since jQuery 1.7.
|
||
// See https://github.com/cmlenz/jquery-iframe-transport/issues/6
|
||
options.data = origOptions.data;
|
||
|
||
if (files.length) {
|
||
form = $("<form enctype='multipart/form-data' method='post'></form>").
|
||
hide().attr({action: options.originalURL, 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;
|
||
}
|
||
$("<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);
|
||
|
||
// Borrowed straight from the JQuery source.
|
||
// Provides a way of specifying the accepted data type similar to the
|
||
// HTTP "Accept" header
|
||
if (options.dataTypes[0] && options.accepts[options.dataTypes[0]]) {
|
||
accepts = options.accepts[options.dataTypes[0]] +
|
||
(options.dataTypes[0] !== "*" ? ", */*; q=0.01" : "");
|
||
} else {
|
||
accepts = options.accepts["*"];
|
||
}
|
||
$("<input type='hidden' name='X-HTTP-Accept'>").
|
||
attr("value", accepts).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.
|
||
markers = files.after(function(idx) {
|
||
var $this = $(this),
|
||
$clone = $this.clone().prop("disabled", true);
|
||
$this.data("clone", $clone);
|
||
return $clone;
|
||
}).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.one("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.one("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();
|
||
}
|
||
}
|
||
|
||
};
|
||
}
|
||
});
|
||
|
||
})(jQuery);
|
||
|
||
/*
|
||
* jQuery File Upload Plugin
|
||
* https://github.com/blueimp/jQuery-File-Upload
|
||
*
|
||
* Copyright 2010, Sebastian Tschan
|
||
* https://blueimp.net
|
||
*
|
||
* Licensed under the MIT license:
|
||
* https://opensource.org/licenses/MIT
|
||
*/
|
||
|
||
/* jshint nomen:false */
|
||
/* global define, require, window, document, location, Blob, FormData */
|
||
|
||
;(function (factory) {
|
||
'use strict';
|
||
if (typeof define === 'function' && define.amd) {
|
||
// Register as an anonymous AMD module:
|
||
define([
|
||
'jquery',
|
||
'jquery-ui/ui/widget'
|
||
], factory);
|
||
} else if (typeof exports === 'object') {
|
||
// Node/CommonJS:
|
||
factory(
|
||
require('jquery'),
|
||
require('./vendor/jquery.ui.widget')
|
||
);
|
||
} else {
|
||
// Browser globals:
|
||
factory(window.jQuery);
|
||
}
|
||
}(function ($) {
|
||
'use strict';
|
||
|
||
// Detect file input support, based on
|
||
// http://viljamis.com/blog/2012/file-upload-support-on-mobile/
|
||
$.support.fileInput = !(new RegExp(
|
||
// Handle devices which give false positives for the feature detection:
|
||
'(Android (1\\.[0156]|2\\.[01]))' +
|
||
'|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' +
|
||
'|(w(eb)?OSBrowser)|(webOS)' +
|
||
'|(Kindle/(1\\.0|2\\.[05]|3\\.0))'
|
||
).test(window.navigator.userAgent) ||
|
||
// Feature detection for all other devices:
|
||
$('<input type="file"/>').prop('disabled'));
|
||
|
||
// The FileReader API is not actually used, but works as feature detection,
|
||
// as some Safari versions (5?) support XHR file uploads via the FormData API,
|
||
// but not non-multipart XHR file uploads.
|
||
// window.XMLHttpRequestUpload is not available on IE10, so we check for
|
||
// window.ProgressEvent instead to detect XHR2 file upload capability:
|
||
$.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader);
|
||
$.support.xhrFormDataFileUpload = !!window.FormData;
|
||
|
||
// Detect support for Blob slicing (required for chunked uploads):
|
||
$.support.blobSlice = window.Blob && (Blob.prototype.slice ||
|
||
Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
|
||
|
||
// Helper function to create drag handlers for dragover/dragenter/dragleave:
|
||
function getDragHandler(type) {
|
||
var isDragOver = type === 'dragover';
|
||
return function (e) {
|
||
e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
|
||
var dataTransfer = e.dataTransfer;
|
||
if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
|
||
this._trigger(
|
||
type,
|
||
$.Event(type, {delegatedEvent: e})
|
||
) !== false) {
|
||
e.preventDefault();
|
||
if (isDragOver) {
|
||
dataTransfer.dropEffect = 'copy';
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
// The fileupload widget listens for change events on file input fields defined
|
||
// via fileInput setting and paste or drop events of the given dropZone.
|
||
// In addition to the default jQuery Widget methods, the fileupload widget
|
||
// exposes the "add" and "send" methods, to add or directly send files using
|
||
// the fileupload API.
|
||
// By default, files added via file input selection, paste, drag & drop or
|
||
// "add" method are uploaded immediately, but it is possible to override
|
||
// the "add" callback option to queue file uploads.
|
||
$.widget('blueimp.fileupload', {
|
||
|
||
options: {
|
||
// The drop target element(s), by the default the complete document.
|
||
// Set to null to disable drag & drop support:
|
||
dropZone: $(document),
|
||
// The paste target element(s), by the default undefined.
|
||
// Set to a DOM node or jQuery object to enable file pasting:
|
||
pasteZone: undefined,
|
||
// The file input field(s), that are listened to for change events.
|
||
// If undefined, it is set to the file input fields inside
|
||
// of the widget element on plugin initialization.
|
||
// Set to null to disable the change listener.
|
||
fileInput: undefined,
|
||
// By default, the file input field is replaced with a clone after
|
||
// each input field change event. This is required for iframe transport
|
||
// queues and allows change events to be fired for the same file
|
||
// selection, but can be disabled by setting the following option to false:
|
||
replaceFileInput: true,
|
||
// The parameter name for the file form data (the request argument name).
|
||
// If undefined or empty, the name property of the file input field is
|
||
// used, or "files[]" if the file input name property is also empty,
|
||
// can be a string or an array of strings:
|
||
paramName: undefined,
|
||
// By default, each file of a selection is uploaded using an individual
|
||
// request for XHR type uploads. Set to false to upload file
|
||
// selections in one request each:
|
||
singleFileUploads: true,
|
||
// To limit the number of files uploaded with one XHR request,
|
||
// set the following option to an integer greater than 0:
|
||
limitMultiFileUploads: undefined,
|
||
// The following option limits the number of files uploaded with one
|
||
// XHR request to keep the request size under or equal to the defined
|
||
// limit in bytes:
|
||
limitMultiFileUploadSize: undefined,
|
||
// Multipart file uploads add a number of bytes to each uploaded file,
|
||
// therefore the following option adds an overhead for each file used
|
||
// in the limitMultiFileUploadSize configuration:
|
||
limitMultiFileUploadSizeOverhead: 512,
|
||
// Set the following option to true to issue all file upload requests
|
||
// in a sequential order:
|
||
sequentialUploads: false,
|
||
// To limit the number of concurrent uploads,
|
||
// set the following option to an integer greater than 0:
|
||
limitConcurrentUploads: undefined,
|
||
// Set the following option to true to force iframe transport uploads:
|
||
forceIframeTransport: false,
|
||
// Set the following option to the location of a redirect url on the
|
||
// origin server, for cross-domain iframe transport uploads:
|
||
redirect: undefined,
|
||
// The parameter name for the redirect url, sent as part of the form
|
||
// data and set to 'redirect' if this option is empty:
|
||
redirectParamName: undefined,
|
||
// Set the following option to the location of a postMessage window,
|
||
// to enable postMessage transport uploads:
|
||
postMessage: undefined,
|
||
// By default, XHR file uploads are sent as multipart/form-data.
|
||
// The iframe transport is always using multipart/form-data.
|
||
// Set to false to enable non-multipart XHR uploads:
|
||
multipart: true,
|
||
// To upload large files in smaller chunks, set the following option
|
||
// to a preferred maximum chunk size. If set to 0, null or undefined,
|
||
// or the browser does not support the required Blob API, files will
|
||
// be uploaded as a whole.
|
||
maxChunkSize: undefined,
|
||
// When a non-multipart upload or a chunked multipart upload has been
|
||
// aborted, this option can be used to resume the upload by setting
|
||
// it to the size of the already uploaded bytes. This option is most
|
||
// useful when modifying the options object inside of the "add" or
|
||
// "send" callbacks, as the options are cloned for each file upload.
|
||
uploadedBytes: undefined,
|
||
// By default, failed (abort or error) file uploads are removed from the
|
||
// global progress calculation. Set the following option to false to
|
||
// prevent recalculating the global progress data:
|
||
recalculateProgress: true,
|
||
// Interval in milliseconds to calculate and trigger progress events:
|
||
progressInterval: 100,
|
||
// Interval in milliseconds to calculate progress bitrate:
|
||
bitrateInterval: 500,
|
||
// By default, uploads are started automatically when adding files:
|
||
autoUpload: true,
|
||
// By default, duplicate file names are expected to be handled on
|
||
// the server-side. If this is not possible (e.g. when uploading
|
||
// files directly to Amazon S3), the following option can be set to
|
||
// an empty object or an object mapping existing filenames, e.g.:
|
||
// { "image.jpg": true, "image (1).jpg": true }
|
||
// If it is set, all files will be uploaded with unique filenames,
|
||
// adding increasing number suffixes if necessary, e.g.:
|
||
// "image (2).jpg"
|
||
uniqueFilenames: undefined,
|
||
|
||
// Error and info messages:
|
||
messages: {
|
||
uploadedBytes: 'Uploaded bytes exceed file size'
|
||
},
|
||
|
||
// Translation function, gets the message key to be translated
|
||
// and an object with context specific data as arguments:
|
||
i18n: function (message, context) {
|
||
message = this.messages[message] || message.toString();
|
||
if (context) {
|
||
$.each(context, function (key, value) {
|
||
message = message.replace('{' + key + '}', value);
|
||
});
|
||
}
|
||
return message;
|
||
},
|
||
|
||
// Additional form data to be sent along with the file uploads can be set
|
||
// using this option, which accepts an array of objects with name and
|
||
// value properties, a function returning such an array, a FormData
|
||
// object (for XHR file uploads), or a simple object.
|
||
// The form of the first fileInput is given as parameter to the function:
|
||
formData: function (form) {
|
||
return form.serializeArray();
|
||
},
|
||
|
||
// The add callback is invoked as soon as files are added to the fileupload
|
||
// widget (via file input selection, drag & drop, paste or add API call).
|
||
// If the singleFileUploads option is enabled, this callback will be
|
||
// called once for each file in the selection for XHR file uploads, else
|
||
// once for each file selection.
|
||
//
|
||
// The upload starts when the submit method is invoked on the data parameter.
|
||
// The data object contains a files property holding the added files
|
||
// and allows you to override plugin options as well as define ajax settings.
|
||
//
|
||
// Listeners for this callback can also be bound the following way:
|
||
// .bind('fileuploadadd', func);
|
||
//
|
||
// data.submit() returns a Promise object and allows to attach additional
|
||
// handlers using jQuery's Deferred callbacks:
|
||
// data.submit().done(func).fail(func).always(func);
|
||
add: function (e, data) {
|
||
if (e.isDefaultPrevented()) {
|
||
return false;
|
||
}
|
||
if (data.autoUpload || (data.autoUpload !== false &&
|
||
$(this).fileupload('option', 'autoUpload'))) {
|
||
data.process().done(function () {
|
||
data.submit();
|
||
});
|
||
}
|
||
},
|
||
|
||
// Other callbacks:
|
||
|
||
// Callback for the submit event of each file upload:
|
||
// submit: function (e, data) {}, // .bind('fileuploadsubmit', func);
|
||
|
||
// Callback for the start of each file upload request:
|
||
// send: function (e, data) {}, // .bind('fileuploadsend', func);
|
||
|
||
// Callback for successful uploads:
|
||
// done: function (e, data) {}, // .bind('fileuploaddone', func);
|
||
|
||
// Callback for failed (abort or error) uploads:
|
||
// fail: function (e, data) {}, // .bind('fileuploadfail', func);
|
||
|
||
// Callback for completed (success, abort or error) requests:
|
||
// always: function (e, data) {}, // .bind('fileuploadalways', func);
|
||
|
||
// Callback for upload progress events:
|
||
// progress: function (e, data) {}, // .bind('fileuploadprogress', func);
|
||
|
||
// Callback for global upload progress events:
|
||
// progressall: function (e, data) {}, // .bind('fileuploadprogressall', func);
|
||
|
||
// Callback for uploads start, equivalent to the global ajaxStart event:
|
||
// start: function (e) {}, // .bind('fileuploadstart', func);
|
||
|
||
// Callback for uploads stop, equivalent to the global ajaxStop event:
|
||
// stop: function (e) {}, // .bind('fileuploadstop', func);
|
||
|
||
// Callback for change events of the fileInput(s):
|
||
// change: function (e, data) {}, // .bind('fileuploadchange', func);
|
||
|
||
// Callback for paste events to the pasteZone(s):
|
||
// paste: function (e, data) {}, // .bind('fileuploadpaste', func);
|
||
|
||
// Callback for drop events of the dropZone(s):
|
||
// drop: function (e, data) {}, // .bind('fileuploaddrop', func);
|
||
|
||
// Callback for dragover events of the dropZone(s):
|
||
// dragover: function (e) {}, // .bind('fileuploaddragover', func);
|
||
|
||
// Callback before the start of each chunk upload request (before form data initialization):
|
||
// chunkbeforesend: function (e, data) {}, // .bind('fileuploadchunkbeforesend', func);
|
||
|
||
// Callback for the start of each chunk upload request:
|
||
// chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func);
|
||
|
||
// Callback for successful chunk uploads:
|
||
// chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func);
|
||
|
||
// Callback for failed (abort or error) chunk uploads:
|
||
// chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func);
|
||
|
||
// Callback for completed (success, abort or error) chunk upload requests:
|
||
// chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func);
|
||
|
||
// The plugin options are used as settings object for the ajax calls.
|
||
// The following are jQuery ajax settings required for the file uploads:
|
||
processData: false,
|
||
contentType: false,
|
||
cache: false,
|
||
timeout: 0
|
||
},
|
||
|
||
// A list of options that require reinitializing event listeners and/or
|
||
// special initialization code:
|
||
_specialOptions: [
|
||
'fileInput',
|
||
'dropZone',
|
||
'pasteZone',
|
||
'multipart',
|
||
'forceIframeTransport'
|
||
],
|
||
|
||
_blobSlice: $.support.blobSlice && function () {
|
||
var slice = this.slice || this.webkitSlice || this.mozSlice;
|
||
return slice.apply(this, arguments);
|
||
},
|
||
|
||
_BitrateTimer: function () {
|
||
this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime());
|
||
this.loaded = 0;
|
||
this.bitrate = 0;
|
||
this.getBitrate = function (now, loaded, interval) {
|
||
var timeDiff = now - this.timestamp;
|
||
if (!this.bitrate || !interval || timeDiff > interval) {
|
||
this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
|
||
this.loaded = loaded;
|
||
this.timestamp = now;
|
||
}
|
||
return this.bitrate;
|
||
};
|
||
},
|
||
|
||
_isXHRUpload: function (options) {
|
||
return !options.forceIframeTransport &&
|
||
((!options.multipart && $.support.xhrFileUpload) ||
|
||
$.support.xhrFormDataFileUpload);
|
||
},
|
||
|
||
_getFormData: function (options) {
|
||
var formData;
|
||
if ($.type(options.formData) === 'function') {
|
||
return options.formData(options.form);
|
||
}
|
||
if ($.isArray(options.formData)) {
|
||
return options.formData;
|
||
}
|
||
if ($.type(options.formData) === 'object') {
|
||
formData = [];
|
||
$.each(options.formData, function (name, value) {
|
||
formData.push({name: name, value: value});
|
||
});
|
||
return formData;
|
||
}
|
||
return [];
|
||
},
|
||
|
||
_getTotal: function (files) {
|
||
var total = 0;
|
||
$.each(files, function (index, file) {
|
||
total += file.size || 1;
|
||
});
|
||
return total;
|
||
},
|
||
|
||
_initProgressObject: function (obj) {
|
||
var progress = {
|
||
loaded: 0,
|
||
total: 0,
|
||
bitrate: 0
|
||
};
|
||
if (obj._progress) {
|
||
$.extend(obj._progress, progress);
|
||
} else {
|
||
obj._progress = progress;
|
||
}
|
||
},
|
||
|
||
_initResponseObject: function (obj) {
|
||
var prop;
|
||
if (obj._response) {
|
||
for (prop in obj._response) {
|
||
if (obj._response.hasOwnProperty(prop)) {
|
||
delete obj._response[prop];
|
||
}
|
||
}
|
||
} else {
|
||
obj._response = {};
|
||
}
|
||
},
|
||
|
||
_onProgress: function (e, data) {
|
||
if (e.lengthComputable) {
|
||
var now = ((Date.now) ? Date.now() : (new Date()).getTime()),
|
||
loaded;
|
||
if (data._time && data.progressInterval &&
|
||
(now - data._time < data.progressInterval) &&
|
||
e.loaded !== e.total) {
|
||
return;
|
||
}
|
||
data._time = now;
|
||
loaded = Math.floor(
|
||
e.loaded / e.total * (data.chunkSize || data._progress.total)
|
||
) + (data.uploadedBytes || 0);
|
||
// Add the difference from the previously loaded state
|
||
// to the global loaded counter:
|
||
this._progress.loaded += (loaded - data._progress.loaded);
|
||
this._progress.bitrate = this._bitrateTimer.getBitrate(
|
||
now,
|
||
this._progress.loaded,
|
||
data.bitrateInterval
|
||
);
|
||
data._progress.loaded = data.loaded = loaded;
|
||
data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate(
|
||
now,
|
||
loaded,
|
||
data.bitrateInterval
|
||
);
|
||
// Trigger a custom progress event with a total data property set
|
||
// to the file size(s) of the current upload and a loaded data
|
||
// property calculated accordingly:
|
||
this._trigger(
|
||
'progress',
|
||
$.Event('progress', {delegatedEvent: e}),
|
||
data
|
||
);
|
||
// Trigger a global progress event for all current file uploads,
|
||
// including ajax calls queued for sequential file uploads:
|
||
this._trigger(
|
||
'progressall',
|
||
$.Event('progressall', {delegatedEvent: e}),
|
||
this._progress
|
||
);
|
||
}
|
||
},
|
||
|
||
_initProgressListener: function (options) {
|
||
var that = this,
|
||
xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
|
||
// Accesss to the native XHR object is required to add event listeners
|
||
// for the upload progress event:
|
||
if (xhr.upload) {
|
||
$(xhr.upload).bind('progress', function (e) {
|
||
var oe = e.originalEvent;
|
||
// Make sure the progress event properties get copied over:
|
||
e.lengthComputable = oe.lengthComputable;
|
||
e.loaded = oe.loaded;
|
||
e.total = oe.total;
|
||
that._onProgress(e, options);
|
||
});
|
||
options.xhr = function () {
|
||
return xhr;
|
||
};
|
||
}
|
||
},
|
||
|
||
_deinitProgressListener: function (options) {
|
||
var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
|
||
if (xhr.upload) {
|
||
$(xhr.upload).unbind('progress');
|
||
}
|
||
},
|
||
|
||
_isInstanceOf: function (type, obj) {
|
||
// Cross-frame instanceof check
|
||
return Object.prototype.toString.call(obj) === '[object ' + type + ']';
|
||
},
|
||
|
||
_getUniqueFilename: function (name, map) {
|
||
name = String(name);
|
||
if (map[name]) {
|
||
name = name.replace(
|
||
/(?: \(([\d]+)\))?(\.[^.]+)?$/,
|
||
function (_, p1, p2) {
|
||
var index = p1 ? Number(p1) + 1 : 1;
|
||
var ext = p2 || '';
|
||
return ' (' + index + ')' + ext;
|
||
}
|
||
);
|
||
return this._getUniqueFilename(name, map);
|
||
}
|
||
map[name] = true;
|
||
return name;
|
||
},
|
||
|
||
_initXHRData: function (options) {
|
||
var that = this,
|
||
formData,
|
||
file = options.files[0],
|
||
// Ignore non-multipart setting if not supported:
|
||
multipart = options.multipart || !$.support.xhrFileUpload,
|
||
paramName = $.type(options.paramName) === 'array' ?
|
||
options.paramName[0] : options.paramName;
|
||
options.headers = $.extend({}, options.headers);
|
||
if (options.contentRange) {
|
||
options.headers['Content-Range'] = options.contentRange;
|
||
}
|
||
if (!multipart || options.blob || !this._isInstanceOf('File', file)) {
|
||
options.headers['Content-Disposition'] = 'attachment; filename="' +
|
||
encodeURI(file.uploadName || file.name) + '"';
|
||
}
|
||
if (!multipart) {
|
||
options.contentType = file.type || 'application/octet-stream';
|
||
options.data = options.blob || file;
|
||
} else if ($.support.xhrFormDataFileUpload) {
|
||
if (options.postMessage) {
|
||
// window.postMessage does not allow sending FormData
|
||
// objects, so we just add the File/Blob objects to
|
||
// the formData array and let the postMessage window
|
||
// create the FormData object out of this array:
|
||
formData = this._getFormData(options);
|
||
if (options.blob) {
|
||
formData.push({
|
||
name: paramName,
|
||
value: options.blob
|
||
});
|
||
} else {
|
||
$.each(options.files, function (index, file) {
|
||
formData.push({
|
||
name: ($.type(options.paramName) === 'array' &&
|
||
options.paramName[index]) || paramName,
|
||
value: file
|
||
});
|
||
});
|
||
}
|
||
} else {
|
||
if (that._isInstanceOf('FormData', options.formData)) {
|
||
formData = options.formData;
|
||
} else {
|
||
formData = new FormData();
|
||
$.each(this._getFormData(options), function (index, field) {
|
||
formData.append(field.name, field.value);
|
||
});
|
||
}
|
||
if (options.blob) {
|
||
formData.append(
|
||
paramName,
|
||
options.blob,
|
||
file.uploadName || file.name
|
||
);
|
||
} else {
|
||
$.each(options.files, function (index, file) {
|
||
// This check allows the tests to run with
|
||
// dummy objects:
|
||
if (that._isInstanceOf('File', file) ||
|
||
that._isInstanceOf('Blob', file)) {
|
||
var fileName = file.uploadName || file.name;
|
||
if (options.uniqueFilenames) {
|
||
fileName = that._getUniqueFilename(
|
||
fileName,
|
||
options.uniqueFilenames
|
||
);
|
||
}
|
||
formData.append(
|
||
($.type(options.paramName) === 'array' &&
|
||
options.paramName[index]) || paramName,
|
||
file,
|
||
fileName
|
||
);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
options.data = formData;
|
||
}
|
||
// Blob reference is not needed anymore, free memory:
|
||
options.blob = null;
|
||
},
|
||
|
||
_initIframeSettings: function (options) {
|
||
var targetHost = $('<a></a>').prop('href', options.url).prop('host');
|
||
// Setting the dataType to iframe enables the iframe transport:
|
||
options.dataType = 'iframe ' + (options.dataType || '');
|
||
// The iframe transport accepts a serialized array as form data:
|
||
options.formData = this._getFormData(options);
|
||
// Add redirect url to form data on cross-domain uploads:
|
||
if (options.redirect && targetHost && targetHost !== location.host) {
|
||
options.formData.push({
|
||
name: options.redirectParamName || 'redirect',
|
||
value: options.redirect
|
||
});
|
||
}
|
||
},
|
||
|
||
_initDataSettings: function (options) {
|
||
if (this._isXHRUpload(options)) {
|
||
if (!this._chunkedUpload(options, true)) {
|
||
if (!options.data) {
|
||
this._initXHRData(options);
|
||
}
|
||
this._initProgressListener(options);
|
||
}
|
||
if (options.postMessage) {
|
||
// Setting the dataType to postmessage enables the
|
||
// postMessage transport:
|
||
options.dataType = 'postmessage ' + (options.dataType || '');
|
||
}
|
||
} else {
|
||
this._initIframeSettings(options);
|
||
}
|
||
},
|
||
|
||
_getParamName: function (options) {
|
||
var fileInput = $(options.fileInput),
|
||
paramName = options.paramName;
|
||
if (!paramName) {
|
||
paramName = [];
|
||
fileInput.each(function () {
|
||
var input = $(this),
|
||
name = input.prop('name') || 'files[]',
|
||
i = (input.prop('files') || [1]).length;
|
||
while (i) {
|
||
paramName.push(name);
|
||
i -= 1;
|
||
}
|
||
});
|
||
if (!paramName.length) {
|
||
paramName = [fileInput.prop('name') || 'files[]'];
|
||
}
|
||
} else if (!$.isArray(paramName)) {
|
||
paramName = [paramName];
|
||
}
|
||
return paramName;
|
||
},
|
||
|
||
_initFormSettings: function (options) {
|
||
// Retrieve missing options from the input field and the
|
||
// associated form, if available:
|
||
if (!options.form || !options.form.length) {
|
||
options.form = $(options.fileInput.prop('form'));
|
||
// If the given file input doesn't have an associated form,
|
||
// use the default widget file input's form:
|
||
if (!options.form.length) {
|
||
options.form = $(this.options.fileInput.prop('form'));
|
||
}
|
||
}
|
||
options.paramName = this._getParamName(options);
|
||
if (!options.url) {
|
||
options.url = options.form.prop('action') || location.href;
|
||
}
|
||
// The HTTP request method must be "POST" or "PUT":
|
||
options.type = (options.type ||
|
||
($.type(options.form.prop('method')) === 'string' &&
|
||
options.form.prop('method')) || ''
|
||
).toUpperCase();
|
||
if (options.type !== 'POST' && options.type !== 'PUT' &&
|
||
options.type !== 'PATCH') {
|
||
options.type = 'POST';
|
||
}
|
||
if (!options.formAcceptCharset) {
|
||
options.formAcceptCharset = options.form.attr('accept-charset');
|
||
}
|
||
},
|
||
|
||
_getAJAXSettings: function (data) {
|
||
var options = $.extend({}, this.options, data);
|
||
this._initFormSettings(options);
|
||
this._initDataSettings(options);
|
||
return options;
|
||
},
|
||
|
||
// jQuery 1.6 doesn't provide .state(),
|
||
// while jQuery 1.8+ removed .isRejected() and .isResolved():
|
||
_getDeferredState: function (deferred) {
|
||
if (deferred.state) {
|
||
return deferred.state();
|
||
}
|
||
if (deferred.isResolved()) {
|
||
return 'resolved';
|
||
}
|
||
if (deferred.isRejected()) {
|
||
return 'rejected';
|
||
}
|
||
return 'pending';
|
||
},
|
||
|
||
// Maps jqXHR callbacks to the equivalent
|
||
// methods of the given Promise object:
|
||
_enhancePromise: function (promise) {
|
||
promise.success = promise.done;
|
||
promise.error = promise.fail;
|
||
promise.complete = promise.always;
|
||
return promise;
|
||
},
|
||
|
||
// Creates and returns a Promise object enhanced with
|
||
// the jqXHR methods abort, success, error and complete:
|
||
_getXHRPromise: function (resolveOrReject, context, args) {
|
||
var dfd = $.Deferred(),
|
||
promise = dfd.promise();
|
||
context = context || this.options.context || promise;
|
||
if (resolveOrReject === true) {
|
||
dfd.resolveWith(context, args);
|
||
} else if (resolveOrReject === false) {
|
||
dfd.rejectWith(context, args);
|
||
}
|
||
promise.abort = dfd.promise;
|
||
return this._enhancePromise(promise);
|
||
},
|
||
|
||
// Adds convenience methods to the data callback argument:
|
||
_addConvenienceMethods: function (e, data) {
|
||
var that = this,
|
||
getPromise = function (args) {
|
||
return $.Deferred().resolveWith(that, args).promise();
|
||
};
|
||
data.process = function (resolveFunc, rejectFunc) {
|
||
if (resolveFunc || rejectFunc) {
|
||
data._processQueue = this._processQueue =
|
||
(this._processQueue || getPromise([this])).then(
|
||
function () {
|
||
if (data.errorThrown) {
|
||
return $.Deferred()
|
||
.rejectWith(that, [data]).promise();
|
||
}
|
||
return getPromise(arguments);
|
||
}
|
||
).then(resolveFunc, rejectFunc);
|
||
}
|
||
return this._processQueue || getPromise([this]);
|
||
};
|
||
data.submit = function () {
|
||
if (this.state() !== 'pending') {
|
||
data.jqXHR = this.jqXHR =
|
||
(that._trigger(
|
||
'submit',
|
||
$.Event('submit', {delegatedEvent: e}),
|
||
this
|
||
) !== false) && that._onSend(e, this);
|
||
}
|
||
return this.jqXHR || that._getXHRPromise();
|
||
};
|
||
data.abort = function () {
|
||
if (this.jqXHR) {
|
||
return this.jqXHR.abort();
|
||
}
|
||
this.errorThrown = 'abort';
|
||
that._trigger('fail', null, this);
|
||
return that._getXHRPromise(false);
|
||
};
|
||
data.state = function () {
|
||
if (this.jqXHR) {
|
||
return that._getDeferredState(this.jqXHR);
|
||
}
|
||
if (this._processQueue) {
|
||
return that._getDeferredState(this._processQueue);
|
||
}
|
||
};
|
||
data.processing = function () {
|
||
return !this.jqXHR && this._processQueue && that
|
||
._getDeferredState(this._processQueue) === 'pending';
|
||
};
|
||
data.progress = function () {
|
||
return this._progress;
|
||
};
|
||
data.response = function () {
|
||
return this._response;
|
||
};
|
||
},
|
||
|
||
// Parses the Range header from the server response
|
||
// and returns the uploaded bytes:
|
||
_getUploadedBytes: function (jqXHR) {
|
||
var range = jqXHR.getResponseHeader('Range'),
|
||
parts = range && range.split('-'),
|
||
upperBytesPos = parts && parts.length > 1 &&
|
||
parseInt(parts[1], 10);
|
||
return upperBytesPos && upperBytesPos + 1;
|
||
},
|
||
|
||
// Uploads a file in multiple, sequential requests
|
||
// by splitting the file up in multiple blob chunks.
|
||
// If the second parameter is true, only tests if the file
|
||
// should be uploaded in chunks, but does not invoke any
|
||
// upload requests:
|
||
_chunkedUpload: function (options, testOnly) {
|
||
options.uploadedBytes = options.uploadedBytes || 0;
|
||
var that = this,
|
||
file = options.files[0],
|
||
fs = file.size,
|
||
ub = options.uploadedBytes,
|
||
mcs = options.maxChunkSize || fs,
|
||
slice = this._blobSlice,
|
||
dfd = $.Deferred(),
|
||
promise = dfd.promise(),
|
||
jqXHR,
|
||
upload;
|
||
if (!(this._isXHRUpload(options) && slice && (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)) ||
|
||
options.data) {
|
||
return false;
|
||
}
|
||
if (testOnly) {
|
||
return true;
|
||
}
|
||
if (ub >= fs) {
|
||
file.error = options.i18n('uploadedBytes');
|
||
return this._getXHRPromise(
|
||
false,
|
||
options.context,
|
||
[null, 'error', file.error]
|
||
);
|
||
}
|
||
// The chunk upload method:
|
||
upload = function () {
|
||
// Clone the options object for each chunk upload:
|
||
var o = $.extend({}, options),
|
||
currentLoaded = o._progress.loaded;
|
||
o.blob = slice.call(
|
||
file,
|
||
ub,
|
||
ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),
|
||
file.type
|
||
);
|
||
// Store the current chunk size, as the blob itself
|
||
// will be dereferenced after data processing:
|
||
o.chunkSize = o.blob.size;
|
||
// Expose the chunk bytes position range:
|
||
o.contentRange = 'bytes ' + ub + '-' +
|
||
(ub + o.chunkSize - 1) + '/' + fs;
|
||
// Trigger chunkbeforesend to allow form data to be updated for this chunk
|
||
that._trigger('chunkbeforesend', null, o);
|
||
// Process the upload data (the blob and potential form data):
|
||
that._initXHRData(o);
|
||
// Add progress listeners for this chunk upload:
|
||
that._initProgressListener(o);
|
||
jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) ||
|
||
that._getXHRPromise(false, o.context))
|
||
.done(function (result, textStatus, jqXHR) {
|
||
ub = that._getUploadedBytes(jqXHR) ||
|
||
(ub + o.chunkSize);
|
||
// Create a progress event if no final progress event
|
||
// with loaded equaling total has been triggered
|
||
// for this chunk:
|
||
if (currentLoaded + o.chunkSize - o._progress.loaded) {
|
||
that._onProgress($.Event('progress', {
|
||
lengthComputable: true,
|
||
loaded: ub - o.uploadedBytes,
|
||
total: ub - o.uploadedBytes
|
||
}), o);
|
||
}
|
||
options.uploadedBytes = o.uploadedBytes = ub;
|
||
o.result = result;
|
||
o.textStatus = textStatus;
|
||
o.jqXHR = jqXHR;
|
||
that._trigger('chunkdone', null, o);
|
||
that._trigger('chunkalways', null, o);
|
||
if (ub < fs) {
|
||
// File upload not yet complete,
|
||
// continue with the next chunk:
|
||
upload();
|
||
} else {
|
||
dfd.resolveWith(
|
||
o.context,
|
||
[result, textStatus, jqXHR]
|
||
);
|
||
}
|
||
})
|
||
.fail(function (jqXHR, textStatus, errorThrown) {
|
||
o.jqXHR = jqXHR;
|
||
o.textStatus = textStatus;
|
||
o.errorThrown = errorThrown;
|
||
that._trigger('chunkfail', null, o);
|
||
that._trigger('chunkalways', null, o);
|
||
dfd.rejectWith(
|
||
o.context,
|
||
[jqXHR, textStatus, errorThrown]
|
||
);
|
||
})
|
||
.always(function () {
|
||
that._deinitProgressListener(o);
|
||
});
|
||
};
|
||
this._enhancePromise(promise);
|
||
promise.abort = function () {
|
||
return jqXHR.abort();
|
||
};
|
||
upload();
|
||
return promise;
|
||
},
|
||
|
||
_beforeSend: function (e, data) {
|
||
if (this._active === 0) {
|
||
// the start callback is triggered when an upload starts
|
||
// and no other uploads are currently running,
|
||
// equivalent to the global ajaxStart event:
|
||
this._trigger('start');
|
||
// Set timer for global bitrate progress calculation:
|
||
this._bitrateTimer = new this._BitrateTimer();
|
||
// Reset the global progress values:
|
||
this._progress.loaded = this._progress.total = 0;
|
||
this._progress.bitrate = 0;
|
||
}
|
||
// Make sure the container objects for the .response() and
|
||
// .progress() methods on the data object are available
|
||
// and reset to their initial state:
|
||
this._initResponseObject(data);
|
||
this._initProgressObject(data);
|
||
data._progress.loaded = data.loaded = data.uploadedBytes || 0;
|
||
data._progress.total = data.total = this._getTotal(data.files) || 1;
|
||
data._progress.bitrate = data.bitrate = 0;
|
||
this._active += 1;
|
||
// Initialize the global progress values:
|
||
this._progress.loaded += data.loaded;
|
||
this._progress.total += data.total;
|
||
},
|
||
|
||
_onDone: function (result, textStatus, jqXHR, options) {
|
||
var total = options._progress.total,
|
||
response = options._response;
|
||
if (options._progress.loaded < total) {
|
||
// Create a progress event if no final progress event
|
||
// with loaded equaling total has been triggered:
|
||
this._onProgress($.Event('progress', {
|
||
lengthComputable: true,
|
||
loaded: total,
|
||
total: total
|
||
}), options);
|
||
}
|
||
response.result = options.result = result;
|
||
response.textStatus = options.textStatus = textStatus;
|
||
response.jqXHR = options.jqXHR = jqXHR;
|
||
this._trigger('done', null, options);
|
||
},
|
||
|
||
_onFail: function (jqXHR, textStatus, errorThrown, options) {
|
||
var response = options._response;
|
||
if (options.recalculateProgress) {
|
||
// Remove the failed (error or abort) file upload from
|
||
// the global progress calculation:
|
||
this._progress.loaded -= options._progress.loaded;
|
||
this._progress.total -= options._progress.total;
|
||
}
|
||
response.jqXHR = options.jqXHR = jqXHR;
|
||
response.textStatus = options.textStatus = textStatus;
|
||
response.errorThrown = options.errorThrown = errorThrown;
|
||
this._trigger('fail', null, options);
|
||
},
|
||
|
||
_onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) {
|
||
// jqXHRorResult, textStatus and jqXHRorError are added to the
|
||
// options object via done and fail callbacks
|
||
this._trigger('always', null, options);
|
||
},
|
||
|
||
_onSend: function (e, data) {
|
||
if (!data.submit) {
|
||
this._addConvenienceMethods(e, data);
|
||
}
|
||
var that = this,
|
||
jqXHR,
|
||
aborted,
|
||
slot,
|
||
pipe,
|
||
options = that._getAJAXSettings(data),
|
||
send = function () {
|
||
that._sending += 1;
|
||
// Set timer for bitrate progress calculation:
|
||
options._bitrateTimer = new that._BitrateTimer();
|
||
jqXHR = jqXHR || (
|
||
((aborted || that._trigger(
|
||
'send',
|
||
$.Event('send', {delegatedEvent: e}),
|
||
options
|
||
) === false) &&
|
||
that._getXHRPromise(false, options.context, aborted)) ||
|
||
that._chunkedUpload(options) || $.ajax(options)
|
||
).done(function (result, textStatus, jqXHR) {
|
||
that._onDone(result, textStatus, jqXHR, options);
|
||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||
that._onFail(jqXHR, textStatus, errorThrown, options);
|
||
}).always(function (jqXHRorResult, textStatus, jqXHRorError) {
|
||
that._deinitProgressListener(options);
|
||
that._onAlways(
|
||
jqXHRorResult,
|
||
textStatus,
|
||
jqXHRorError,
|
||
options
|
||
);
|
||
that._sending -= 1;
|
||
that._active -= 1;
|
||
if (options.limitConcurrentUploads &&
|
||
options.limitConcurrentUploads > that._sending) {
|
||
// Start the next queued upload,
|
||
// that has not been aborted:
|
||
var nextSlot = that._slots.shift();
|
||
while (nextSlot) {
|
||
if (that._getDeferredState(nextSlot) === 'pending') {
|
||
nextSlot.resolve();
|
||
break;
|
||
}
|
||
nextSlot = that._slots.shift();
|
||
}
|
||
}
|
||
if (that._active === 0) {
|
||
// The stop callback is triggered when all uploads have
|
||
// been completed, equivalent to the global ajaxStop event:
|
||
that._trigger('stop');
|
||
}
|
||
});
|
||
return jqXHR;
|
||
};
|
||
this._beforeSend(e, options);
|
||
if (this.options.sequentialUploads ||
|
||
(this.options.limitConcurrentUploads &&
|
||
this.options.limitConcurrentUploads <= this._sending)) {
|
||
if (this.options.limitConcurrentUploads > 1) {
|
||
slot = $.Deferred();
|
||
this._slots.push(slot);
|
||
pipe = slot.then(send);
|
||
} else {
|
||
this._sequence = this._sequence.then(send, send);
|
||
pipe = this._sequence;
|
||
}
|
||
// Return the piped Promise object, enhanced with an abort method,
|
||
// which is delegated to the jqXHR object of the current upload,
|
||
// and jqXHR callbacks mapped to the equivalent Promise methods:
|
||
pipe.abort = function () {
|
||
aborted = [undefined, 'abort', 'abort'];
|
||
if (!jqXHR) {
|
||
if (slot) {
|
||
slot.rejectWith(options.context, aborted);
|
||
}
|
||
return send();
|
||
}
|
||
return jqXHR.abort();
|
||
};
|
||
return this._enhancePromise(pipe);
|
||
}
|
||
return send();
|
||
},
|
||
|
||
_onAdd: function (e, data) {
|
||
var that = this,
|
||
result = true,
|
||
options = $.extend({}, this.options, data),
|
||
files = data.files,
|
||
filesLength = files.length,
|
||
limit = options.limitMultiFileUploads,
|
||
limitSize = options.limitMultiFileUploadSize,
|
||
overhead = options.limitMultiFileUploadSizeOverhead,
|
||
batchSize = 0,
|
||
paramName = this._getParamName(options),
|
||
paramNameSet,
|
||
paramNameSlice,
|
||
fileSet,
|
||
i,
|
||
j = 0;
|
||
if (!filesLength) {
|
||
return false;
|
||
}
|
||
if (limitSize && files[0].size === undefined) {
|
||
limitSize = undefined;
|
||
}
|
||
if (!(options.singleFileUploads || limit || limitSize) ||
|
||
!this._isXHRUpload(options)) {
|
||
fileSet = [files];
|
||
paramNameSet = [paramName];
|
||
} else if (!(options.singleFileUploads || limitSize) && limit) {
|
||
fileSet = [];
|
||
paramNameSet = [];
|
||
for (i = 0; i < filesLength; i += limit) {
|
||
fileSet.push(files.slice(i, i + limit));
|
||
paramNameSlice = paramName.slice(i, i + limit);
|
||
if (!paramNameSlice.length) {
|
||
paramNameSlice = paramName;
|
||
}
|
||
paramNameSet.push(paramNameSlice);
|
||
}
|
||
} else if (!options.singleFileUploads && limitSize) {
|
||
fileSet = [];
|
||
paramNameSet = [];
|
||
for (i = 0; i < filesLength; i = i + 1) {
|
||
batchSize += files[i].size + overhead;
|
||
if (i + 1 === filesLength ||
|
||
((batchSize + files[i + 1].size + overhead) > limitSize) ||
|
||
(limit && i + 1 - j >= limit)) {
|
||
fileSet.push(files.slice(j, i + 1));
|
||
paramNameSlice = paramName.slice(j, i + 1);
|
||
if (!paramNameSlice.length) {
|
||
paramNameSlice = paramName;
|
||
}
|
||
paramNameSet.push(paramNameSlice);
|
||
j = i + 1;
|
||
batchSize = 0;
|
||
}
|
||
}
|
||
} else {
|
||
paramNameSet = paramName;
|
||
}
|
||
data.originalFiles = files;
|
||
$.each(fileSet || files, function (index, element) {
|
||
var newData = $.extend({}, data);
|
||
newData.files = fileSet ? element : [element];
|
||
newData.paramName = paramNameSet[index];
|
||
that._initResponseObject(newData);
|
||
that._initProgressObject(newData);
|
||
that._addConvenienceMethods(e, newData);
|
||
result = that._trigger(
|
||
'add',
|
||
$.Event('add', {delegatedEvent: e}),
|
||
newData
|
||
);
|
||
return result;
|
||
});
|
||
return result;
|
||
},
|
||
|
||
_replaceFileInput: function (data) {
|
||
var input = data.fileInput,
|
||
inputClone = input.clone(true),
|
||
restoreFocus = input.is(document.activeElement);
|
||
// Add a reference for the new cloned file input to the data argument:
|
||
data.fileInputClone = inputClone;
|
||
$('<form></form>').append(inputClone)[0].reset();
|
||
// Detaching allows to insert the fileInput on another form
|
||
// without loosing the file input value:
|
||
input.after(inputClone).detach();
|
||
// If the fileInput had focus before it was detached,
|
||
// restore focus to the inputClone.
|
||
if (restoreFocus) {
|
||
inputClone.focus();
|
||
}
|
||
// Avoid memory leaks with the detached file input:
|
||
$.cleanData(input.unbind('remove'));
|
||
// Replace the original file input element in the fileInput
|
||
// elements set with the clone, which has been copied including
|
||
// event handlers:
|
||
this.options.fileInput = this.options.fileInput.map(function (i, el) {
|
||
if (el === input[0]) {
|
||
return inputClone[0];
|
||
}
|
||
return el;
|
||
});
|
||
// If the widget has been initialized on the file input itself,
|
||
// override this.element with the file input clone:
|
||
if (input[0] === this.element[0]) {
|
||
this.element = inputClone;
|
||
}
|
||
},
|
||
|
||
_handleFileTreeEntry: function (entry, path) {
|
||
var that = this,
|
||
dfd = $.Deferred(),
|
||
entries = [],
|
||
dirReader,
|
||
errorHandler = function (e) {
|
||
if (e && !e.entry) {
|
||
e.entry = entry;
|
||
}
|
||
// Since $.when returns immediately if one
|
||
// Deferred is rejected, we use resolve instead.
|
||
// This allows valid files and invalid items
|
||
// to be returned together in one set:
|
||
dfd.resolve([e]);
|
||
},
|
||
successHandler = function (entries) {
|
||
that._handleFileTreeEntries(
|
||
entries,
|
||
path + entry.name + '/'
|
||
).done(function (files) {
|
||
dfd.resolve(files);
|
||
}).fail(errorHandler);
|
||
},
|
||
readEntries = function () {
|
||
dirReader.readEntries(function (results) {
|
||
if (!results.length) {
|
||
successHandler(entries);
|
||
} else {
|
||
entries = entries.concat(results);
|
||
readEntries();
|
||
}
|
||
}, errorHandler);
|
||
};
|
||
path = path || '';
|
||
if (entry.isFile) {
|
||
if (entry._file) {
|
||
// Workaround for Chrome bug #149735
|
||
entry._file.relativePath = path;
|
||
dfd.resolve(entry._file);
|
||
} else {
|
||
entry.file(function (file) {
|
||
file.relativePath = path;
|
||
dfd.resolve(file);
|
||
}, errorHandler);
|
||
}
|
||
} else if (entry.isDirectory) {
|
||
dirReader = entry.createReader();
|
||
readEntries();
|
||
} else {
|
||
// Return an empty list for file system items
|
||
// other than files or directories:
|
||
dfd.resolve([]);
|
||
}
|
||
return dfd.promise();
|
||
},
|
||
|
||
_handleFileTreeEntries: function (entries, path) {
|
||
var that = this;
|
||
return $.when.apply(
|
||
$,
|
||
$.map(entries, function (entry) {
|
||
return that._handleFileTreeEntry(entry, path);
|
||
})
|
||
).then(function () {
|
||
return Array.prototype.concat.apply(
|
||
[],
|
||
arguments
|
||
);
|
||
});
|
||
},
|
||
|
||
_getDroppedFiles: function (dataTransfer) {
|
||
dataTransfer = dataTransfer || {};
|
||
var items = dataTransfer.items;
|
||
if (items && items.length && (items[0].webkitGetAsEntry ||
|
||
items[0].getAsEntry)) {
|
||
return this._handleFileTreeEntries(
|
||
$.map(items, function (item) {
|
||
var entry;
|
||
if (item.webkitGetAsEntry) {
|
||
entry = item.webkitGetAsEntry();
|
||
if (entry) {
|
||
// Workaround for Chrome bug #149735:
|
||
entry._file = item.getAsFile();
|
||
}
|
||
return entry;
|
||
}
|
||
return item.getAsEntry();
|
||
})
|
||
);
|
||
}
|
||
return $.Deferred().resolve(
|
||
$.makeArray(dataTransfer.files)
|
||
).promise();
|
||
},
|
||
|
||
_getSingleFileInputFiles: function (fileInput) {
|
||
fileInput = $(fileInput);
|
||
var entries = fileInput.prop('webkitEntries') ||
|
||
fileInput.prop('entries'),
|
||
files,
|
||
value;
|
||
if (entries && entries.length) {
|
||
return this._handleFileTreeEntries(entries);
|
||
}
|
||
files = $.makeArray(fileInput.prop('files'));
|
||
if (!files.length) {
|
||
value = fileInput.prop('value');
|
||
if (!value) {
|
||
return $.Deferred().resolve([]).promise();
|
||
}
|
||
// If the files property is not available, the browser does not
|
||
// support the File API and we add a pseudo File object with
|
||
// the input value as name with path information removed:
|
||
files = [{name: value.replace(/^.*\\/, '')}];
|
||
} else if (files[0].name === undefined && files[0].fileName) {
|
||
// File normalization for Safari 4 and Firefox 3:
|
||
$.each(files, function (index, file) {
|
||
file.name = file.fileName;
|
||
file.size = file.fileSize;
|
||
});
|
||
}
|
||
return $.Deferred().resolve(files).promise();
|
||
},
|
||
|
||
_getFileInputFiles: function (fileInput) {
|
||
if (!(fileInput instanceof $) || fileInput.length === 1) {
|
||
return this._getSingleFileInputFiles(fileInput);
|
||
}
|
||
return $.when.apply(
|
||
$,
|
||
$.map(fileInput, this._getSingleFileInputFiles)
|
||
).then(function () {
|
||
return Array.prototype.concat.apply(
|
||
[],
|
||
arguments
|
||
);
|
||
});
|
||
},
|
||
|
||
_onChange: function (e) {
|
||
var that = this,
|
||
data = {
|
||
fileInput: $(e.target),
|
||
form: $(e.target.form)
|
||
};
|
||
this._getFileInputFiles(data.fileInput).always(function (files) {
|
||
data.files = files;
|
||
if (that.options.replaceFileInput) {
|
||
that._replaceFileInput(data);
|
||
}
|
||
if (that._trigger(
|
||
'change',
|
||
$.Event('change', {delegatedEvent: e}),
|
||
data
|
||
) !== false) {
|
||
that._onAdd(e, data);
|
||
}
|
||
});
|
||
},
|
||
|
||
_onPaste: function (e) {
|
||
var items = e.originalEvent && e.originalEvent.clipboardData &&
|
||
e.originalEvent.clipboardData.items,
|
||
data = {files: []};
|
||
if (items && items.length) {
|
||
$.each(items, function (index, item) {
|
||
var file = item.getAsFile && item.getAsFile();
|
||
if (file) {
|
||
data.files.push(file);
|
||
}
|
||
});
|
||
if (this._trigger(
|
||
'paste',
|
||
$.Event('paste', {delegatedEvent: e}),
|
||
data
|
||
) !== false) {
|
||
this._onAdd(e, data);
|
||
}
|
||
}
|
||
},
|
||
|
||
_onDrop: function (e) {
|
||
e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
|
||
var that = this,
|
||
dataTransfer = e.dataTransfer,
|
||
data = {};
|
||
if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
|
||
e.preventDefault();
|
||
this._getDroppedFiles(dataTransfer).always(function (files) {
|
||
data.files = files;
|
||
if (that._trigger(
|
||
'drop',
|
||
$.Event('drop', {delegatedEvent: e}),
|
||
data
|
||
) !== false) {
|
||
that._onAdd(e, data);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
_onDragOver: getDragHandler('dragover'),
|
||
|
||
_onDragEnter: getDragHandler('dragenter'),
|
||
|
||
_onDragLeave: getDragHandler('dragleave'),
|
||
|
||
_initEventHandlers: function () {
|
||
if (this._isXHRUpload(this.options)) {
|
||
this._on(this.options.dropZone, {
|
||
dragover: this._onDragOver,
|
||
drop: this._onDrop,
|
||
// event.preventDefault() on dragenter is required for IE10+:
|
||
dragenter: this._onDragEnter,
|
||
// dragleave is not required, but added for completeness:
|
||
dragleave: this._onDragLeave
|
||
});
|
||
this._on(this.options.pasteZone, {
|
||
paste: this._onPaste
|
||
});
|
||
}
|
||
if ($.support.fileInput) {
|
||
this._on(this.options.fileInput, {
|
||
change: this._onChange
|
||
});
|
||
}
|
||
},
|
||
|
||
_destroyEventHandlers: function () {
|
||
this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
|
||
this._off(this.options.pasteZone, 'paste');
|
||
this._off(this.options.fileInput, 'change');
|
||
},
|
||
|
||
_destroy: function () {
|
||
this._destroyEventHandlers();
|
||
},
|
||
|
||
_setOption: function (key, value) {
|
||
var reinit = $.inArray(key, this._specialOptions) !== -1;
|
||
if (reinit) {
|
||
this._destroyEventHandlers();
|
||
}
|
||
this._super(key, value);
|
||
if (reinit) {
|
||
this._initSpecialOptions();
|
||
this._initEventHandlers();
|
||
}
|
||
},
|
||
|
||
_initSpecialOptions: function () {
|
||
var options = this.options;
|
||
if (options.fileInput === undefined) {
|
||
options.fileInput = this.element.is('input[type="file"]') ?
|
||
this.element : this.element.find('input[type="file"]');
|
||
} else if (!(options.fileInput instanceof $)) {
|
||
options.fileInput = $(options.fileInput);
|
||
}
|
||
if (!(options.dropZone instanceof $)) {
|
||
options.dropZone = $(options.dropZone);
|
||
}
|
||
if (!(options.pasteZone instanceof $)) {
|
||
options.pasteZone = $(options.pasteZone);
|
||
}
|
||
},
|
||
|
||
_getRegExp: function (str) {
|
||
var parts = str.split('/'),
|
||
modifiers = parts.pop();
|
||
parts.shift();
|
||
return new RegExp(parts.join('/'), modifiers);
|
||
},
|
||
|
||
_isRegExpOption: function (key, value) {
|
||
return key !== 'url' && $.type(value) === 'string' &&
|
||
/^\/.*\/[igm]{0,3}$/.test(value);
|
||
},
|
||
|
||
_initDataAttributes: function () {
|
||
var that = this,
|
||
options = this.options,
|
||
data = this.element.data();
|
||
// Initialize options set via HTML5 data-attributes:
|
||
$.each(
|
||
this.element[0].attributes,
|
||
function (index, attr) {
|
||
var key = attr.name.toLowerCase(),
|
||
value;
|
||
if (/^data-/.test(key)) {
|
||
// Convert hyphen-ated key to camelCase:
|
||
key = key.slice(5).replace(/-[a-z]/g, function (str) {
|
||
return str.charAt(1).toUpperCase();
|
||
});
|
||
value = data[key];
|
||
if (that._isRegExpOption(key, value)) {
|
||
value = that._getRegExp(value);
|
||
}
|
||
options[key] = value;
|
||
}
|
||
}
|
||
);
|
||
},
|
||
|
||
_create: function () {
|
||
this._initDataAttributes();
|
||
this._initSpecialOptions();
|
||
this._slots = [];
|
||
this._sequence = this._getXHRPromise(true);
|
||
this._sending = this._active = 0;
|
||
this._initProgressObject(this);
|
||
this._initEventHandlers();
|
||
},
|
||
|
||
// This method is exposed to the widget API and allows to query
|
||
// the number of active uploads:
|
||
active: function () {
|
||
return this._active;
|
||
},
|
||
|
||
// This method is exposed to the widget API and allows to query
|
||
// the widget upload progress.
|
||
// It returns an object with loaded, total and bitrate properties
|
||
// for the running uploads:
|
||
progress: function () {
|
||
return this._progress;
|
||
},
|
||
|
||
// This method is exposed to the widget API and allows adding files
|
||
// using the fileupload API. The data parameter accepts an object which
|
||
// must have a files property and can contain additional options:
|
||
// .fileupload('add', {files: filesList});
|
||
add: function (data) {
|
||
var that = this;
|
||
if (!data || this.options.disabled) {
|
||
return;
|
||
}
|
||
if (data.fileInput && !data.files) {
|
||
this._getFileInputFiles(data.fileInput).always(function (files) {
|
||
data.files = files;
|
||
that._onAdd(null, data);
|
||
});
|
||
} else {
|
||
data.files = $.makeArray(data.files);
|
||
this._onAdd(null, data);
|
||
}
|
||
},
|
||
|
||
// This method is exposed to the widget API and allows sending files
|
||
// using the fileupload API. The data parameter accepts an object which
|
||
// must have a files or fileInput property and can contain additional options:
|
||
// .fileupload('send', {files: filesList});
|
||
// The method returns a Promise object for the file upload call.
|
||
send: function (data) {
|
||
if (data && !this.options.disabled) {
|
||
if (data.fileInput && !data.files) {
|
||
var that = this,
|
||
dfd = $.Deferred(),
|
||
promise = dfd.promise(),
|
||
jqXHR,
|
||
aborted;
|
||
promise.abort = function () {
|
||
aborted = true;
|
||
if (jqXHR) {
|
||
return jqXHR.abort();
|
||
}
|
||
dfd.reject(null, 'abort', 'abort');
|
||
return promise;
|
||
};
|
||
this._getFileInputFiles(data.fileInput).always(
|
||
function (files) {
|
||
if (aborted) {
|
||
return;
|
||
}
|
||
if (!files.length) {
|
||
dfd.reject();
|
||
return;
|
||
}
|
||
data.files = files;
|
||
jqXHR = that._onSend(null, data);
|
||
jqXHR.then(
|
||
function (result, textStatus, jqXHR) {
|
||
dfd.resolve(result, textStatus, jqXHR);
|
||
},
|
||
function (jqXHR, textStatus, errorThrown) {
|
||
dfd.reject(jqXHR, textStatus, errorThrown);
|
||
}
|
||
);
|
||
}
|
||
);
|
||
return this._enhancePromise(promise);
|
||
}
|
||
data.files = $.makeArray(data.files);
|
||
if (data.files.length) {
|
||
return this._onSend(null, data);
|
||
}
|
||
}
|
||
return this._getXHRPromise(false, data && data.context);
|
||
}
|
||
|
||
});
|
||
|
||
}));
|
||
|
||
/*!
|
||
* Bootstrap Colorpicker v2.5.2
|
||
* https://itsjavi.com/bootstrap-colorpicker/
|
||
*
|
||
* Originally written by (c) 2012 Stefan Petre
|
||
* Licensed under the Apache License v2.0
|
||
* http://www.apache.org/licenses/LICENSE-2.0.txt
|
||
*
|
||
*/
|
||
|
||
(function(root, factory) {
|
||
if (typeof define === 'function' && define.amd) {
|
||
// AMD. Register as an anonymous module unless amdModuleId is set
|
||
define(["jquery"], function(jq) {
|
||
return (factory(jq));
|
||
});
|
||
} else if (typeof exports === 'object') {
|
||
// Node. Does not work with strict CommonJS, but
|
||
// only CommonJS-like environments that support module.exports,
|
||
// like Node.
|
||
module.exports = factory(require("jquery"));
|
||
} else if (jQuery && !jQuery.fn.colorpicker) {
|
||
factory(jQuery);
|
||
}
|
||
}(this, function($) {
|
||
'use strict';
|
||
/**
|
||
* Color manipulation helper class
|
||
*
|
||
* @param {Object|String} [val]
|
||
* @param {Object} [predefinedColors]
|
||
* @param {String|null} [fallbackColor]
|
||
* @param {String|null} [fallbackFormat]
|
||
* @param {Boolean} [hexNumberSignPrefix]
|
||
* @constructor
|
||
*/
|
||
var Color = function(
|
||
val, predefinedColors, fallbackColor, fallbackFormat, hexNumberSignPrefix) {
|
||
this.fallbackValue = fallbackColor ?
|
||
(
|
||
(typeof fallbackColor === 'string') ?
|
||
this.parse(fallbackColor) :
|
||
fallbackColor
|
||
) :
|
||
null;
|
||
|
||
this.fallbackFormat = fallbackFormat ? fallbackFormat : 'rgba';
|
||
|
||
this.hexNumberSignPrefix = hexNumberSignPrefix === true;
|
||
|
||
this.value = this.fallbackValue;
|
||
|
||
this.origFormat = null; // original string format
|
||
|
||
this.predefinedColors = predefinedColors ? predefinedColors : {};
|
||
|
||
// We don't want to share aliases across instances so we extend new object
|
||
this.colors = $.extend({}, Color.webColors, this.predefinedColors);
|
||
|
||
if (val) {
|
||
if (typeof val.h !== 'undefined') {
|
||
this.value = val;
|
||
} else {
|
||
this.setColor(String(val));
|
||
}
|
||
}
|
||
|
||
if (!this.value) {
|
||
// Initial value is always black if no arguments are passed or val is empty
|
||
this.value = {
|
||
h: 0,
|
||
s: 0,
|
||
b: 0,
|
||
a: 1
|
||
};
|
||
}
|
||
};
|
||
|
||
Color.webColors = { // 140 predefined colors from the HTML Colors spec
|
||
"aliceblue": "f0f8ff",
|
||
"antiquewhite": "faebd7",
|
||
"aqua": "00ffff",
|
||
"aquamarine": "7fffd4",
|
||
"azure": "f0ffff",
|
||
"beige": "f5f5dc",
|
||
"bisque": "ffe4c4",
|
||
"black": "000000",
|
||
"blanchedalmond": "ffebcd",
|
||
"blue": "0000ff",
|
||
"blueviolet": "8a2be2",
|
||
"brown": "a52a2a",
|
||
"burlywood": "deb887",
|
||
"cadetblue": "5f9ea0",
|
||
"chartreuse": "7fff00",
|
||
"chocolate": "d2691e",
|
||
"coral": "ff7f50",
|
||
"cornflowerblue": "6495ed",
|
||
"cornsilk": "fff8dc",
|
||
"crimson": "dc143c",
|
||
"cyan": "00ffff",
|
||
"darkblue": "00008b",
|
||
"darkcyan": "008b8b",
|
||
"darkgoldenrod": "b8860b",
|
||
"darkgray": "a9a9a9",
|
||
"darkgreen": "006400",
|
||
"darkkhaki": "bdb76b",
|
||
"darkmagenta": "8b008b",
|
||
"darkolivegreen": "556b2f",
|
||
"darkorange": "ff8c00",
|
||
"darkorchid": "9932cc",
|
||
"darkred": "8b0000",
|
||
"darksalmon": "e9967a",
|
||
"darkseagreen": "8fbc8f",
|
||
"darkslateblue": "483d8b",
|
||
"darkslategray": "2f4f4f",
|
||
"darkturquoise": "00ced1",
|
||
"darkviolet": "9400d3",
|
||
"deeppink": "ff1493",
|
||
"deepskyblue": "00bfff",
|
||
"dimgray": "696969",
|
||
"dodgerblue": "1e90ff",
|
||
"firebrick": "b22222",
|
||
"floralwhite": "fffaf0",
|
||
"forestgreen": "228b22",
|
||
"fuchsia": "ff00ff",
|
||
"gainsboro": "dcdcdc",
|
||
"ghostwhite": "f8f8ff",
|
||
"gold": "ffd700",
|
||
"goldenrod": "daa520",
|
||
"gray": "808080",
|
||
"green": "008000",
|
||
"greenyellow": "adff2f",
|
||
"honeydew": "f0fff0",
|
||
"hotpink": "ff69b4",
|
||
"indianred": "cd5c5c",
|
||
"indigo": "4b0082",
|
||
"ivory": "fffff0",
|
||
"khaki": "f0e68c",
|
||
"lavender": "e6e6fa",
|
||
"lavenderblush": "fff0f5",
|
||
"lawngreen": "7cfc00",
|
||
"lemonchiffon": "fffacd",
|
||
"lightblue": "add8e6",
|
||
"lightcoral": "f08080",
|
||
"lightcyan": "e0ffff",
|
||
"lightgoldenrodyellow": "fafad2",
|
||
"lightgrey": "d3d3d3",
|
||
"lightgreen": "90ee90",
|
||
"lightpink": "ffb6c1",
|
||
"lightsalmon": "ffa07a",
|
||
"lightseagreen": "20b2aa",
|
||
"lightskyblue": "87cefa",
|
||
"lightslategray": "778899",
|
||
"lightsteelblue": "b0c4de",
|
||
"lightyellow": "ffffe0",
|
||
"lime": "00ff00",
|
||
"limegreen": "32cd32",
|
||
"linen": "faf0e6",
|
||
"magenta": "ff00ff",
|
||
"maroon": "800000",
|
||
"mediumaquamarine": "66cdaa",
|
||
"mediumblue": "0000cd",
|
||
"mediumorchid": "ba55d3",
|
||
"mediumpurple": "9370d8",
|
||
"mediumseagreen": "3cb371",
|
||
"mediumslateblue": "7b68ee",
|
||
"mediumspringgreen": "00fa9a",
|
||
"mediumturquoise": "48d1cc",
|
||
"mediumvioletred": "c71585",
|
||
"midnightblue": "191970",
|
||
"mintcream": "f5fffa",
|
||
"mistyrose": "ffe4e1",
|
||
"moccasin": "ffe4b5",
|
||
"navajowhite": "ffdead",
|
||
"navy": "000080",
|
||
"oldlace": "fdf5e6",
|
||
"olive": "808000",
|
||
"olivedrab": "6b8e23",
|
||
"orange": "ffa500",
|
||
"orangered": "ff4500",
|
||
"orchid": "da70d6",
|
||
"palegoldenrod": "eee8aa",
|
||
"palegreen": "98fb98",
|
||
"paleturquoise": "afeeee",
|
||
"palevioletred": "d87093",
|
||
"papayawhip": "ffefd5",
|
||
"peachpuff": "ffdab9",
|
||
"peru": "cd853f",
|
||
"pink": "ffc0cb",
|
||
"plum": "dda0dd",
|
||
"powderblue": "b0e0e6",
|
||
"purple": "800080",
|
||
"red": "ff0000",
|
||
"rosybrown": "bc8f8f",
|
||
"royalblue": "4169e1",
|
||
"saddlebrown": "8b4513",
|
||
"salmon": "fa8072",
|
||
"sandybrown": "f4a460",
|
||
"seagreen": "2e8b57",
|
||
"seashell": "fff5ee",
|
||
"sienna": "a0522d",
|
||
"silver": "c0c0c0",
|
||
"skyblue": "87ceeb",
|
||
"slateblue": "6a5acd",
|
||
"slategray": "708090",
|
||
"snow": "fffafa",
|
||
"springgreen": "00ff7f",
|
||
"steelblue": "4682b4",
|
||
"tan": "d2b48c",
|
||
"teal": "008080",
|
||
"thistle": "d8bfd8",
|
||
"tomato": "ff6347",
|
||
"turquoise": "40e0d0",
|
||
"violet": "ee82ee",
|
||
"wheat": "f5deb3",
|
||
"white": "ffffff",
|
||
"whitesmoke": "f5f5f5",
|
||
"yellow": "ffff00",
|
||
"yellowgreen": "9acd32",
|
||
"transparent": "transparent"
|
||
};
|
||
|
||
Color.prototype = {
|
||
constructor: Color,
|
||
colors: {}, // merged web and predefined colors
|
||
predefinedColors: {},
|
||
/**
|
||
* @return {Object}
|
||
*/
|
||
getValue: function() {
|
||
return this.value;
|
||
},
|
||
/**
|
||
* @param {Object} val
|
||
*/
|
||
setValue: function(val) {
|
||
this.value = val;
|
||
},
|
||
_sanitizeNumber: function(val) {
|
||
if (typeof val === 'number') {
|
||
return val;
|
||
}
|
||
if (isNaN(val) || (val === null) || (val === '') || (val === undefined)) {
|
||
return 1;
|
||
}
|
||
if (val === '') {
|
||
return 0;
|
||
}
|
||
if (typeof val.toLowerCase !== 'undefined') {
|
||
if (val.match(/^\./)) {
|
||
val = "0" + val;
|
||
}
|
||
return Math.ceil(parseFloat(val) * 100) / 100;
|
||
}
|
||
return 1;
|
||
},
|
||
isTransparent: function(strVal) {
|
||
if (!strVal || !(typeof strVal === 'string' || strVal instanceof String)) {
|
||
return false;
|
||
}
|
||
strVal = strVal.toLowerCase().trim();
|
||
return (strVal === 'transparent') || (strVal.match(/#?00000000/)) || (strVal.match(/(rgba|hsla)\(0,0,0,0?\.?0\)/));
|
||
},
|
||
rgbaIsTransparent: function(rgba) {
|
||
return ((rgba.r === 0) && (rgba.g === 0) && (rgba.b === 0) && (rgba.a === 0));
|
||
},
|
||
// parse a string to HSB
|
||
/**
|
||
* @protected
|
||
* @param {String} strVal
|
||
* @returns {boolean} Returns true if it could be parsed, false otherwise
|
||
*/
|
||
setColor: function(strVal) {
|
||
strVal = strVal.toLowerCase().trim();
|
||
if (strVal) {
|
||
if (this.isTransparent(strVal)) {
|
||
this.value = {
|
||
h: 0,
|
||
s: 0,
|
||
b: 0,
|
||
a: 0
|
||
};
|
||
return true;
|
||
} else {
|
||
var parsedColor = this.parse(strVal);
|
||
if (parsedColor) {
|
||
this.value = this.value = {
|
||
h: parsedColor.h,
|
||
s: parsedColor.s,
|
||
b: parsedColor.b,
|
||
a: parsedColor.a
|
||
};
|
||
if (!this.origFormat) {
|
||
this.origFormat = parsedColor.format;
|
||
}
|
||
} else if (this.fallbackValue) {
|
||
// if parser fails, defaults to fallbackValue if defined, otherwise the value won't be changed
|
||
this.value = this.fallbackValue;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
setHue: function(h) {
|
||
this.value.h = 1 - h;
|
||
},
|
||
setSaturation: function(s) {
|
||
this.value.s = s;
|
||
},
|
||
setBrightness: function(b) {
|
||
this.value.b = 1 - b;
|
||
},
|
||
setAlpha: function(a) {
|
||
this.value.a = Math.round((parseInt((1 - a) * 100, 10) / 100) * 100) / 100;
|
||
},
|
||
toRGB: function(h, s, b, a) {
|
||
if (arguments.length === 0) {
|
||
h = this.value.h;
|
||
s = this.value.s;
|
||
b = this.value.b;
|
||
a = this.value.a;
|
||
}
|
||
|
||
h *= 360;
|
||
var R, G, B, X, C;
|
||
h = (h % 360) / 60;
|
||
C = b * s;
|
||
X = C * (1 - Math.abs(h % 2 - 1));
|
||
R = G = B = b - C;
|
||
|
||
h = ~~h;
|
||
R += [C, X, 0, 0, X, C][h];
|
||
G += [X, C, C, X, 0, 0][h];
|
||
B += [0, 0, X, C, C, X][h];
|
||
|
||
return {
|
||
r: Math.round(R * 255),
|
||
g: Math.round(G * 255),
|
||
b: Math.round(B * 255),
|
||
a: a
|
||
};
|
||
},
|
||
toHex: function(ignoreFormat, h, s, b, a) {
|
||
if (arguments.length <= 1) {
|
||
h = this.value.h;
|
||
s = this.value.s;
|
||
b = this.value.b;
|
||
a = this.value.a;
|
||
}
|
||
|
||
var prefix = '#';
|
||
var rgb = this.toRGB(h, s, b, a);
|
||
|
||
if (this.rgbaIsTransparent(rgb)) {
|
||
return 'transparent';
|
||
}
|
||
|
||
if (!ignoreFormat) {
|
||
prefix = (this.hexNumberSignPrefix ? '#' : '');
|
||
}
|
||
|
||
var hexStr = prefix + (
|
||
(1 << 24) +
|
||
(parseInt(rgb.r) << 16) +
|
||
(parseInt(rgb.g) << 8) +
|
||
parseInt(rgb.b))
|
||
.toString(16)
|
||
.slice(1);
|
||
|
||
return hexStr;
|
||
},
|
||
toHSL: function(h, s, b, a) {
|
||
if (arguments.length === 0) {
|
||
h = this.value.h;
|
||
s = this.value.s;
|
||
b = this.value.b;
|
||
a = this.value.a;
|
||
}
|
||
|
||
var H = h,
|
||
L = (2 - s) * b,
|
||
S = s * b;
|
||
if (L > 0 && L <= 1) {
|
||
S /= L;
|
||
} else {
|
||
S /= 2 - L;
|
||
}
|
||
L /= 2;
|
||
if (S > 1) {
|
||
S = 1;
|
||
}
|
||
return {
|
||
h: isNaN(H) ? 0 : H,
|
||
s: isNaN(S) ? 0 : S,
|
||
l: isNaN(L) ? 0 : L,
|
||
a: isNaN(a) ? 0 : a
|
||
};
|
||
},
|
||
toAlias: function(r, g, b, a) {
|
||
var c, rgb = (arguments.length === 0) ? this.toHex(true) : this.toHex(true, r, g, b, a);
|
||
|
||
// support predef. colors in non-hex format too, as defined in the alias itself
|
||
var original = this.origFormat === 'alias' ? rgb : this.toString(false, this.origFormat);
|
||
|
||
for (var alias in this.colors) {
|
||
c = this.colors[alias].toLowerCase().trim();
|
||
if ((c === rgb) || (c === original)) {
|
||
return alias;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
RGBtoHSB: function(r, g, b, a) {
|
||
r /= 255;
|
||
g /= 255;
|
||
b /= 255;
|
||
|
||
var H, S, V, C;
|
||
V = Math.max(r, g, b);
|
||
C = V - Math.min(r, g, b);
|
||
H = (C === 0 ? null :
|
||
V === r ? (g - b) / C :
|
||
V === g ? (b - r) / C + 2 :
|
||
(r - g) / C + 4
|
||
);
|
||
H = ((H + 360) % 6) * 60 / 360;
|
||
S = C === 0 ? 0 : C / V;
|
||
return {
|
||
h: this._sanitizeNumber(H),
|
||
s: S,
|
||
b: V,
|
||
a: this._sanitizeNumber(a)
|
||
};
|
||
},
|
||
HueToRGB: function(p, q, h) {
|
||
if (h < 0) {
|
||
h += 1;
|
||
} else if (h > 1) {
|
||
h -= 1;
|
||
}
|
||
if ((h * 6) < 1) {
|
||
return p + (q - p) * h * 6;
|
||
} else if ((h * 2) < 1) {
|
||
return q;
|
||
} else if ((h * 3) < 2) {
|
||
return p + (q - p) * ((2 / 3) - h) * 6;
|
||
} else {
|
||
return p;
|
||
}
|
||
},
|
||
HSLtoRGB: function(h, s, l, a) {
|
||
if (s < 0) {
|
||
s = 0;
|
||
}
|
||
var q;
|
||
if (l <= 0.5) {
|
||
q = l * (1 + s);
|
||
} else {
|
||
q = l + s - (l * s);
|
||
}
|
||
|
||
var p = 2 * l - q;
|
||
|
||
var tr = h + (1 / 3);
|
||
var tg = h;
|
||
var tb = h - (1 / 3);
|
||
|
||
var r = Math.round(this.HueToRGB(p, q, tr) * 255);
|
||
var g = Math.round(this.HueToRGB(p, q, tg) * 255);
|
||
var b = Math.round(this.HueToRGB(p, q, tb) * 255);
|
||
return [r, g, b, this._sanitizeNumber(a)];
|
||
},
|
||
/**
|
||
* @param {String} strVal
|
||
* @returns {Object} Object containing h,s,b,a,format properties or FALSE if failed to parse
|
||
*/
|
||
parse: function(strVal) {
|
||
if (typeof strVal !== 'string') {
|
||
return this.fallbackValue;
|
||
}
|
||
if (arguments.length === 0) {
|
||
return false;
|
||
}
|
||
|
||
var that = this,
|
||
result = false,
|
||
isAlias = (typeof this.colors[strVal] !== 'undefined'),
|
||
values, format;
|
||
|
||
if (isAlias) {
|
||
strVal = this.colors[strVal].toLowerCase().trim();
|
||
}
|
||
|
||
$.each(this.stringParsers, function(i, parser) {
|
||
var match = parser.re.exec(strVal);
|
||
values = match && parser.parse.apply(that, [match]);
|
||
if (values) {
|
||
result = {};
|
||
format = (isAlias ? 'alias' : (parser.format ? parser.format : that.getValidFallbackFormat()));
|
||
if (format.match(/hsla?/)) {
|
||
result = that.RGBtoHSB.apply(that, that.HSLtoRGB.apply(that, values));
|
||
} else {
|
||
result = that.RGBtoHSB.apply(that, values);
|
||
}
|
||
if (result instanceof Object) {
|
||
result.format = format;
|
||
}
|
||
return false; // stop iterating
|
||
}
|
||
return true;
|
||
});
|
||
return result;
|
||
},
|
||
getValidFallbackFormat: function() {
|
||
var formats = [
|
||
'rgba', 'rgb', 'hex', 'hsla', 'hsl'
|
||
];
|
||
if (this.origFormat && (formats.indexOf(this.origFormat) !== -1)) {
|
||
return this.origFormat;
|
||
}
|
||
if (this.fallbackFormat && (formats.indexOf(this.fallbackFormat) !== -1)) {
|
||
return this.fallbackFormat;
|
||
}
|
||
|
||
return 'rgba'; // By default, return a format that will not lose the alpha info
|
||
},
|
||
/**
|
||
*
|
||
* @param {string} [format] (default: rgba)
|
||
* @param {boolean} [translateAlias] Return real color for pre-defined (non-standard) aliases (default: false)
|
||
* @param {boolean} [forceRawValue] Forces hashtag prefix when getting hex color (default: false)
|
||
* @returns {String}
|
||
*/
|
||
toString: function(forceRawValue, format, translateAlias) {
|
||
format = format || this.origFormat || this.fallbackFormat;
|
||
translateAlias = translateAlias || false;
|
||
|
||
var c = false;
|
||
|
||
switch (format) {
|
||
case 'rgb':
|
||
{
|
||
c = this.toRGB();
|
||
if (this.rgbaIsTransparent(c)) {
|
||
return 'transparent';
|
||
}
|
||
return 'rgb(' + c.r + ',' + c.g + ',' + c.b + ')';
|
||
}
|
||
break;
|
||
case 'rgba':
|
||
{
|
||
c = this.toRGB();
|
||
return 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + c.a + ')';
|
||
}
|
||
break;
|
||
case 'hsl':
|
||
{
|
||
c = this.toHSL();
|
||
return 'hsl(' + Math.round(c.h * 360) + ',' + Math.round(c.s * 100) + '%,' + Math.round(c.l * 100) + '%)';
|
||
}
|
||
break;
|
||
case 'hsla':
|
||
{
|
||
c = this.toHSL();
|
||
return 'hsla(' + Math.round(c.h * 360) + ',' + Math.round(c.s * 100) + '%,' + Math.round(c.l * 100) + '%,' + c.a + ')';
|
||
}
|
||
break;
|
||
case 'hex':
|
||
{
|
||
return this.toHex(forceRawValue);
|
||
}
|
||
break;
|
||
case 'alias':
|
||
{
|
||
c = this.toAlias();
|
||
|
||
if (c === false) {
|
||
return this.toString(forceRawValue, this.getValidFallbackFormat());
|
||
}
|
||
|
||
if (translateAlias && !(c in Color.webColors) && (c in this.predefinedColors)) {
|
||
return this.predefinedColors[c];
|
||
}
|
||
|
||
return c;
|
||
}
|
||
default:
|
||
{
|
||
return c;
|
||
}
|
||
break;
|
||
}
|
||
},
|
||
// a set of RE's that can match strings and generate color tuples.
|
||
// from John Resig color plugin
|
||
// https://github.com/jquery/jquery-color/
|
||
stringParsers: [{
|
||
re: /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*?\)/,
|
||
format: 'rgb',
|
||
parse: function(execResult) {
|
||
return [
|
||
execResult[1],
|
||
execResult[2],
|
||
execResult[3],
|
||
1
|
||
];
|
||
}
|
||
}, {
|
||
re: /rgb\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,
|
||
format: 'rgb',
|
||
parse: function(execResult) {
|
||
return [
|
||
2.55 * execResult[1],
|
||
2.55 * execResult[2],
|
||
2.55 * execResult[3],
|
||
1
|
||
];
|
||
}
|
||
}, {
|
||
re: /rgba\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,
|
||
format: 'rgba',
|
||
parse: function(execResult) {
|
||
return [
|
||
execResult[1],
|
||
execResult[2],
|
||
execResult[3],
|
||
execResult[4]
|
||
];
|
||
}
|
||
}, {
|
||
re: /rgba\(\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,
|
||
format: 'rgba',
|
||
parse: function(execResult) {
|
||
return [
|
||
2.55 * execResult[1],
|
||
2.55 * execResult[2],
|
||
2.55 * execResult[3],
|
||
execResult[4]
|
||
];
|
||
}
|
||
}, {
|
||
re: /hsl\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*?\)/,
|
||
format: 'hsl',
|
||
parse: function(execResult) {
|
||
return [
|
||
execResult[1] / 360,
|
||
execResult[2] / 100,
|
||
execResult[3] / 100,
|
||
execResult[4]
|
||
];
|
||
}
|
||
}, {
|
||
re: /hsla\(\s*(\d*(?:\.\d+)?)\s*,\s*(\d*(?:\.\d+)?)\%\s*,\s*(\d*(?:\.\d+)?)\%\s*(?:,\s*(\d*(?:\.\d+)?)\s*)?\)/,
|
||
format: 'hsla',
|
||
parse: function(execResult) {
|
||
return [
|
||
execResult[1] / 360,
|
||
execResult[2] / 100,
|
||
execResult[3] / 100,
|
||
execResult[4]
|
||
];
|
||
}
|
||
}, {
|
||
re: /#?([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/,
|
||
format: 'hex',
|
||
parse: function(execResult) {
|
||
return [
|
||
parseInt(execResult[1], 16),
|
||
parseInt(execResult[2], 16),
|
||
parseInt(execResult[3], 16),
|
||
1
|
||
];
|
||
}
|
||
}, {
|
||
re: /#?([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/,
|
||
format: 'hex',
|
||
parse: function(execResult) {
|
||
return [
|
||
parseInt(execResult[1] + execResult[1], 16),
|
||
parseInt(execResult[2] + execResult[2], 16),
|
||
parseInt(execResult[3] + execResult[3], 16),
|
||
1
|
||
];
|
||
}
|
||
}],
|
||
colorNameToHex: function(name) {
|
||
if (typeof this.colors[name.toLowerCase()] !== 'undefined') {
|
||
return this.colors[name.toLowerCase()];
|
||
}
|
||
return false;
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Default plugin options
|
||
*/
|
||
var defaults = {
|
||
horizontal: false, // horizontal mode layout ?
|
||
inline: false, //forces to show the colorpicker as an inline element
|
||
color: false, //forces a color
|
||
format: false, //forces a format
|
||
input: 'input', // children input selector
|
||
container: false, // container selector
|
||
component: '.add-on, .input-group-addon', // children component selector
|
||
fallbackColor: false, // fallback color value. null = keeps current color.
|
||
fallbackFormat: 'hex', // fallback color format
|
||
hexNumberSignPrefix: true, // put a '#' (number sign) before hex strings
|
||
sliders: {
|
||
saturation: {
|
||
maxLeft: 100,
|
||
maxTop: 100,
|
||
callLeft: 'setSaturation',
|
||
callTop: 'setBrightness'
|
||
},
|
||
hue: {
|
||
maxLeft: 0,
|
||
maxTop: 100,
|
||
callLeft: false,
|
||
callTop: 'setHue'
|
||
},
|
||
alpha: {
|
||
maxLeft: 0,
|
||
maxTop: 100,
|
||
callLeft: false,
|
||
callTop: 'setAlpha'
|
||
}
|
||
},
|
||
slidersHorz: {
|
||
saturation: {
|
||
maxLeft: 100,
|
||
maxTop: 100,
|
||
callLeft: 'setSaturation',
|
||
callTop: 'setBrightness'
|
||
},
|
||
hue: {
|
||
maxLeft: 100,
|
||
maxTop: 0,
|
||
callLeft: 'setHue',
|
||
callTop: false
|
||
},
|
||
alpha: {
|
||
maxLeft: 100,
|
||
maxTop: 0,
|
||
callLeft: 'setAlpha',
|
||
callTop: false
|
||
}
|
||
},
|
||
template: '<div class="colorpicker dropdown-menu">' +
|
||
'<div class="colorpicker-saturation"><i><b></b></i></div>' +
|
||
'<div class="colorpicker-hue"><i></i></div>' +
|
||
'<div class="colorpicker-alpha"><i></i></div>' +
|
||
'<div class="colorpicker-color"><div /></div>' +
|
||
'<div class="colorpicker-selectors"></div>' +
|
||
'</div>',
|
||
align: 'right',
|
||
customClass: null, // custom class added to the colorpicker element
|
||
colorSelectors: null // custom color aliases
|
||
};
|
||
|
||
/**
|
||
* Colorpicker component class
|
||
*
|
||
* @param {Object|String} element
|
||
* @param {Object} options
|
||
* @constructor
|
||
*/
|
||
var Colorpicker = function(element, options) {
|
||
this.element = $(element).addClass('colorpicker-element');
|
||
this.options = $.extend(true, {}, defaults, this.element.data(), options);
|
||
this.component = this.options.component;
|
||
this.component = (this.component !== false) ? this.element.find(this.component) : false;
|
||
if (this.component && (this.component.length === 0)) {
|
||
this.component = false;
|
||
}
|
||
this.container = (this.options.container === true) ? this.element : this.options.container;
|
||
this.container = (this.container !== false) ? $(this.container) : false;
|
||
|
||
// Is the element an input? Should we search inside for any input?
|
||
this.input = this.element.is('input') ? this.element : (this.options.input ?
|
||
this.element.find(this.options.input) : false);
|
||
if (this.input && (this.input.length === 0)) {
|
||
this.input = false;
|
||
}
|
||
// Set HSB color
|
||
this.color = this.createColor(this.options.color !== false ? this.options.color : this.getValue());
|
||
|
||
this.format = this.options.format !== false ? this.options.format : this.color.origFormat;
|
||
|
||
if (this.options.color !== false) {
|
||
this.updateInput(this.color);
|
||
this.updateData(this.color);
|
||
}
|
||
|
||
this.disabled = false;
|
||
|
||
// Setup picker
|
||
var $picker = this.picker = $(this.options.template);
|
||
if (this.options.customClass) {
|
||
$picker.addClass(this.options.customClass);
|
||
}
|
||
if (this.options.inline) {
|
||
$picker.addClass('colorpicker-inline colorpicker-visible');
|
||
} else {
|
||
$picker.addClass('colorpicker-hidden');
|
||
}
|
||
if (this.options.horizontal) {
|
||
$picker.addClass('colorpicker-horizontal');
|
||
}
|
||
if (
|
||
(['rgba', 'hsla', 'alias'].indexOf(this.format) !== -1) ||
|
||
this.options.format === false ||
|
||
this.getValue() === 'transparent'
|
||
) {
|
||
$picker.addClass('colorpicker-with-alpha');
|
||
}
|
||
if (this.options.align === 'right') {
|
||
$picker.addClass('colorpicker-right');
|
||
}
|
||
if (this.options.inline === true) {
|
||
$picker.addClass('colorpicker-no-arrow');
|
||
}
|
||
if (this.options.colorSelectors) {
|
||
var colorpicker = this,
|
||
selectorsContainer = colorpicker.picker.find('.colorpicker-selectors');
|
||
|
||
if (selectorsContainer.length > 0) {
|
||
$.each(this.options.colorSelectors, function(name, color) {
|
||
var $btn = $('<i />')
|
||
.addClass('colorpicker-selectors-color')
|
||
.css('background-color', color)
|
||
.data('class', name).data('alias', name);
|
||
|
||
$btn.on('mousedown.colorpicker touchstart.colorpicker', function(event) {
|
||
event.preventDefault();
|
||
colorpicker.setValue(
|
||
colorpicker.format === 'alias' ? $(this).data('alias') : $(this).css('background-color')
|
||
);
|
||
});
|
||
selectorsContainer.append($btn);
|
||
});
|
||
selectorsContainer.show().addClass('colorpicker-visible');
|
||
}
|
||
}
|
||
|
||
// Prevent closing the colorpicker when clicking on itself
|
||
$picker.on('mousedown.colorpicker touchstart.colorpicker', $.proxy(function(e) {
|
||
if (e.target === e.currentTarget) {
|
||
e.preventDefault();
|
||
}
|
||
}, this));
|
||
|
||
// Bind click/tap events on the sliders
|
||
$picker.find('.colorpicker-saturation, .colorpicker-hue, .colorpicker-alpha')
|
||
.on('mousedown.colorpicker touchstart.colorpicker', $.proxy(this.mousedown, this));
|
||
|
||
$picker.appendTo(this.container ? this.container : $('body'));
|
||
|
||
// Bind other events
|
||
if (this.input !== false) {
|
||
this.input.on({
|
||
'keyup.colorpicker': $.proxy(this.keyup, this)
|
||
});
|
||
this.input.on({
|
||
'input.colorpicker': $.proxy(this.change, this)
|
||
});
|
||
if (this.component === false) {
|
||
this.element.on({
|
||
'focus.colorpicker': $.proxy(this.show, this)
|
||
});
|
||
}
|
||
if (this.options.inline === false) {
|
||
this.element.on({
|
||
'focusout.colorpicker': $.proxy(this.hide, this)
|
||
});
|
||
}
|
||
}
|
||
|
||
if (this.component !== false) {
|
||
this.component.on({
|
||
'click.colorpicker': $.proxy(this.show, this)
|
||
});
|
||
}
|
||
|
||
if ((this.input === false) && (this.component === false)) {
|
||
this.element.on({
|
||
'click.colorpicker': $.proxy(this.show, this)
|
||
});
|
||
}
|
||
|
||
// for HTML5 input[type='color']
|
||
if ((this.input !== false) && (this.component !== false) && (this.input.attr('type') === 'color')) {
|
||
|
||
this.input.on({
|
||
'click.colorpicker': $.proxy(this.show, this),
|
||
'focus.colorpicker': $.proxy(this.show, this)
|
||
});
|
||
}
|
||
this.update();
|
||
|
||
$($.proxy(function() {
|
||
this.element.trigger('create');
|
||
}, this));
|
||
};
|
||
|
||
Colorpicker.Color = Color;
|
||
|
||
Colorpicker.prototype = {
|
||
constructor: Colorpicker,
|
||
destroy: function() {
|
||
this.picker.remove();
|
||
this.element.removeData('colorpicker', 'color').off('.colorpicker');
|
||
if (this.input !== false) {
|
||
this.input.off('.colorpicker');
|
||
}
|
||
if (this.component !== false) {
|
||
this.component.off('.colorpicker');
|
||
}
|
||
this.element.removeClass('colorpicker-element');
|
||
this.element.trigger({
|
||
type: 'destroy'
|
||
});
|
||
},
|
||
reposition: function() {
|
||
if (this.options.inline !== false || this.options.container) {
|
||
return false;
|
||
}
|
||
var type = this.container && this.container[0] !== window.document.body ? 'position' : 'offset';
|
||
var element = this.component || this.element;
|
||
var offset = element[type]();
|
||
if (this.options.align === 'right') {
|
||
offset.left -= this.picker.outerWidth() - element.outerWidth();
|
||
}
|
||
this.picker.css({
|
||
top: offset.top + element.outerHeight(),
|
||
left: offset.left
|
||
});
|
||
},
|
||
show: function(e) {
|
||
if (this.isDisabled()) {
|
||
// Don't show the widget if it's disabled (the input)
|
||
return;
|
||
}
|
||
this.picker.addClass('colorpicker-visible').removeClass('colorpicker-hidden');
|
||
this.reposition();
|
||
$(window).on('resize.colorpicker', $.proxy(this.reposition, this));
|
||
if (e && (!this.hasInput() || this.input.attr('type') === 'color')) {
|
||
if (e.stopPropagation && e.preventDefault) {
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
if ((this.component || !this.input) && (this.options.inline === false)) {
|
||
$(window.document).on({
|
||
'mousedown.colorpicker': $.proxy(this.hide, this)
|
||
});
|
||
}
|
||
this.element.trigger({
|
||
type: 'showPicker',
|
||
color: this.color
|
||
});
|
||
},
|
||
hide: function(e) {
|
||
if ((typeof e !== 'undefined') && e.target) {
|
||
// Prevent hide if triggered by an event and an element inside the colorpicker has been clicked/touched
|
||
if (
|
||
$(e.currentTarget).parents('.colorpicker').length > 0 ||
|
||
$(e.target).parents('.colorpicker').length > 0
|
||
) {
|
||
return false;
|
||
}
|
||
}
|
||
this.picker.addClass('colorpicker-hidden').removeClass('colorpicker-visible');
|
||
$(window).off('resize.colorpicker', this.reposition);
|
||
$(window.document).off({
|
||
'mousedown.colorpicker': this.hide
|
||
});
|
||
this.update();
|
||
this.element.trigger({
|
||
type: 'hidePicker',
|
||
color: this.color
|
||
});
|
||
},
|
||
updateData: function(val) {
|
||
val = val || this.color.toString(false, this.format);
|
||
this.element.data('color', val);
|
||
return val;
|
||
},
|
||
updateInput: function(val) {
|
||
val = val || this.color.toString(false, this.format);
|
||
if (this.input !== false) {
|
||
this.input.prop('value', val);
|
||
this.input.trigger('change');
|
||
}
|
||
return val;
|
||
},
|
||
updatePicker: function(val) {
|
||
if (typeof val !== 'undefined') {
|
||
this.color = this.createColor(val);
|
||
}
|
||
var sl = (this.options.horizontal === false) ? this.options.sliders : this.options.slidersHorz;
|
||
var icns = this.picker.find('i');
|
||
if (icns.length === 0) {
|
||
return;
|
||
}
|
||
if (this.options.horizontal === false) {
|
||
sl = this.options.sliders;
|
||
icns.eq(1).css('top', sl.hue.maxTop * (1 - this.color.value.h)).end()
|
||
.eq(2).css('top', sl.alpha.maxTop * (1 - this.color.value.a));
|
||
} else {
|
||
sl = this.options.slidersHorz;
|
||
icns.eq(1).css('left', sl.hue.maxLeft * (1 - this.color.value.h)).end()
|
||
.eq(2).css('left', sl.alpha.maxLeft * (1 - this.color.value.a));
|
||
}
|
||
icns.eq(0).css({
|
||
'top': sl.saturation.maxTop - this.color.value.b * sl.saturation.maxTop,
|
||
'left': this.color.value.s * sl.saturation.maxLeft
|
||
});
|
||
|
||
this.picker.find('.colorpicker-saturation')
|
||
.css('backgroundColor', this.color.toHex(true, this.color.value.h, 1, 1, 1));
|
||
|
||
this.picker.find('.colorpicker-alpha')
|
||
.css('backgroundColor', this.color.toHex(true));
|
||
|
||
this.picker.find('.colorpicker-color, .colorpicker-color div')
|
||
.css('backgroundColor', this.color.toString(true, this.format));
|
||
|
||
return val;
|
||
},
|
||
updateComponent: function(val) {
|
||
var color;
|
||
|
||
if (typeof val !== 'undefined') {
|
||
color = this.createColor(val);
|
||
} else {
|
||
color = this.color;
|
||
}
|
||
|
||
if (this.component !== false) {
|
||
var icn = this.component.find('i').eq(0);
|
||
if (icn.length > 0) {
|
||
icn.css({
|
||
'backgroundColor': color.toString(true, this.format)
|
||
});
|
||
} else {
|
||
this.component.css({
|
||
'backgroundColor': color.toString(true, this.format)
|
||
});
|
||
}
|
||
}
|
||
|
||
return color.toString(false, this.format);
|
||
},
|
||
update: function(force) {
|
||
var val;
|
||
if ((this.getValue(false) !== false) || (force === true)) {
|
||
// Update input/data only if the current value is not empty
|
||
val = this.updateComponent();
|
||
this.updateInput(val);
|
||
this.updateData(val);
|
||
this.updatePicker(); // only update picker if value is not empty
|
||
}
|
||
return val;
|
||
|
||
},
|
||
setValue: function(val) { // set color manually
|
||
this.color = this.createColor(val);
|
||
this.update(true);
|
||
this.element.trigger({
|
||
type: 'changeColor',
|
||
color: this.color,
|
||
value: val
|
||
});
|
||
},
|
||
/**
|
||
* Creates a new color using the instance options
|
||
* @protected
|
||
* @param {String} val
|
||
* @returns {Color}
|
||
*/
|
||
createColor: function(val) {
|
||
return new Color(
|
||
val ? val : null,
|
||
this.options.colorSelectors,
|
||
this.options.fallbackColor ? this.options.fallbackColor : this.color,
|
||
this.options.fallbackFormat,
|
||
this.options.hexNumberSignPrefix
|
||
);
|
||
},
|
||
getValue: function(defaultValue) {
|
||
defaultValue = (typeof defaultValue === 'undefined') ? this.options.fallbackColor : defaultValue;
|
||
var val;
|
||
if (this.hasInput()) {
|
||
val = this.input.val();
|
||
} else {
|
||
val = this.element.data('color');
|
||
}
|
||
if ((val === undefined) || (val === '') || (val === null)) {
|
||
// if not defined or empty, return default
|
||
val = defaultValue;
|
||
}
|
||
return val;
|
||
},
|
||
hasInput: function() {
|
||
return (this.input !== false);
|
||
},
|
||
isDisabled: function() {
|
||
return this.disabled;
|
||
},
|
||
disable: function() {
|
||
if (this.hasInput()) {
|
||
this.input.prop('disabled', true);
|
||
}
|
||
this.disabled = true;
|
||
this.element.trigger({
|
||
type: 'disable',
|
||
color: this.color,
|
||
value: this.getValue()
|
||
});
|
||
return true;
|
||
},
|
||
enable: function() {
|
||
if (this.hasInput()) {
|
||
this.input.prop('disabled', false);
|
||
}
|
||
this.disabled = false;
|
||
this.element.trigger({
|
||
type: 'enable',
|
||
color: this.color,
|
||
value: this.getValue()
|
||
});
|
||
return true;
|
||
},
|
||
currentSlider: null,
|
||
mousePointer: {
|
||
left: 0,
|
||
top: 0
|
||
},
|
||
mousedown: function(e) {
|
||
if (!e.pageX && !e.pageY && e.originalEvent && e.originalEvent.touches) {
|
||
e.pageX = e.originalEvent.touches[0].pageX;
|
||
e.pageY = e.originalEvent.touches[0].pageY;
|
||
}
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
|
||
var target = $(e.target);
|
||
|
||
//detect the slider and set the limits and callbacks
|
||
var zone = target.closest('div');
|
||
var sl = this.options.horizontal ? this.options.slidersHorz : this.options.sliders;
|
||
if (!zone.is('.colorpicker')) {
|
||
if (zone.is('.colorpicker-saturation')) {
|
||
this.currentSlider = $.extend({}, sl.saturation);
|
||
} else if (zone.is('.colorpicker-hue')) {
|
||
this.currentSlider = $.extend({}, sl.hue);
|
||
} else if (zone.is('.colorpicker-alpha')) {
|
||
this.currentSlider = $.extend({}, sl.alpha);
|
||
} else {
|
||
return false;
|
||
}
|
||
var offset = zone.offset();
|
||
//reference to guide's style
|
||
this.currentSlider.guide = zone.find('i')[0].style;
|
||
this.currentSlider.left = e.pageX - offset.left;
|
||
this.currentSlider.top = e.pageY - offset.top;
|
||
this.mousePointer = {
|
||
left: e.pageX,
|
||
top: e.pageY
|
||
};
|
||
//trigger mousemove to move the guide to the current position
|
||
$(window.document).on({
|
||
'mousemove.colorpicker': $.proxy(this.mousemove, this),
|
||
'touchmove.colorpicker': $.proxy(this.mousemove, this),
|
||
'mouseup.colorpicker': $.proxy(this.mouseup, this),
|
||
'touchend.colorpicker': $.proxy(this.mouseup, this)
|
||
}).trigger('mousemove');
|
||
}
|
||
return false;
|
||
},
|
||
mousemove: function(e) {
|
||
if (!e.pageX && !e.pageY && e.originalEvent && e.originalEvent.touches) {
|
||
e.pageX = e.originalEvent.touches[0].pageX;
|
||
e.pageY = e.originalEvent.touches[0].pageY;
|
||
}
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
var left = Math.max(
|
||
0,
|
||
Math.min(
|
||
this.currentSlider.maxLeft,
|
||
this.currentSlider.left + ((e.pageX || this.mousePointer.left) - this.mousePointer.left)
|
||
)
|
||
);
|
||
var top = Math.max(
|
||
0,
|
||
Math.min(
|
||
this.currentSlider.maxTop,
|
||
this.currentSlider.top + ((e.pageY || this.mousePointer.top) - this.mousePointer.top)
|
||
)
|
||
);
|
||
this.currentSlider.guide.left = left + 'px';
|
||
this.currentSlider.guide.top = top + 'px';
|
||
if (this.currentSlider.callLeft) {
|
||
this.color[this.currentSlider.callLeft].call(this.color, left / this.currentSlider.maxLeft);
|
||
}
|
||
if (this.currentSlider.callTop) {
|
||
this.color[this.currentSlider.callTop].call(this.color, top / this.currentSlider.maxTop);
|
||
}
|
||
// Change format dynamically
|
||
// Only occurs if user choose the dynamic format by
|
||
// setting option format to false
|
||
if (
|
||
this.options.format === false &&
|
||
(this.currentSlider.callTop === 'setAlpha' ||
|
||
this.currentSlider.callLeft === 'setAlpha')
|
||
) {
|
||
|
||
// Converting from hex / rgb to rgba
|
||
if (this.color.value.a !== 1) {
|
||
this.format = 'rgba';
|
||
this.color.origFormat = 'rgba';
|
||
}
|
||
|
||
// Converting from rgba to hex
|
||
else {
|
||
this.format = 'hex';
|
||
this.color.origFormat = 'hex';
|
||
}
|
||
}
|
||
this.update(true);
|
||
|
||
this.element.trigger({
|
||
type: 'changeColor',
|
||
color: this.color
|
||
});
|
||
return false;
|
||
},
|
||
mouseup: function(e) {
|
||
e.stopPropagation();
|
||
e.preventDefault();
|
||
$(window.document).off({
|
||
'mousemove.colorpicker': this.mousemove,
|
||
'touchmove.colorpicker': this.mousemove,
|
||
'mouseup.colorpicker': this.mouseup,
|
||
'touchend.colorpicker': this.mouseup
|
||
});
|
||
return false;
|
||
},
|
||
change: function(e) {
|
||
this.color = this.createColor(this.input.val());
|
||
// Change format dynamically
|
||
// Only occurs if user choose the dynamic format by
|
||
// setting option format to false
|
||
if (this.color.origFormat && this.options.format === false) {
|
||
this.format = this.color.origFormat;
|
||
}
|
||
if (this.getValue(false) !== false) {
|
||
this.updateData();
|
||
this.updateComponent();
|
||
this.updatePicker();
|
||
}
|
||
|
||
this.element.trigger({
|
||
type: 'changeColor',
|
||
color: this.color,
|
||
value: this.input.val()
|
||
});
|
||
},
|
||
keyup: function(e) {
|
||
if ((e.keyCode === 38)) {
|
||
if (this.color.value.a < 1) {
|
||
this.color.value.a = Math.round((this.color.value.a + 0.01) * 100) / 100;
|
||
}
|
||
this.update(true);
|
||
} else if ((e.keyCode === 40)) {
|
||
if (this.color.value.a > 0) {
|
||
this.color.value.a = Math.round((this.color.value.a - 0.01) * 100) / 100;
|
||
}
|
||
this.update(true);
|
||
}
|
||
|
||
this.element.trigger({
|
||
type: 'changeColor',
|
||
color: this.color,
|
||
value: this.input.val()
|
||
});
|
||
}
|
||
};
|
||
|
||
$.colorpicker = Colorpicker;
|
||
|
||
$.fn.colorpicker = function(option) {
|
||
var apiArgs = Array.prototype.slice.call(arguments, 1),
|
||
isSingleElement = (this.length === 1),
|
||
returnValue = null;
|
||
|
||
var $jq = this.each(function() {
|
||
var $this = $(this),
|
||
inst = $this.data('colorpicker'),
|
||
options = ((typeof option === 'object') ? option : {});
|
||
|
||
if (!inst) {
|
||
inst = new Colorpicker(this, options);
|
||
$this.data('colorpicker', inst);
|
||
}
|
||
|
||
if (typeof option === 'string') {
|
||
if ($.isFunction(inst[option])) {
|
||
returnValue = inst[option].apply(inst, apiArgs);
|
||
} else { // its a property ?
|
||
if (apiArgs.length) {
|
||
// set property
|
||
inst[option] = apiArgs[0];
|
||
}
|
||
returnValue = inst[option];
|
||
}
|
||
} else {
|
||
returnValue = $this;
|
||
}
|
||
});
|
||
return isSingleElement ? returnValue : $jq;
|
||
};
|
||
|
||
$.fn.colorpicker.constructor = Colorpicker;
|
||
|
||
}));
|
||
|
||
/*!
|
||
* Datepicker for Bootstrap v1.10.0 (https://github.com/uxsolutions/bootstrap-datepicker)
|
||
*
|
||
* Licensed under the Apache License v2.0 (https://www.apache.org/licenses/LICENSE-2.0)
|
||
*/
|
||
|
||
(function(factory){
|
||
if (typeof define === 'function' && define.amd) {
|
||
define(['jquery'], factory);
|
||
} else if (typeof exports === 'object') {
|
||
factory(require('jquery'));
|
||
} else {
|
||
factory(jQuery);
|
||
}
|
||
}(function($, undefined){
|
||
function UTCDate(){
|
||
return new Date(Date.UTC.apply(Date, arguments));
|
||
}
|
||
function UTCToday(){
|
||
var today = new Date();
|
||
return UTCDate(today.getFullYear(), today.getMonth(), today.getDate());
|
||
}
|
||
function isUTCEquals(date1, date2) {
|
||
return (
|
||
date1.getUTCFullYear() === date2.getUTCFullYear() &&
|
||
date1.getUTCMonth() === date2.getUTCMonth() &&
|
||
date1.getUTCDate() === date2.getUTCDate()
|
||
);
|
||
}
|
||
function alias(method, deprecationMsg){
|
||
return function(){
|
||
if (deprecationMsg !== undefined) {
|
||
$.fn.datepicker.deprecated(deprecationMsg);
|
||
}
|
||
|
||
return this[method].apply(this, arguments);
|
||
};
|
||
}
|
||
function isValidDate(d) {
|
||
return d && !isNaN(d.getTime());
|
||
}
|
||
|
||
var DateArray = (function(){
|
||
var extras = {
|
||
get: function(i){
|
||
return this.slice(i)[0];
|
||
},
|
||
contains: function(d){
|
||
// Array.indexOf is not cross-browser;
|
||
// $.inArray doesn't work with Dates
|
||
var val = d && d.valueOf();
|
||
for (var i=0, l=this.length; i < l; i++)
|
||
// Use date arithmetic to allow dates with different times to match
|
||
if (0 <= this[i].valueOf() - val && this[i].valueOf() - val < 1000*60*60*24)
|
||
return i;
|
||
return -1;
|
||
},
|
||
remove: function(i){
|
||
this.splice(i,1);
|
||
},
|
||
replace: function(new_array){
|
||
if (!new_array)
|
||
return;
|
||
if (!Array.isArray(new_array))
|
||
new_array = [new_array];
|
||
this.clear();
|
||
this.push.apply(this, new_array);
|
||
},
|
||
clear: function(){
|
||
this.length = 0;
|
||
},
|
||
copy: function(){
|
||
var a = new DateArray();
|
||
a.replace(this);
|
||
return a;
|
||
}
|
||
};
|
||
|
||
return function(){
|
||
var a = [];
|
||
a.push.apply(a, arguments);
|
||
$.extend(a, extras);
|
||
return a;
|
||
};
|
||
})();
|
||
|
||
|
||
// Picker object
|
||
|
||
var Datepicker = function(element, options){
|
||
$.data(element, 'datepicker', this);
|
||
|
||
this._events = [];
|
||
this._secondaryEvents = [];
|
||
|
||
this._process_options(options);
|
||
|
||
this.dates = new DateArray();
|
||
this.viewDate = this.o.defaultViewDate;
|
||
this.focusDate = null;
|
||
|
||
this.element = $(element);
|
||
this.isInput = this.element.is('input');
|
||
this.inputField = this.isInput ? this.element : this.element.find('input');
|
||
this.component = this.element.hasClass('date') ? this.element.find('.add-on, .input-group-addon, .input-group-append, .input-group-prepend, .btn') : false;
|
||
if (this.component && this.component.length === 0){
|
||
this.component = false;
|
||
}
|
||
|
||
if (this.o.isInline === null){
|
||
this.isInline = !this.component && !this.isInput;
|
||
} else {
|
||
this.isInline = this.o.isInline;
|
||
}
|
||
|
||
this.picker = $(DPGlobal.template);
|
||
|
||
// Checking templates and inserting
|
||
if (this._check_template(this.o.templates.leftArrow)) {
|
||
this.picker.find('.prev').html(this.o.templates.leftArrow);
|
||
}
|
||
|
||
if (this._check_template(this.o.templates.rightArrow)) {
|
||
this.picker.find('.next').html(this.o.templates.rightArrow);
|
||
}
|
||
|
||
this._buildEvents();
|
||
this._attachEvents();
|
||
|
||
if (this.isInline){
|
||
this.picker.addClass('datepicker-inline').appendTo(this.element);
|
||
}
|
||
else {
|
||
this.picker.addClass('datepicker-dropdown dropdown-menu');
|
||
}
|
||
|
||
if (this.o.rtl){
|
||
this.picker.addClass('datepicker-rtl');
|
||
}
|
||
|
||
if (this.o.calendarWeeks) {
|
||
this.picker.find('.datepicker-days .datepicker-switch, thead .datepicker-title, tfoot .today, tfoot .clear')
|
||
.attr('colspan', function(i, val){
|
||
return Number(val) + 1;
|
||
});
|
||
}
|
||
|
||
this._process_options({
|
||
startDate: this._o.startDate,
|
||
endDate: this._o.endDate,
|
||
daysOfWeekDisabled: this.o.daysOfWeekDisabled,
|
||
daysOfWeekHighlighted: this.o.daysOfWeekHighlighted,
|
||
datesDisabled: this.o.datesDisabled
|
||
});
|
||
|
||
this._allow_update = false;
|
||
this.setViewMode(this.o.startView);
|
||
this._allow_update = true;
|
||
|
||
this.fillDow();
|
||
this.fillMonths();
|
||
|
||
this.update();
|
||
|
||
if (this.isInline){
|
||
this.show();
|
||
}
|
||
};
|
||
|
||
Datepicker.prototype = {
|
||
constructor: Datepicker,
|
||
|
||
_resolveViewName: function(view){
|
||
$.each(DPGlobal.viewModes, function(i, viewMode){
|
||
if (view === i || $.inArray(view, viewMode.names) !== -1){
|
||
view = i;
|
||
return false;
|
||
}
|
||
});
|
||
|
||
return view;
|
||
},
|
||
|
||
_resolveDaysOfWeek: function(daysOfWeek){
|
||
if (!Array.isArray(daysOfWeek))
|
||
daysOfWeek = daysOfWeek.split(/[,\s]*/);
|
||
return $.map(daysOfWeek, Number);
|
||
},
|
||
|
||
_check_template: function(tmp){
|
||
try {
|
||
// If empty
|
||
if (tmp === undefined || tmp === "") {
|
||
return false;
|
||
}
|
||
// If no html, everything ok
|
||
if ((tmp.match(/[<>]/g) || []).length <= 0) {
|
||
return true;
|
||
}
|
||
// Checking if html is fine
|
||
var jDom = $(tmp);
|
||
return jDom.length > 0;
|
||
}
|
||
catch (ex) {
|
||
return false;
|
||
}
|
||
},
|
||
|
||
_process_options: function(opts){
|
||
// Store raw options for reference
|
||
this._o = $.extend({}, this._o, opts);
|
||
// Processed options
|
||
var o = this.o = $.extend({}, this._o);
|
||
|
||
// Check if "de-DE" style date is available, if not language should
|
||
// fallback to 2 letter code eg "de"
|
||
var lang = o.language;
|
||
if (!dates[lang]){
|
||
lang = lang.split('-')[0];
|
||
if (!dates[lang])
|
||
lang = defaults.language;
|
||
}
|
||
o.language = lang;
|
||
|
||
// Retrieve view index from any aliases
|
||
o.startView = this._resolveViewName(o.startView);
|
||
o.minViewMode = this._resolveViewName(o.minViewMode);
|
||
o.maxViewMode = this._resolveViewName(o.maxViewMode);
|
||
|
||
// Check view is between min and max
|
||
o.startView = Math.max(this.o.minViewMode, Math.min(this.o.maxViewMode, o.startView));
|
||
|
||
// true, false, or Number > 0
|
||
if (o.multidate !== true){
|
||
o.multidate = Number(o.multidate) || false;
|
||
if (o.multidate !== false)
|
||
o.multidate = Math.max(0, o.multidate);
|
||
}
|
||
o.multidateSeparator = String(o.multidateSeparator);
|
||
|
||
o.weekStart %= 7;
|
||
o.weekEnd = (o.weekStart + 6) % 7;
|
||
|
||
var format = DPGlobal.parseFormat(o.format);
|
||
if (o.startDate !== -Infinity){
|
||
if (!!o.startDate){
|
||
if (o.startDate instanceof Date)
|
||
o.startDate = this._local_to_utc(this._zero_time(o.startDate));
|
||
else
|
||
o.startDate = DPGlobal.parseDate(o.startDate, format, o.language, o.assumeNearbyYear);
|
||
}
|
||
else {
|
||
o.startDate = -Infinity;
|
||
}
|
||
}
|
||
if (o.endDate !== Infinity){
|
||
if (!!o.endDate){
|
||
if (o.endDate instanceof Date)
|
||
o.endDate = this._local_to_utc(this._zero_time(o.endDate));
|
||
else
|
||
o.endDate = DPGlobal.parseDate(o.endDate, format, o.language, o.assumeNearbyYear);
|
||
}
|
||
else {
|
||
o.endDate = Infinity;
|
||
}
|
||
}
|
||
|
||
o.daysOfWeekDisabled = this._resolveDaysOfWeek(o.daysOfWeekDisabled||[]);
|
||
o.daysOfWeekHighlighted = this._resolveDaysOfWeek(o.daysOfWeekHighlighted||[]);
|
||
|
||
o.datesDisabled = o.datesDisabled||[];
|
||
if (!Array.isArray(o.datesDisabled)) {
|
||
o.datesDisabled = o.datesDisabled.split(',');
|
||
}
|
||
o.datesDisabled = $.map(o.datesDisabled, function(d){
|
||
return DPGlobal.parseDate(d, format, o.language, o.assumeNearbyYear);
|
||
});
|
||
|
||
var plc = String(o.orientation).toLowerCase().split(/\s+/g),
|
||
_plc = o.orientation.toLowerCase();
|
||
plc = $.grep(plc, function(word){
|
||
return /^auto|left|right|top|bottom$/.test(word);
|
||
});
|
||
o.orientation = {x: 'auto', y: 'auto'};
|
||
if (!_plc || _plc === 'auto')
|
||
; // no action
|
||
else if (plc.length === 1){
|
||
switch (plc[0]){
|
||
case 'top':
|
||
case 'bottom':
|
||
o.orientation.y = plc[0];
|
||
break;
|
||
case 'left':
|
||
case 'right':
|
||
o.orientation.x = plc[0];
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
_plc = $.grep(plc, function(word){
|
||
return /^left|right$/.test(word);
|
||
});
|
||
o.orientation.x = _plc[0] || 'auto';
|
||
|
||
_plc = $.grep(plc, function(word){
|
||
return /^top|bottom$/.test(word);
|
||
});
|
||
o.orientation.y = _plc[0] || 'auto';
|
||
}
|
||
if (o.defaultViewDate instanceof Date || typeof o.defaultViewDate === 'string') {
|
||
o.defaultViewDate = DPGlobal.parseDate(o.defaultViewDate, format, o.language, o.assumeNearbyYear);
|
||
} else if (o.defaultViewDate) {
|
||
var year = o.defaultViewDate.year || new Date().getFullYear();
|
||
var month = o.defaultViewDate.month || 0;
|
||
var day = o.defaultViewDate.day || 1;
|
||
o.defaultViewDate = UTCDate(year, month, day);
|
||
} else {
|
||
o.defaultViewDate = UTCToday();
|
||
}
|
||
},
|
||
_applyEvents: function(evs){
|
||
for (var i=0, el, ch, ev; i < evs.length; i++){
|
||
el = evs[i][0];
|
||
if (evs[i].length === 2){
|
||
ch = undefined;
|
||
ev = evs[i][1];
|
||
} else if (evs[i].length === 3){
|
||
ch = evs[i][1];
|
||
ev = evs[i][2];
|
||
}
|
||
el.on(ev, ch);
|
||
}
|
||
},
|
||
_unapplyEvents: function(evs){
|
||
for (var i=0, el, ev, ch; i < evs.length; i++){
|
||
el = evs[i][0];
|
||
if (evs[i].length === 2){
|
||
ch = undefined;
|
||
ev = evs[i][1];
|
||
} else if (evs[i].length === 3){
|
||
ch = evs[i][1];
|
||
ev = evs[i][2];
|
||
}
|
||
el.off(ev, ch);
|
||
}
|
||
},
|
||
_buildEvents: function(){
|
||
var events = {
|
||
keyup: $.proxy(function(e){
|
||
if ($.inArray(e.keyCode, [27, 37, 39, 38, 40, 32, 13, 9]) === -1)
|
||
this.update();
|
||
}, this),
|
||
keydown: $.proxy(this.keydown, this),
|
||
paste: $.proxy(this.paste, this)
|
||
};
|
||
|
||
if (this.o.showOnFocus === true) {
|
||
events.focus = $.proxy(this.show, this);
|
||
}
|
||
|
||
if (this.isInput) { // single input
|
||
this._events = [
|
||
[this.element, events]
|
||
];
|
||
}
|
||
// component: input + button
|
||
else if (this.component && this.inputField.length) {
|
||
this._events = [
|
||
// For components that are not readonly, allow keyboard nav
|
||
[this.inputField, events],
|
||
[this.component, {
|
||
click: $.proxy(this.show, this)
|
||
}]
|
||
];
|
||
}
|
||
else {
|
||
this._events = [
|
||
[this.element, {
|
||
click: $.proxy(this.show, this),
|
||
keydown: $.proxy(this.keydown, this)
|
||
}]
|
||
];
|
||
}
|
||
this._events.push(
|
||
// Component: listen for blur on element descendants
|
||
[this.element, '*', {
|
||
blur: $.proxy(function(e){
|
||
this._focused_from = e.target;
|
||
}, this)
|
||
}],
|
||
// Input: listen for blur on element
|
||
[this.element, {
|
||
blur: $.proxy(function(e){
|
||
this._focused_from = e.target;
|
||
}, this)
|
||
}]
|
||
);
|
||
|
||
if (this.o.immediateUpdates) {
|
||
// Trigger input updates immediately on changed year/month
|
||
this._events.push([this.element, {
|
||
'changeYear changeMonth': $.proxy(function(e){
|
||
this.update(e.date);
|
||
}, this)
|
||
}]);
|
||
}
|
||
|
||
this._secondaryEvents = [
|
||
[this.picker, {
|
||
click: $.proxy(this.click, this)
|
||
}],
|
||
[this.picker, '.prev, .next', {
|
||
click: $.proxy(this.navArrowsClick, this)
|
||
}],
|
||
[this.picker, '.day:not(.disabled)', {
|
||
click: $.proxy(this.dayCellClick, this)
|
||
}],
|
||
[$(window), {
|
||
resize: $.proxy(this.place, this)
|
||
}],
|
||
[$(document), {
|
||
'mousedown touchstart': $.proxy(function(e){
|
||
// Clicked outside the datepicker, hide it
|
||
if (!(
|
||
this.element.is(e.target) ||
|
||
this.element.find(e.target).length ||
|
||
this.picker.is(e.target) ||
|
||
this.picker.find(e.target).length ||
|
||
this.isInline
|
||
)){
|
||
this.hide();
|
||
}
|
||
}, this)
|
||
}]
|
||
];
|
||
},
|
||
_attachEvents: function(){
|
||
this._detachEvents();
|
||
this._applyEvents(this._events);
|
||
},
|
||
_detachEvents: function(){
|
||
this._unapplyEvents(this._events);
|
||
},
|
||
_attachSecondaryEvents: function(){
|
||
this._detachSecondaryEvents();
|
||
this._applyEvents(this._secondaryEvents);
|
||
},
|
||
_detachSecondaryEvents: function(){
|
||
this._unapplyEvents(this._secondaryEvents);
|
||
},
|
||
_trigger: function(event, altdate){
|
||
var date = altdate || this.dates.get(-1),
|
||
local_date = this._utc_to_local(date);
|
||
|
||
this.element.trigger({
|
||
type: event,
|
||
date: local_date,
|
||
viewMode: this.viewMode,
|
||
dates: $.map(this.dates, this._utc_to_local),
|
||
format: $.proxy(function(ix, format){
|
||
if (arguments.length === 0){
|
||
ix = this.dates.length - 1;
|
||
format = this.o.format;
|
||
} else if (typeof ix === 'string'){
|
||
format = ix;
|
||
ix = this.dates.length - 1;
|
||
}
|
||
format = format || this.o.format;
|
||
var date = this.dates.get(ix);
|
||
return DPGlobal.formatDate(date, format, this.o.language);
|
||
}, this)
|
||
});
|
||
},
|
||
|
||
show: function(){
|
||
if (this.inputField.is(':disabled') || (this.inputField.prop('readonly') && this.o.enableOnReadonly === false))
|
||
return;
|
||
if (!this.isInline)
|
||
this.picker.appendTo(this.o.container);
|
||
this.place();
|
||
this.picker.show();
|
||
this._attachSecondaryEvents();
|
||
this._trigger('show');
|
||
if ((window.navigator.msMaxTouchPoints || 'ontouchstart' in document) && this.o.disableTouchKeyboard) {
|
||
$(this.element).blur();
|
||
}
|
||
return this;
|
||
},
|
||
|
||
hide: function(){
|
||
if (this.isInline || !this.picker.is(':visible'))
|
||
return this;
|
||
this.focusDate = null;
|
||
this.picker.hide().detach();
|
||
this._detachSecondaryEvents();
|
||
this.setViewMode(this.o.startView);
|
||
|
||
if (this.o.forceParse && this.inputField.val())
|
||
this.setValue();
|
||
this._trigger('hide');
|
||
return this;
|
||
},
|
||
|
||
destroy: function(){
|
||
this.hide();
|
||
this._detachEvents();
|
||
this._detachSecondaryEvents();
|
||
this.picker.remove();
|
||
delete this.element.data().datepicker;
|
||
if (!this.isInput){
|
||
delete this.element.data().date;
|
||
}
|
||
return this;
|
||
},
|
||
|
||
paste: function(e){
|
||
var dateString;
|
||
if (e.originalEvent.clipboardData && e.originalEvent.clipboardData.types
|
||
&& $.inArray('text/plain', e.originalEvent.clipboardData.types) !== -1) {
|
||
dateString = e.originalEvent.clipboardData.getData('text/plain');
|
||
} else if (window.clipboardData) {
|
||
dateString = window.clipboardData.getData('Text');
|
||
} else {
|
||
return;
|
||
}
|
||
this.setDate(dateString);
|
||
this.update();
|
||
e.preventDefault();
|
||
},
|
||
|
||
_utc_to_local: function(utc){
|
||
if (!utc) {
|
||
return utc;
|
||
}
|
||
|
||
var local = new Date(utc.getTime() + (utc.getTimezoneOffset() * 60000));
|
||
|
||
if (local.getTimezoneOffset() !== utc.getTimezoneOffset()) {
|
||
local = new Date(utc.getTime() + (local.getTimezoneOffset() * 60000));
|
||
}
|
||
|
||
return local;
|
||
},
|
||
_local_to_utc: function(local){
|
||
return local && new Date(local.getTime() - (local.getTimezoneOffset()*60000));
|
||
},
|
||
_zero_time: function(local){
|
||
return local && new Date(local.getFullYear(), local.getMonth(), local.getDate());
|
||
},
|
||
_zero_utc_time: function(utc){
|
||
return utc && UTCDate(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate());
|
||
},
|
||
|
||
getDates: function(){
|
||
return $.map(this.dates, this._utc_to_local);
|
||
},
|
||
|
||
getUTCDates: function(){
|
||
return $.map(this.dates, function(d){
|
||
return new Date(d);
|
||
});
|
||
},
|
||
|
||
getDate: function(){
|
||
return this._utc_to_local(this.getUTCDate());
|
||
},
|
||
|
||
getUTCDate: function(){
|
||
var selected_date = this.dates.get(-1);
|
||
if (selected_date !== undefined) {
|
||
return new Date(selected_date);
|
||
} else {
|
||
return null;
|
||
}
|
||
},
|
||
|
||
clearDates: function(){
|
||
this.inputField.val('');
|
||
this._trigger('changeDate');
|
||
this.update();
|
||
if (this.o.autoclose) {
|
||
this.hide();
|
||
}
|
||
},
|
||
|
||
setDates: function(){
|
||
var args = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
||
this.update.apply(this, args);
|
||
this._trigger('changeDate');
|
||
this.setValue();
|
||
return this;
|
||
},
|
||
|
||
setUTCDates: function(){
|
||
var args = Array.isArray(arguments[0]) ? arguments[0] : arguments;
|
||
this.setDates.apply(this, $.map(args, this._utc_to_local));
|
||
return this;
|
||
},
|
||
|
||
setDate: alias('setDates'),
|
||
setUTCDate: alias('setUTCDates'),
|
||
remove: alias('destroy', 'Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead'),
|
||
|
||
setValue: function(){
|
||
var formatted = this.getFormattedDate();
|
||
this.inputField.val(formatted);
|
||
return this;
|
||
},
|
||
|
||
getFormattedDate: function(format){
|
||
if (format === undefined)
|
||
format = this.o.format;
|
||
|
||
var lang = this.o.language;
|
||
return $.map(this.dates, function(d){
|
||
return DPGlobal.formatDate(d, format, lang);
|
||
}).join(this.o.multidateSeparator);
|
||
},
|
||
|
||
getStartDate: function(){
|
||
return this.o.startDate;
|
||
},
|
||
|
||
setStartDate: function(startDate){
|
||
this._process_options({startDate: startDate});
|
||
this.update();
|
||
this.updateNavArrows();
|
||
return this;
|
||
},
|
||
|
||
getEndDate: function(){
|
||
return this.o.endDate;
|
||
},
|
||
|
||
setEndDate: function(endDate){
|
||
this._process_options({endDate: endDate});
|
||
this.update();
|
||
this.updateNavArrows();
|
||
return this;
|
||
},
|
||
|
||
setDaysOfWeekDisabled: function(daysOfWeekDisabled){
|
||
this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
|
||
this.update();
|
||
return this;
|
||
},
|
||
|
||
setDaysOfWeekHighlighted: function(daysOfWeekHighlighted){
|
||
this._process_options({daysOfWeekHighlighted: daysOfWeekHighlighted});
|
||
this.update();
|
||
return this;
|
||
},
|
||
|
||
setDatesDisabled: function(datesDisabled){
|
||
this._process_options({datesDisabled: datesDisabled});
|
||
this.update();
|
||
return this;
|
||
},
|
||
|
||
place: function(){
|
||
if (this.isInline)
|
||
return this;
|
||
var calendarWidth = this.picker.outerWidth(),
|
||
calendarHeight = this.picker.outerHeight(),
|
||
visualPadding = 10,
|
||
container = $(this.o.container),
|
||
windowWidth = container.width(),
|
||
scrollTop = this.o.container === 'body' ? $(document).scrollTop() : container.scrollTop(),
|
||
appendOffset = container.offset();
|
||
|
||
var parentsZindex = [0];
|
||
this.element.parents().each(function(){
|
||
var itemZIndex = $(this).css('z-index');
|
||
if (itemZIndex !== 'auto' && Number(itemZIndex) !== 0) parentsZindex.push(Number(itemZIndex));
|
||
});
|
||
var zIndex = Math.max.apply(Math, parentsZindex) + this.o.zIndexOffset;
|
||
var offset = this.component ? this.component.parent().offset() : this.element.offset();
|
||
var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(false);
|
||
var width = this.component ? this.component.outerWidth(true) : this.element.outerWidth(false);
|
||
var left = offset.left - appendOffset.left;
|
||
var top = offset.top - appendOffset.top;
|
||
|
||
if (this.o.container !== 'body') {
|
||
top += scrollTop;
|
||
}
|
||
|
||
this.picker.removeClass(
|
||
'datepicker-orient-top datepicker-orient-bottom '+
|
||
'datepicker-orient-right datepicker-orient-left'
|
||
);
|
||
|
||
if (this.o.orientation.x !== 'auto'){
|
||
this.picker.addClass('datepicker-orient-' + this.o.orientation.x);
|
||
if (this.o.orientation.x === 'right')
|
||
left -= calendarWidth - width;
|
||
}
|
||
// auto x orientation is best-placement: if it crosses a window
|
||
// edge, fudge it sideways
|
||
else {
|
||
if (offset.left < 0) {
|
||
// component is outside the window on the left side. Move it into visible range
|
||
this.picker.addClass('datepicker-orient-left');
|
||
left -= offset.left - visualPadding;
|
||
} else if (left + calendarWidth > windowWidth) {
|
||
// the calendar passes the widow right edge. Align it to component right side
|
||
this.picker.addClass('datepicker-orient-right');
|
||
left += width - calendarWidth;
|
||
} else {
|
||
if (this.o.rtl) {
|
||
// Default to right
|
||
this.picker.addClass('datepicker-orient-right');
|
||
} else {
|
||
// Default to left
|
||
this.picker.addClass('datepicker-orient-left');
|
||
}
|
||
}
|
||
}
|
||
|
||
// auto y orientation is best-situation: top or bottom, no fudging,
|
||
// decision based on which shows more of the calendar
|
||
var yorient = this.o.orientation.y,
|
||
top_overflow;
|
||
if (yorient === 'auto'){
|
||
top_overflow = -scrollTop + top - calendarHeight;
|
||
yorient = top_overflow < 0 ? 'bottom' : 'top';
|
||
}
|
||
|
||
this.picker.addClass('datepicker-orient-' + yorient);
|
||
if (yorient === 'top')
|
||
top -= calendarHeight + parseInt(this.picker.css('padding-top'));
|
||
else
|
||
top += height;
|
||
|
||
if (this.o.rtl) {
|
||
var right = windowWidth - (left + width);
|
||
this.picker.css({
|
||
top: top,
|
||
right: right,
|
||
zIndex: zIndex
|
||
});
|
||
} else {
|
||
this.picker.css({
|
||
top: top,
|
||
left: left,
|
||
zIndex: zIndex
|
||
});
|
||
}
|
||
return this;
|
||
},
|
||
|
||
_allow_update: true,
|
||
update: function(){
|
||
if (!this._allow_update)
|
||
return this;
|
||
|
||
var oldDates = this.dates.copy(),
|
||
dates = [],
|
||
fromArgs = false;
|
||
if (arguments.length){
|
||
$.each(arguments, $.proxy(function(i, date){
|
||
if (date instanceof Date)
|
||
date = this._local_to_utc(date);
|
||
dates.push(date);
|
||
}, this));
|
||
fromArgs = true;
|
||
} else {
|
||
dates = this.isInput
|
||
? this.element.val()
|
||
: this.element.data('date') || this.inputField.val();
|
||
if (dates && this.o.multidate)
|
||
dates = dates.split(this.o.multidateSeparator);
|
||
else
|
||
dates = [dates];
|
||
delete this.element.data().date;
|
||
}
|
||
|
||
dates = $.map(dates, $.proxy(function(date){
|
||
return DPGlobal.parseDate(date, this.o.format, this.o.language, this.o.assumeNearbyYear);
|
||
}, this));
|
||
dates = $.grep(dates, $.proxy(function(date){
|
||
return (
|
||
!this.dateWithinRange(date) ||
|
||
!date
|
||
);
|
||
}, this), true);
|
||
this.dates.replace(dates);
|
||
|
||
if (this.o.updateViewDate) {
|
||
if (this.dates.length)
|
||
this.viewDate = new Date(this.dates.get(-1));
|
||
else if (this.viewDate < this.o.startDate)
|
||
this.viewDate = new Date(this.o.startDate);
|
||
else if (this.viewDate > this.o.endDate)
|
||
this.viewDate = new Date(this.o.endDate);
|
||
else
|
||
this.viewDate = this.o.defaultViewDate;
|
||
}
|
||
|
||
if (fromArgs){
|
||
// setting date by clicking
|
||
this.setValue();
|
||
this.element.change();
|
||
}
|
||
else if (this.dates.length){
|
||
// setting date by typing
|
||
if (String(oldDates) !== String(this.dates) && fromArgs) {
|
||
this._trigger('changeDate');
|
||
this.element.change();
|
||
}
|
||
}
|
||
if (!this.dates.length && oldDates.length) {
|
||
this._trigger('clearDate');
|
||
this.element.change();
|
||
}
|
||
|
||
this.fill();
|
||
return this;
|
||
},
|
||
|
||
fillDow: function(){
|
||
if (this.o.showWeekDays) {
|
||
var dowCnt = this.o.weekStart,
|
||
html = '<tr>';
|
||
if (this.o.calendarWeeks){
|
||
html += '<th class="cw"> </th>';
|
||
}
|
||
while (dowCnt < this.o.weekStart + 7){
|
||
html += '<th class="dow';
|
||
if ($.inArray(dowCnt, this.o.daysOfWeekDisabled) !== -1)
|
||
html += ' disabled';
|
||
html += '">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
|
||
}
|
||
html += '</tr>';
|
||
this.picker.find('.datepicker-days thead').append(html);
|
||
}
|
||
},
|
||
|
||
fillMonths: function(){
|
||
var localDate = this._utc_to_local(this.viewDate);
|
||
var html = '';
|
||
var focused;
|
||
for (var i = 0; i < 12; i++){
|
||
focused = localDate && localDate.getMonth() === i ? ' focused' : '';
|
||
html += '<span class="month' + focused + '">' + dates[this.o.language].monthsShort[i] + '</span>';
|
||
}
|
||
this.picker.find('.datepicker-months td').html(html);
|
||
},
|
||
|
||
setRange: function(range){
|
||
if (!range || !range.length)
|
||
delete this.range;
|
||
else
|
||
this.range = $.map(range, function(d){
|
||
return d.valueOf();
|
||
});
|
||
this.fill();
|
||
},
|
||
|
||
getClassNames: function(date){
|
||
var cls = [],
|
||
year = this.viewDate.getUTCFullYear(),
|
||
month = this.viewDate.getUTCMonth(),
|
||
today = UTCToday();
|
||
if (date.getUTCFullYear() < year || (date.getUTCFullYear() === year && date.getUTCMonth() < month)){
|
||
cls.push('old');
|
||
} else if (date.getUTCFullYear() > year || (date.getUTCFullYear() === year && date.getUTCMonth() > month)){
|
||
cls.push('new');
|
||
}
|
||
if (this.focusDate && date.valueOf() === this.focusDate.valueOf())
|
||
cls.push('focused');
|
||
// Compare internal UTC date with UTC today, not local today
|
||
if (this.o.todayHighlight && isUTCEquals(date, today)) {
|
||
cls.push('today');
|
||
}
|
||
if (this.dates.contains(date) !== -1)
|
||
cls.push('active');
|
||
if (!this.dateWithinRange(date)){
|
||
cls.push('disabled');
|
||
}
|
||
if (this.dateIsDisabled(date)){
|
||
cls.push('disabled', 'disabled-date');
|
||
}
|
||
if ($.inArray(date.getUTCDay(), this.o.daysOfWeekHighlighted) !== -1){
|
||
cls.push('highlighted');
|
||
}
|
||
|
||
if (this.range){
|
||
if (date > this.range[0] && date < this.range[this.range.length-1]){
|
||
cls.push('range');
|
||
}
|
||
if ($.inArray(date.valueOf(), this.range) !== -1){
|
||
cls.push('selected');
|
||
}
|
||
if (date.valueOf() === this.range[0]){
|
||
cls.push('range-start');
|
||
}
|
||
if (date.valueOf() === this.range[this.range.length-1]){
|
||
cls.push('range-end');
|
||
}
|
||
}
|
||
return cls;
|
||
},
|
||
|
||
_fill_yearsView: function(selector, cssClass, factor, year, startYear, endYear, beforeFn){
|
||
var html = '';
|
||
var step = factor / 10;
|
||
var view = this.picker.find(selector);
|
||
var startVal = Math.floor(year / factor) * factor;
|
||
var endVal = startVal + step * 9;
|
||
var focusedVal = Math.floor(this.viewDate.getFullYear() / step) * step;
|
||
var selected = $.map(this.dates, function(d){
|
||
return Math.floor(d.getUTCFullYear() / step) * step;
|
||
});
|
||
|
||
var classes, tooltip, before;
|
||
for (var currVal = startVal - step; currVal <= endVal + step; currVal += step) {
|
||
classes = [cssClass];
|
||
tooltip = null;
|
||
|
||
if (currVal === startVal - step) {
|
||
classes.push('old');
|
||
} else if (currVal === endVal + step) {
|
||
classes.push('new');
|
||
}
|
||
if ($.inArray(currVal, selected) !== -1) {
|
||
classes.push('active');
|
||
}
|
||
if (currVal < startYear || currVal > endYear) {
|
||
classes.push('disabled');
|
||
}
|
||
if (currVal === focusedVal) {
|
||
classes.push('focused');
|
||
}
|
||
|
||
if (beforeFn !== $.noop) {
|
||
before = beforeFn(new Date(currVal, 0, 1));
|
||
if (before === undefined) {
|
||
before = {};
|
||
} else if (typeof before === 'boolean') {
|
||
before = {enabled: before};
|
||
} else if (typeof before === 'string') {
|
||
before = {classes: before};
|
||
}
|
||
if (before.enabled === false) {
|
||
classes.push('disabled');
|
||
}
|
||
if (before.classes) {
|
||
classes = classes.concat(before.classes.split(/\s+/));
|
||
}
|
||
if (before.tooltip) {
|
||
tooltip = before.tooltip;
|
||
}
|
||
}
|
||
|
||
html += '<span class="' + classes.join(' ') + '"' + (tooltip ? ' title="' + tooltip + '"' : '') + '>' + currVal + '</span>';
|
||
}
|
||
|
||
view.find('.datepicker-switch').text(startVal + '-' + endVal);
|
||
view.find('td').html(html);
|
||
},
|
||
|
||
fill: function(){
|
||
var d = new Date(this.viewDate),
|
||
year = d.getUTCFullYear(),
|
||
month = d.getUTCMonth(),
|
||
startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
|
||
startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
|
||
endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
|
||
endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
|
||
todaytxt = dates[this.o.language].today || dates['en'].today || '',
|
||
cleartxt = dates[this.o.language].clear || dates['en'].clear || '',
|
||
titleFormat = dates[this.o.language].titleFormat || dates['en'].titleFormat,
|
||
todayDate = UTCToday(),
|
||
titleBtnVisible = (this.o.todayBtn === true || this.o.todayBtn === 'linked') && todayDate >= this.o.startDate && todayDate <= this.o.endDate && !this.weekOfDateIsDisabled(todayDate),
|
||
tooltip,
|
||
before;
|
||
if (isNaN(year) || isNaN(month))
|
||
return;
|
||
this.picker.find('.datepicker-days .datepicker-switch')
|
||
.text(DPGlobal.formatDate(d, titleFormat, this.o.language));
|
||
this.picker.find('tfoot .today')
|
||
.text(todaytxt)
|
||
.css('display', titleBtnVisible ? 'table-cell' : 'none');
|
||
this.picker.find('tfoot .clear')
|
||
.text(cleartxt)
|
||
.css('display', this.o.clearBtn === true ? 'table-cell' : 'none');
|
||
this.picker.find('thead .datepicker-title')
|
||
.text(this.o.title)
|
||
.css('display', typeof this.o.title === 'string' && this.o.title !== '' ? 'table-cell' : 'none');
|
||
this.updateNavArrows();
|
||
this.fillMonths();
|
||
var prevMonth = UTCDate(year, month, 0),
|
||
day = prevMonth.getUTCDate();
|
||
prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
|
||
var nextMonth = new Date(prevMonth);
|
||
if (prevMonth.getUTCFullYear() < 100){
|
||
nextMonth.setUTCFullYear(prevMonth.getUTCFullYear());
|
||
}
|
||
nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
|
||
nextMonth = nextMonth.valueOf();
|
||
var html = [];
|
||
var weekDay, clsName;
|
||
while (prevMonth.valueOf() < nextMonth){
|
||
weekDay = prevMonth.getUTCDay();
|
||
if (weekDay === this.o.weekStart){
|
||
html.push('<tr>');
|
||
if (this.o.calendarWeeks){
|
||
// ISO 8601: First week contains first thursday.
|
||
// ISO also states week starts on Monday, but we can be more abstract here.
|
||
var
|
||
// Start of current week: based on weekstart/current date
|
||
ws = new Date(+prevMonth + (this.o.weekStart - weekDay - 7) % 7 * 864e5),
|
||
// Thursday of this week
|
||
th = new Date(Number(ws) + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
|
||
// First Thursday of year, year from thursday
|
||
yth = new Date(Number(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay()) % 7 * 864e5),
|
||
// Calendar week: ms between thursdays, div ms per day, div 7 days
|
||
calWeek = (th - yth) / 864e5 / 7 + 1;
|
||
html.push('<td class="cw">'+ calWeek +'</td>');
|
||
}
|
||
}
|
||
clsName = this.getClassNames(prevMonth);
|
||
clsName.push('day');
|
||
|
||
var content = prevMonth.getUTCDate();
|
||
|
||
if (this.o.beforeShowDay !== $.noop){
|
||
before = this.o.beforeShowDay(this._utc_to_local(prevMonth));
|
||
if (before === undefined)
|
||
before = {};
|
||
else if (typeof before === 'boolean')
|
||
before = {enabled: before};
|
||
else if (typeof before === 'string')
|
||
before = {classes: before};
|
||
if (before.enabled === false)
|
||
clsName.push('disabled');
|
||
if (before.classes)
|
||
clsName = clsName.concat(before.classes.split(/\s+/));
|
||
if (before.tooltip)
|
||
tooltip = before.tooltip;
|
||
if (before.content)
|
||
content = before.content;
|
||
}
|
||
|
||
//Check if uniqueSort exists (supported by jquery >=1.12 and >=2.2)
|
||
//Fallback to unique function for older jquery versions
|
||
if (typeof $.uniqueSort === "function") {
|
||
clsName = $.uniqueSort(clsName);
|
||
} else {
|
||
clsName = $.unique(clsName);
|
||
}
|
||
|
||
html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + ' data-date="' + prevMonth.getTime().toString() + '">' + content + '</td>');
|
||
tooltip = null;
|
||
if (weekDay === this.o.weekEnd){
|
||
html.push('</tr>');
|
||
}
|
||
prevMonth.setUTCDate(prevMonth.getUTCDate() + 1);
|
||
}
|
||
this.picker.find('.datepicker-days tbody').html(html.join(''));
|
||
|
||
var monthsTitle = dates[this.o.language].monthsTitle || dates['en'].monthsTitle || 'Months';
|
||
var months = this.picker.find('.datepicker-months')
|
||
.find('.datepicker-switch')
|
||
.text(this.o.maxViewMode < 2 ? monthsTitle : year)
|
||
.end()
|
||
.find('tbody span').removeClass('active');
|
||
|
||
$.each(this.dates, function(i, d){
|
||
if (d.getUTCFullYear() === year)
|
||
months.eq(d.getUTCMonth()).addClass('active');
|
||
});
|
||
|
||
if (year < startYear || year > endYear){
|
||
months.addClass('disabled');
|
||
}
|
||
if (year === startYear){
|
||
months.slice(0, startMonth).addClass('disabled');
|
||
}
|
||
if (year === endYear){
|
||
months.slice(endMonth+1).addClass('disabled');
|
||
}
|
||
|
||
if (this.o.beforeShowMonth !== $.noop){
|
||
var that = this;
|
||
$.each(months, function(i, month){
|
||
var moDate = new Date(year, i, 1);
|
||
var before = that.o.beforeShowMonth(moDate);
|
||
if (before === undefined)
|
||
before = {};
|
||
else if (typeof before === 'boolean')
|
||
before = {enabled: before};
|
||
else if (typeof before === 'string')
|
||
before = {classes: before};
|
||
if (before.enabled === false && !$(month).hasClass('disabled'))
|
||
$(month).addClass('disabled');
|
||
if (before.classes)
|
||
$(month).addClass(before.classes);
|
||
if (before.tooltip)
|
||
$(month).prop('title', before.tooltip);
|
||
});
|
||
}
|
||
|
||
// Generating decade/years picker
|
||
this._fill_yearsView(
|
||
'.datepicker-years',
|
||
'year',
|
||
10,
|
||
year,
|
||
startYear,
|
||
endYear,
|
||
this.o.beforeShowYear
|
||
);
|
||
|
||
// Generating century/decades picker
|
||
this._fill_yearsView(
|
||
'.datepicker-decades',
|
||
'decade',
|
||
100,
|
||
year,
|
||
startYear,
|
||
endYear,
|
||
this.o.beforeShowDecade
|
||
);
|
||
|
||
// Generating millennium/centuries picker
|
||
this._fill_yearsView(
|
||
'.datepicker-centuries',
|
||
'century',
|
||
1000,
|
||
year,
|
||
startYear,
|
||
endYear,
|
||
this.o.beforeShowCentury
|
||
);
|
||
},
|
||
|
||
updateNavArrows: function(){
|
||
if (!this._allow_update)
|
||
return;
|
||
|
||
var d = new Date(this.viewDate),
|
||
year = d.getUTCFullYear(),
|
||
month = d.getUTCMonth(),
|
||
startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
|
||
startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
|
||
endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
|
||
endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
|
||
prevIsDisabled,
|
||
nextIsDisabled,
|
||
factor = 1;
|
||
switch (this.viewMode){
|
||
case 4:
|
||
factor *= 10;
|
||
/* falls through */
|
||
case 3:
|
||
factor *= 10;
|
||
/* falls through */
|
||
case 2:
|
||
factor *= 10;
|
||
/* falls through */
|
||
case 1:
|
||
prevIsDisabled = Math.floor(year / factor) * factor <= startYear;
|
||
nextIsDisabled = Math.floor(year / factor) * factor + factor > endYear;
|
||
break;
|
||
case 0:
|
||
prevIsDisabled = year <= startYear && month <= startMonth;
|
||
nextIsDisabled = year >= endYear && month >= endMonth;
|
||
break;
|
||
}
|
||
|
||
this.picker.find('.prev').toggleClass('disabled', prevIsDisabled);
|
||
this.picker.find('.next').toggleClass('disabled', nextIsDisabled);
|
||
},
|
||
|
||
click: function(e){
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
|
||
var target, dir, day, year, month;
|
||
target = $(e.target);
|
||
|
||
// Clicked on the switch
|
||
if (target.hasClass('datepicker-switch') && this.viewMode !== this.o.maxViewMode){
|
||
this.setViewMode(this.viewMode + 1);
|
||
}
|
||
|
||
// Clicked on today button
|
||
if (target.hasClass('today') && !target.hasClass('day')){
|
||
this.setViewMode(0);
|
||
this._setDate(UTCToday(), this.o.todayBtn === 'linked' ? null : 'view');
|
||
}
|
||
|
||
// Clicked on clear button
|
||
if (target.hasClass('clear')){
|
||
this.clearDates();
|
||
}
|
||
|
||
if (!target.hasClass('disabled')){
|
||
// Clicked on a month, year, decade, century
|
||
if (target.hasClass('month')
|
||
|| target.hasClass('year')
|
||
|| target.hasClass('decade')
|
||
|| target.hasClass('century')) {
|
||
this.viewDate.setUTCDate(1);
|
||
|
||
day = 1;
|
||
if (this.viewMode === 1){
|
||
month = target.parent().find('span').index(target);
|
||
year = this.viewDate.getUTCFullYear();
|
||
this.viewDate.setUTCMonth(month);
|
||
} else {
|
||
month = 0;
|
||
year = Number(target.text());
|
||
this.viewDate.setUTCFullYear(year);
|
||
}
|
||
|
||
this._trigger(DPGlobal.viewModes[this.viewMode - 1].e, this.viewDate);
|
||
|
||
if (this.viewMode === this.o.minViewMode){
|
||
this._setDate(UTCDate(year, month, day));
|
||
} else {
|
||
this.setViewMode(this.viewMode - 1);
|
||
this.fill();
|
||
}
|
||
}
|
||
}
|
||
|
||
if (this.picker.is(':visible') && this._focused_from){
|
||
this._focused_from.focus();
|
||
}
|
||
delete this._focused_from;
|
||
},
|
||
|
||
dayCellClick: function(e){
|
||
var $target = $(e.currentTarget);
|
||
var timestamp = $target.data('date');
|
||
var date = new Date(timestamp);
|
||
|
||
if (this.o.updateViewDate) {
|
||
if (date.getUTCFullYear() !== this.viewDate.getUTCFullYear()) {
|
||
this._trigger('changeYear', this.viewDate);
|
||
}
|
||
|
||
if (date.getUTCMonth() !== this.viewDate.getUTCMonth()) {
|
||
this._trigger('changeMonth', this.viewDate);
|
||
}
|
||
}
|
||
this._setDate(date);
|
||
},
|
||
|
||
// Clicked on prev or next
|
||
navArrowsClick: function(e){
|
||
var $target = $(e.currentTarget);
|
||
var dir = $target.hasClass('prev') ? -1 : 1;
|
||
if (this.viewMode !== 0){
|
||
dir *= DPGlobal.viewModes[this.viewMode].navStep * 12;
|
||
}
|
||
this.viewDate = this.moveMonth(this.viewDate, dir);
|
||
this._trigger(DPGlobal.viewModes[this.viewMode].e, this.viewDate);
|
||
this.fill();
|
||
},
|
||
|
||
_toggle_multidate: function(date){
|
||
var ix = this.dates.contains(date);
|
||
if (!date){
|
||
this.dates.clear();
|
||
}
|
||
|
||
if (ix !== -1){
|
||
if (this.o.multidate === true || this.o.multidate > 1 || this.o.toggleActive){
|
||
this.dates.remove(ix);
|
||
}
|
||
} else if (this.o.multidate === false) {
|
||
this.dates.clear();
|
||
this.dates.push(date);
|
||
}
|
||
else {
|
||
this.dates.push(date);
|
||
}
|
||
|
||
if (typeof this.o.multidate === 'number')
|
||
while (this.dates.length > this.o.multidate)
|
||
this.dates.remove(0);
|
||
},
|
||
|
||
_setDate: function(date, which){
|
||
if (!which || which === 'date')
|
||
this._toggle_multidate(date && new Date(date));
|
||
if ((!which && this.o.updateViewDate) || which === 'view')
|
||
this.viewDate = date && new Date(date);
|
||
|
||
this.fill();
|
||
this.setValue();
|
||
if (!which || which !== 'view') {
|
||
this._trigger('changeDate');
|
||
}
|
||
this.inputField.trigger('change');
|
||
if (this.o.autoclose && (!which || which === 'date')){
|
||
this.hide();
|
||
}
|
||
},
|
||
|
||
moveDay: function(date, dir){
|
||
var newDate = new Date(date);
|
||
newDate.setUTCDate(date.getUTCDate() + dir);
|
||
|
||
return newDate;
|
||
},
|
||
|
||
moveWeek: function(date, dir){
|
||
return this.moveDay(date, dir * 7);
|
||
},
|
||
|
||
moveMonth: function(date, dir){
|
||
if (!isValidDate(date))
|
||
return this.o.defaultViewDate;
|
||
if (!dir)
|
||
return date;
|
||
var new_date = new Date(date.valueOf()),
|
||
day = new_date.getUTCDate(),
|
||
month = new_date.getUTCMonth(),
|
||
mag = Math.abs(dir),
|
||
new_month, test;
|
||
dir = dir > 0 ? 1 : -1;
|
||
if (mag === 1){
|
||
test = dir === -1
|
||
// If going back one month, make sure month is not current month
|
||
// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
|
||
? function(){
|
||
return new_date.getUTCMonth() === month;
|
||
}
|
||
// If going forward one month, make sure month is as expected
|
||
// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
|
||
: function(){
|
||
return new_date.getUTCMonth() !== new_month;
|
||
};
|
||
new_month = month + dir;
|
||
new_date.setUTCMonth(new_month);
|
||
// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
|
||
new_month = (new_month + 12) % 12;
|
||
}
|
||
else {
|
||
// For magnitudes >1, move one month at a time...
|
||
for (var i=0; i < mag; i++)
|
||
// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
|
||
new_date = this.moveMonth(new_date, dir);
|
||
// ...then reset the day, keeping it in the new month
|
||
new_month = new_date.getUTCMonth();
|
||
new_date.setUTCDate(day);
|
||
test = function(){
|
||
return new_month !== new_date.getUTCMonth();
|
||
};
|
||
}
|
||
// Common date-resetting loop -- if date is beyond end of month, make it
|
||
// end of month
|
||
while (test()){
|
||
new_date.setUTCDate(--day);
|
||
new_date.setUTCMonth(new_month);
|
||
}
|
||
return new_date;
|
||
},
|
||
|
||
moveYear: function(date, dir){
|
||
return this.moveMonth(date, dir*12);
|
||
},
|
||
|
||
moveAvailableDate: function(date, dir, fn){
|
||
do {
|
||
date = this[fn](date, dir);
|
||
|
||
if (!this.dateWithinRange(date))
|
||
return false;
|
||
|
||
fn = 'moveDay';
|
||
}
|
||
while (this.dateIsDisabled(date));
|
||
|
||
return date;
|
||
},
|
||
|
||
weekOfDateIsDisabled: function(date){
|
||
return $.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1;
|
||
},
|
||
|
||
dateIsDisabled: function(date){
|
||
return (
|
||
this.weekOfDateIsDisabled(date) ||
|
||
$.grep(this.o.datesDisabled, function(d){
|
||
return isUTCEquals(date, d);
|
||
}).length > 0
|
||
);
|
||
},
|
||
|
||
dateWithinRange: function(date){
|
||
return date >= this.o.startDate && date <= this.o.endDate;
|
||
},
|
||
|
||
keydown: function(e){
|
||
if (!this.picker.is(':visible')){
|
||
if (e.keyCode === 40 || e.keyCode === 27) { // allow down to re-show picker
|
||
this.show();
|
||
e.stopPropagation();
|
||
}
|
||
return;
|
||
}
|
||
var dateChanged = false,
|
||
dir, newViewDate,
|
||
focusDate = this.focusDate || this.viewDate;
|
||
switch (e.keyCode){
|
||
case 27: // escape
|
||
if (this.focusDate){
|
||
this.focusDate = null;
|
||
this.viewDate = this.dates.get(-1) || this.viewDate;
|
||
this.fill();
|
||
}
|
||
else
|
||
this.hide();
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
case 37: // left
|
||
case 38: // up
|
||
case 39: // right
|
||
case 40: // down
|
||
if (!this.o.keyboardNavigation || this.o.daysOfWeekDisabled.length === 7)
|
||
break;
|
||
dir = e.keyCode === 37 || e.keyCode === 38 ? -1 : 1;
|
||
if (this.viewMode === 0) {
|
||
if (e.ctrlKey){
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear');
|
||
|
||
if (newViewDate)
|
||
this._trigger('changeYear', this.viewDate);
|
||
} else if (e.shiftKey){
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth');
|
||
|
||
if (newViewDate)
|
||
this._trigger('changeMonth', this.viewDate);
|
||
} else if (e.keyCode === 37 || e.keyCode === 39){
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveDay');
|
||
} else if (!this.weekOfDateIsDisabled(focusDate)){
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveWeek');
|
||
}
|
||
} else if (this.viewMode === 1) {
|
||
if (e.keyCode === 38 || e.keyCode === 40) {
|
||
dir = dir * 4;
|
||
}
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveMonth');
|
||
} else if (this.viewMode === 2) {
|
||
if (e.keyCode === 38 || e.keyCode === 40) {
|
||
dir = dir * 4;
|
||
}
|
||
newViewDate = this.moveAvailableDate(focusDate, dir, 'moveYear');
|
||
}
|
||
if (newViewDate){
|
||
this.focusDate = this.viewDate = newViewDate;
|
||
this.setValue();
|
||
this.fill();
|
||
e.preventDefault();
|
||
}
|
||
break;
|
||
case 13: // enter
|
||
if (!this.o.forceParse)
|
||
break;
|
||
focusDate = this.focusDate || this.dates.get(-1) || this.viewDate;
|
||
if (this.o.keyboardNavigation) {
|
||
this._toggle_multidate(focusDate);
|
||
dateChanged = true;
|
||
}
|
||
this.focusDate = null;
|
||
this.viewDate = this.dates.get(-1) || this.viewDate;
|
||
this.setValue();
|
||
this.fill();
|
||
if (this.picker.is(':visible')){
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
if (this.o.autoclose)
|
||
this.hide();
|
||
}
|
||
break;
|
||
case 9: // tab
|
||
this.focusDate = null;
|
||
this.viewDate = this.dates.get(-1) || this.viewDate;
|
||
this.fill();
|
||
this.hide();
|
||
break;
|
||
}
|
||
if (dateChanged){
|
||
if (this.dates.length)
|
||
this._trigger('changeDate');
|
||
else
|
||
this._trigger('clearDate');
|
||
this.inputField.trigger('change');
|
||
}
|
||
},
|
||
|
||
setViewMode: function(viewMode){
|
||
this.viewMode = viewMode;
|
||
this.picker
|
||
.children('div')
|
||
.hide()
|
||
.filter('.datepicker-' + DPGlobal.viewModes[this.viewMode].clsName)
|
||
.show();
|
||
this.updateNavArrows();
|
||
this._trigger('changeViewMode', new Date(this.viewDate));
|
||
}
|
||
};
|
||
|
||
var DateRangePicker = function(element, options){
|
||
$.data(element, 'datepicker', this);
|
||
this.element = $(element);
|
||
this.inputs = $.map(options.inputs, function(i){
|
||
return i.jquery ? i[0] : i;
|
||
});
|
||
delete options.inputs;
|
||
|
||
this.keepEmptyValues = options.keepEmptyValues;
|
||
delete options.keepEmptyValues;
|
||
|
||
datepickerPlugin.call($(this.inputs), options)
|
||
.on('changeDate', $.proxy(this.dateUpdated, this));
|
||
|
||
this.pickers = $.map(this.inputs, function(i){
|
||
return $.data(i, 'datepicker');
|
||
});
|
||
this.updateDates();
|
||
};
|
||
DateRangePicker.prototype = {
|
||
updateDates: function(){
|
||
this.dates = $.map(this.pickers, function(i){
|
||
return i.getUTCDate();
|
||
});
|
||
this.updateRanges();
|
||
},
|
||
updateRanges: function(){
|
||
var range = $.map(this.dates, function(d){
|
||
return d.valueOf();
|
||
});
|
||
$.each(this.pickers, function(i, p){
|
||
p.setRange(range);
|
||
});
|
||
},
|
||
clearDates: function(){
|
||
$.each(this.pickers, function(i, p){
|
||
p.clearDates();
|
||
});
|
||
},
|
||
dateUpdated: function(e){
|
||
// `this.updating` is a workaround for preventing infinite recursion
|
||
// between `changeDate` triggering and `setUTCDate` calling. Until
|
||
// there is a better mechanism.
|
||
if (this.updating)
|
||
return;
|
||
this.updating = true;
|
||
|
||
var dp = $.data(e.target, 'datepicker');
|
||
|
||
if (dp === undefined) {
|
||
return;
|
||
}
|
||
|
||
var new_date = dp.getUTCDate(),
|
||
keep_empty_values = this.keepEmptyValues,
|
||
i = $.inArray(e.target, this.inputs),
|
||
j = i - 1,
|
||
k = i + 1,
|
||
l = this.inputs.length;
|
||
if (i === -1)
|
||
return;
|
||
|
||
$.each(this.pickers, function(i, p){
|
||
if (!p.getUTCDate() && (p === dp || !keep_empty_values))
|
||
p.setUTCDate(new_date);
|
||
});
|
||
|
||
if (new_date < this.dates[j]){
|
||
// Date being moved earlier/left
|
||
while (j >= 0 && new_date < this.dates[j] && (this.pickers[j].element.val() || "").length > 0) {
|
||
this.pickers[j--].setUTCDate(new_date);
|
||
}
|
||
} else if (new_date > this.dates[k]){
|
||
// Date being moved later/right
|
||
while (k < l && new_date > this.dates[k] && (this.pickers[k].element.val() || "").length > 0) {
|
||
this.pickers[k++].setUTCDate(new_date);
|
||
}
|
||
}
|
||
this.updateDates();
|
||
|
||
delete this.updating;
|
||
},
|
||
destroy: function(){
|
||
$.map(this.pickers, function(p){ p.destroy(); });
|
||
$(this.inputs).off('changeDate', this.dateUpdated);
|
||
delete this.element.data().datepicker;
|
||
},
|
||
remove: alias('destroy', 'Method `remove` is deprecated and will be removed in version 2.0. Use `destroy` instead')
|
||
};
|
||
|
||
function opts_from_el(el, prefix){
|
||
// Derive options from element data-attrs
|
||
var data = $(el).data(),
|
||
out = {}, inkey,
|
||
replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])');
|
||
prefix = new RegExp('^' + prefix.toLowerCase());
|
||
function re_lower(_,a){
|
||
return a.toLowerCase();
|
||
}
|
||
for (var key in data)
|
||
if (prefix.test(key)){
|
||
inkey = key.replace(replace, re_lower);
|
||
out[inkey] = data[key];
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function opts_from_locale(lang){
|
||
// Derive options from locale plugins
|
||
var out = {};
|
||
// Check if "de-DE" style date is available, if not language should
|
||
// fallback to 2 letter code eg "de"
|
||
if (!dates[lang]){
|
||
lang = lang.split('-')[0];
|
||
if (!dates[lang])
|
||
return;
|
||
}
|
||
var d = dates[lang];
|
||
$.each(locale_opts, function(i,k){
|
||
if (k in d)
|
||
out[k] = d[k];
|
||
});
|
||
return out;
|
||
}
|
||
|
||
var old = $.fn.datepicker;
|
||
var datepickerPlugin = function(option){
|
||
var args = Array.apply(null, arguments);
|
||
args.shift();
|
||
var internal_return;
|
||
this.each(function(){
|
||
var $this = $(this),
|
||
data = $this.data('datepicker'),
|
||
options = typeof option === 'object' && option;
|
||
if (!data){
|
||
var elopts = opts_from_el(this, 'date'),
|
||
// Preliminary otions
|
||
xopts = $.extend({}, defaults, elopts, options),
|
||
locopts = opts_from_locale(xopts.language),
|
||
// Options priority: js args, data-attrs, locales, defaults
|
||
opts = $.extend({}, defaults, locopts, elopts, options);
|
||
if ($this.hasClass('input-daterange') || opts.inputs){
|
||
$.extend(opts, {
|
||
inputs: opts.inputs || $this.find('input').toArray()
|
||
});
|
||
data = new DateRangePicker(this, opts);
|
||
}
|
||
else {
|
||
data = new Datepicker(this, opts);
|
||
}
|
||
$this.data('datepicker', data);
|
||
}
|
||
if (typeof option === 'string' && typeof data[option] === 'function'){
|
||
internal_return = data[option].apply(data, args);
|
||
}
|
||
});
|
||
|
||
if (
|
||
internal_return === undefined ||
|
||
internal_return instanceof Datepicker ||
|
||
internal_return instanceof DateRangePicker
|
||
)
|
||
return this;
|
||
|
||
if (this.length > 1)
|
||
throw new Error('Using only allowed for the collection of a single element (' + option + ' function)');
|
||
else
|
||
return internal_return;
|
||
};
|
||
$.fn.datepicker = datepickerPlugin;
|
||
|
||
var defaults = $.fn.datepicker.defaults = {
|
||
assumeNearbyYear: false,
|
||
autoclose: false,
|
||
beforeShowDay: $.noop,
|
||
beforeShowMonth: $.noop,
|
||
beforeShowYear: $.noop,
|
||
beforeShowDecade: $.noop,
|
||
beforeShowCentury: $.noop,
|
||
calendarWeeks: false,
|
||
clearBtn: false,
|
||
toggleActive: false,
|
||
daysOfWeekDisabled: [],
|
||
daysOfWeekHighlighted: [],
|
||
datesDisabled: [],
|
||
endDate: Infinity,
|
||
forceParse: true,
|
||
format: 'mm/dd/yyyy',
|
||
isInline: null,
|
||
keepEmptyValues: false,
|
||
keyboardNavigation: true,
|
||
language: 'en',
|
||
minViewMode: 0,
|
||
maxViewMode: 4,
|
||
multidate: false,
|
||
multidateSeparator: ',',
|
||
orientation: "auto",
|
||
rtl: false,
|
||
startDate: -Infinity,
|
||
startView: 0,
|
||
todayBtn: false,
|
||
todayHighlight: false,
|
||
updateViewDate: true,
|
||
weekStart: 0,
|
||
disableTouchKeyboard: false,
|
||
enableOnReadonly: true,
|
||
showOnFocus: true,
|
||
zIndexOffset: 10,
|
||
container: 'body',
|
||
immediateUpdates: false,
|
||
title: '',
|
||
templates: {
|
||
leftArrow: '«',
|
||
rightArrow: '»'
|
||
},
|
||
showWeekDays: true
|
||
};
|
||
var locale_opts = $.fn.datepicker.locale_opts = [
|
||
'format',
|
||
'rtl',
|
||
'weekStart'
|
||
];
|
||
$.fn.datepicker.Constructor = Datepicker;
|
||
var dates = $.fn.datepicker.dates = {
|
||
en: {
|
||
days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
|
||
daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
|
||
daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
|
||
months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
|
||
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
||
today: "Today",
|
||
clear: "Clear",
|
||
titleFormat: "MM yyyy"
|
||
}
|
||
};
|
||
|
||
var DPGlobal = {
|
||
viewModes: [
|
||
{
|
||
names: ['days', 'month'],
|
||
clsName: 'days',
|
||
e: 'changeMonth'
|
||
},
|
||
{
|
||
names: ['months', 'year'],
|
||
clsName: 'months',
|
||
e: 'changeYear',
|
||
navStep: 1
|
||
},
|
||
{
|
||
names: ['years', 'decade'],
|
||
clsName: 'years',
|
||
e: 'changeDecade',
|
||
navStep: 10
|
||
},
|
||
{
|
||
names: ['decades', 'century'],
|
||
clsName: 'decades',
|
||
e: 'changeCentury',
|
||
navStep: 100
|
||
},
|
||
{
|
||
names: ['centuries', 'millennium'],
|
||
clsName: 'centuries',
|
||
e: 'changeMillennium',
|
||
navStep: 1000
|
||
}
|
||
],
|
||
validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
|
||
nonpunctuation: /[^ -\/:-@\u5e74\u6708\u65e5\[-`{-~\t\n\r]+/g,
|
||
parseFormat: function(format){
|
||
if (typeof format.toValue === 'function' && typeof format.toDisplay === 'function')
|
||
return format;
|
||
// IE treats \0 as a string end in inputs (truncating the value),
|
||
// so it's a bad format delimiter, anyway
|
||
var separators = format.replace(this.validParts, '\0').split('\0'),
|
||
parts = format.match(this.validParts);
|
||
if (!separators || !separators.length || !parts || parts.length === 0){
|
||
throw new Error("Invalid date format.");
|
||
}
|
||
return {separators: separators, parts: parts};
|
||
},
|
||
parseDate: function(date, format, language, assumeNearby){
|
||
if (!date)
|
||
return undefined;
|
||
if (date instanceof Date)
|
||
return date;
|
||
if (typeof format === 'string')
|
||
format = DPGlobal.parseFormat(format);
|
||
if (format.toValue)
|
||
return format.toValue(date, format, language);
|
||
var fn_map = {
|
||
d: 'moveDay',
|
||
m: 'moveMonth',
|
||
w: 'moveWeek',
|
||
y: 'moveYear'
|
||
},
|
||
dateAliases = {
|
||
yesterday: '-1d',
|
||
today: '+0d',
|
||
tomorrow: '+1d'
|
||
},
|
||
parts, part, dir, i, fn;
|
||
if (date in dateAliases){
|
||
date = dateAliases[date];
|
||
}
|
||
if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/i.test(date)){
|
||
parts = date.match(/([\-+]\d+)([dmwy])/gi);
|
||
date = new Date();
|
||
for (i=0; i < parts.length; i++){
|
||
part = parts[i].match(/([\-+]\d+)([dmwy])/i);
|
||
dir = Number(part[1]);
|
||
fn = fn_map[part[2].toLowerCase()];
|
||
date = Datepicker.prototype[fn](date, dir);
|
||
}
|
||
return Datepicker.prototype._zero_utc_time(date);
|
||
}
|
||
|
||
parts = date && date.match(this.nonpunctuation) || [];
|
||
|
||
function applyNearbyYear(year, threshold){
|
||
if (threshold === true)
|
||
threshold = 10;
|
||
|
||
// if year is 2 digits or less, than the user most likely is trying to get a recent century
|
||
if (year < 100){
|
||
year += 2000;
|
||
// if the new year is more than threshold years in advance, use last century
|
||
if (year > ((new Date()).getFullYear()+threshold)){
|
||
year -= 100;
|
||
}
|
||
}
|
||
|
||
return year;
|
||
}
|
||
|
||
var parsed = {},
|
||
setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
|
||
setters_map = {
|
||
yyyy: function(d,v){
|
||
return d.setUTCFullYear(assumeNearby ? applyNearbyYear(v, assumeNearby) : v);
|
||
},
|
||
m: function(d,v){
|
||
if (isNaN(d))
|
||
return d;
|
||
v -= 1;
|
||
while (v < 0) v += 12;
|
||
v %= 12;
|
||
d.setUTCMonth(v);
|
||
while (d.getUTCMonth() !== v)
|
||
d.setUTCDate(d.getUTCDate()-1);
|
||
return d;
|
||
},
|
||
d: function(d,v){
|
||
return d.setUTCDate(v);
|
||
}
|
||
},
|
||
val, filtered;
|
||
setters_map['yy'] = setters_map['yyyy'];
|
||
setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
|
||
setters_map['dd'] = setters_map['d'];
|
||
date = UTCToday();
|
||
var fparts = format.parts.slice();
|
||
// Remove noop parts
|
||
if (parts.length !== fparts.length){
|
||
fparts = $(fparts).filter(function(i,p){
|
||
return $.inArray(p, setters_order) !== -1;
|
||
}).toArray();
|
||
}
|
||
// Process remainder
|
||
function match_part(){
|
||
var m = this.slice(0, parts[i].length),
|
||
p = parts[i].slice(0, m.length);
|
||
return m.toLowerCase() === p.toLowerCase();
|
||
}
|
||
if (parts.length === fparts.length){
|
||
var cnt;
|
||
for (i=0, cnt = fparts.length; i < cnt; i++){
|
||
val = parseInt(parts[i], 10);
|
||
part = fparts[i];
|
||
if (isNaN(val)){
|
||
switch (part){
|
||
case 'MM':
|
||
filtered = $(dates[language].months).filter(match_part);
|
||
val = $.inArray(filtered[0], dates[language].months) + 1;
|
||
break;
|
||
case 'M':
|
||
filtered = $(dates[language].monthsShort).filter(match_part);
|
||
val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
|
||
break;
|
||
}
|
||
}
|
||
parsed[part] = val;
|
||
}
|
||
var _date, s;
|
||
for (i=0; i < setters_order.length; i++){
|
||
s = setters_order[i];
|
||
if (s in parsed && !isNaN(parsed[s])){
|
||
_date = new Date(date);
|
||
setters_map[s](_date, parsed[s]);
|
||
if (!isNaN(_date))
|
||
date = _date;
|
||
}
|
||
}
|
||
}
|
||
return date;
|
||
},
|
||
formatDate: function(date, format, language){
|
||
if (!date)
|
||
return '';
|
||
if (typeof format === 'string')
|
||
format = DPGlobal.parseFormat(format);
|
||
if (format.toDisplay)
|
||
return format.toDisplay(date, format, language);
|
||
var val = {
|
||
d: date.getUTCDate(),
|
||
D: dates[language].daysShort[date.getUTCDay()],
|
||
DD: dates[language].days[date.getUTCDay()],
|
||
m: date.getUTCMonth() + 1,
|
||
M: dates[language].monthsShort[date.getUTCMonth()],
|
||
MM: dates[language].months[date.getUTCMonth()],
|
||
yy: date.getUTCFullYear().toString().substring(2),
|
||
yyyy: date.getUTCFullYear()
|
||
};
|
||
val.dd = (val.d < 10 ? '0' : '') + val.d;
|
||
val.mm = (val.m < 10 ? '0' : '') + val.m;
|
||
date = [];
|
||
var seps = $.extend([], format.separators);
|
||
for (var i=0, cnt = format.parts.length; i <= cnt; i++){
|
||
if (seps.length)
|
||
date.push(seps.shift());
|
||
date.push(val[format.parts[i]]);
|
||
}
|
||
return date.join('');
|
||
},
|
||
headTemplate: '<thead>'+
|
||
'<tr>'+
|
||
'<th colspan="7" class="datepicker-title"></th>'+
|
||
'</tr>'+
|
||
'<tr>'+
|
||
'<th class="prev">'+defaults.templates.leftArrow+'</th>'+
|
||
'<th colspan="5" class="datepicker-switch"></th>'+
|
||
'<th class="next">'+defaults.templates.rightArrow+'</th>'+
|
||
'</tr>'+
|
||
'</thead>',
|
||
contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
|
||
footTemplate: '<tfoot>'+
|
||
'<tr>'+
|
||
'<th colspan="7" class="today"></th>'+
|
||
'</tr>'+
|
||
'<tr>'+
|
||
'<th colspan="7" class="clear"></th>'+
|
||
'</tr>'+
|
||
'</tfoot>'
|
||
};
|
||
DPGlobal.template = '<div class="datepicker">'+
|
||
'<div class="datepicker-days">'+
|
||
'<table class="table-condensed">'+
|
||
DPGlobal.headTemplate+
|
||
'<tbody></tbody>'+
|
||
DPGlobal.footTemplate+
|
||
'</table>'+
|
||
'</div>'+
|
||
'<div class="datepicker-months">'+
|
||
'<table class="table-condensed">'+
|
||
DPGlobal.headTemplate+
|
||
DPGlobal.contTemplate+
|
||
DPGlobal.footTemplate+
|
||
'</table>'+
|
||
'</div>'+
|
||
'<div class="datepicker-years">'+
|
||
'<table class="table-condensed">'+
|
||
DPGlobal.headTemplate+
|
||
DPGlobal.contTemplate+
|
||
DPGlobal.footTemplate+
|
||
'</table>'+
|
||
'</div>'+
|
||
'<div class="datepicker-decades">'+
|
||
'<table class="table-condensed">'+
|
||
DPGlobal.headTemplate+
|
||
DPGlobal.contTemplate+
|
||
DPGlobal.footTemplate+
|
||
'</table>'+
|
||
'</div>'+
|
||
'<div class="datepicker-centuries">'+
|
||
'<table class="table-condensed">'+
|
||
DPGlobal.headTemplate+
|
||
DPGlobal.contTemplate+
|
||
DPGlobal.footTemplate+
|
||
'</table>'+
|
||
'</div>'+
|
||
'</div>';
|
||
|
||
$.fn.datepicker.DPGlobal = DPGlobal;
|
||
|
||
|
||
/* DATEPICKER NO CONFLICT
|
||
* =================== */
|
||
|
||
$.fn.datepicker.noConflict = function(){
|
||
$.fn.datepicker = old;
|
||
return this;
|
||
};
|
||
|
||
/* DATEPICKER VERSION
|
||
* =================== */
|
||
$.fn.datepicker.version = '1.10.0';
|
||
|
||
$.fn.datepicker.deprecated = function(msg){
|
||
var console = window.console;
|
||
if (console && console.warn) {
|
||
console.warn('DEPRECATED: ' + msg);
|
||
}
|
||
};
|
||
|
||
|
||
/* DATEPICKER DATA-API
|
||
* ================== */
|
||
|
||
$(document).on(
|
||
'focus.datepicker.data-api click.datepicker.data-api',
|
||
'[data-provide="datepicker"]',
|
||
function(e){
|
||
var $this = $(this);
|
||
if ($this.data('datepicker'))
|
||
return;
|
||
e.preventDefault();
|
||
// component click requires us to explicitly show it
|
||
datepickerPlugin.call($this, 'show');
|
||
}
|
||
);
|
||
$(function(){
|
||
datepickerPlugin.call($('[data-provide="datepicker-inline"]'));
|
||
});
|
||
|
||
}));
|
||
|
||
/*!
|
||
* Lightbox for Bootstrap by @ashleydw
|
||
* https://github.com/ashleydw/lightbox
|
||
*
|
||
* License: https://github.com/ashleydw/lightbox/blob/master/LICENSE
|
||
*/
|
||
+function ($) {
|
||
|
||
'use strict';
|
||
|
||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
||
|
||
var Lightbox = (function ($) {
|
||
|
||
var NAME = 'ekkoLightbox';
|
||
var JQUERY_NO_CONFLICT = $.fn[NAME];
|
||
|
||
var Default = {
|
||
title: '',
|
||
footer: '',
|
||
maxWidth: 9999,
|
||
maxHeight: 9999,
|
||
showArrows: true, //display the left / right arrows or not
|
||
wrapping: true, //if true, gallery loops infinitely
|
||
type: null, //force the lightbox into image / youtube mode. if null, or not image|youtube|vimeo; detect it
|
||
alwaysShowClose: false, //always show the close button, even if there is no title
|
||
loadingMessage: '<div class="ekko-lightbox-loader"><div><div></div><div></div></div></div>', // http://tobiasahlin.com/spinkit/
|
||
leftArrow: '<span>❮</span>',
|
||
rightArrow: '<span>❯</span>',
|
||
strings: {
|
||
close: 'Close',
|
||
fail: 'Failed to load image:',
|
||
type: 'Could not detect remote target type. Force the type using data-type'
|
||
},
|
||
doc: document, // if in an iframe can specify top.document
|
||
onShow: function onShow() {},
|
||
onShown: function onShown() {},
|
||
onHide: function onHide() {},
|
||
onHidden: function onHidden() {},
|
||
onNavigate: function onNavigate() {},
|
||
onContentLoaded: function onContentLoaded() {}
|
||
};
|
||
|
||
var Lightbox = (function () {
|
||
_createClass(Lightbox, null, [{
|
||
key: 'Default',
|
||
|
||
/**
|
||
Class properties:
|
||
_$element: null -> the <a> element currently being displayed
|
||
_$modal: The bootstrap modal generated
|
||
_$modalDialog: The .modal-dialog
|
||
_$modalContent: The .modal-content
|
||
_$modalBody: The .modal-body
|
||
_$modalHeader: The .modal-header
|
||
_$modalFooter: The .modal-footer
|
||
_$lightboxContainerOne: Container of the first lightbox element
|
||
_$lightboxContainerTwo: Container of the second lightbox element
|
||
_$lightboxBody: First element in the container
|
||
_$modalArrows: The overlayed arrows container
|
||
_$galleryItems: Other <a>'s available for this gallery
|
||
_galleryName: Name of the current data('gallery') showing
|
||
_galleryIndex: The current index of the _$galleryItems being shown
|
||
_config: {} the options for the modal
|
||
_modalId: unique id for the current lightbox
|
||
_padding / _border: CSS properties for the modal container; these are used to calculate the available space for the content
|
||
*/
|
||
|
||
get: function get() {
|
||
return Default;
|
||
}
|
||
}]);
|
||
|
||
function Lightbox($element, config) {
|
||
var _this = this;
|
||
|
||
_classCallCheck(this, Lightbox);
|
||
|
||
this._config = $.extend({}, Default, config);
|
||
this._$modalArrows = null;
|
||
this._galleryIndex = 0;
|
||
this._galleryName = null;
|
||
this._padding = null;
|
||
this._border = null;
|
||
this._titleIsShown = false;
|
||
this._footerIsShown = false;
|
||
this._wantedWidth = 0;
|
||
this._wantedHeight = 0;
|
||
this._touchstartX = 0;
|
||
this._touchendX = 0;
|
||
|
||
this._modalId = 'ekkoLightbox-' + Math.floor(Math.random() * 1000 + 1);
|
||
this._$element = $element instanceof jQuery ? $element : $($element);
|
||
|
||
this._isBootstrap3 = $.fn.modal.Constructor.VERSION[0] == 3;
|
||
|
||
var h4 = '<h4 class="modal-title">' + (this._config.title || " ") + '</h4>';
|
||
var btn = '<button type="button" class="close" data-dismiss="modal" aria-label="' + this._config.strings.close + '"><span aria-hidden="true">×</span></button>';
|
||
|
||
var header = '<div class="modal-header' + (this._config.title || this._config.alwaysShowClose ? '' : ' hide') + '">' + (this._isBootstrap3 ? btn + h4 : h4 + btn) + '</div>';
|
||
var footer = '<div class="modal-footer' + (this._config.footer ? '' : ' hide') + '">' + (this._config.footer || " ") + '</div>';
|
||
var body = '<div class="modal-body"><div class="ekko-lightbox-container"><div class="ekko-lightbox-item fade in show"></div><div class="ekko-lightbox-item fade"></div></div></div>';
|
||
var dialog = '<div class="modal-dialog" role="document"><div class="modal-content">' + header + body + footer + '</div></div>';
|
||
$(this._config.doc.body).append('<div id="' + this._modalId + '" class="ekko-lightbox modal fade" tabindex="-1" tabindex="-1" role="dialog" aria-hidden="true">' + dialog + '</div>');
|
||
|
||
this._$modal = $('#' + this._modalId, this._config.doc);
|
||
this._$modalDialog = this._$modal.find('.modal-dialog').first();
|
||
this._$modalContent = this._$modal.find('.modal-content').first();
|
||
this._$modalBody = this._$modal.find('.modal-body').first();
|
||
this._$modalHeader = this._$modal.find('.modal-header').first();
|
||
this._$modalFooter = this._$modal.find('.modal-footer').first();
|
||
|
||
this._$lightboxContainer = this._$modalBody.find('.ekko-lightbox-container').first();
|
||
this._$lightboxBodyOne = this._$lightboxContainer.find('> div:first-child').first();
|
||
this._$lightboxBodyTwo = this._$lightboxContainer.find('> div:last-child').first();
|
||
|
||
this._border = this._calculateBorders();
|
||
this._padding = this._calculatePadding();
|
||
|
||
this._galleryName = this._$element.data('gallery');
|
||
if (this._galleryName) {
|
||
this._$galleryItems = $(document.body).find('*[data-gallery="' + this._galleryName + '"]');
|
||
this._galleryIndex = this._$galleryItems.index(this._$element);
|
||
$(document).on('keydown.ekkoLightbox', this._navigationalBinder.bind(this));
|
||
|
||
// add the directional arrows to the modal
|
||
if (this._config.showArrows && this._$galleryItems.length > 1) {
|
||
this._$lightboxContainer.append('<div class="ekko-lightbox-nav-overlay"><a href="#">' + this._config.leftArrow + '</a><a href="#">' + this._config.rightArrow + '</a></div>');
|
||
this._$modalArrows = this._$lightboxContainer.find('div.ekko-lightbox-nav-overlay').first();
|
||
this._$lightboxContainer.on('click', 'a:first-child', function (event) {
|
||
event.preventDefault();
|
||
return _this.navigateLeft();
|
||
});
|
||
this._$lightboxContainer.on('click', 'a:last-child', function (event) {
|
||
event.preventDefault();
|
||
return _this.navigateRight();
|
||
});
|
||
this.updateNavigation();
|
||
}
|
||
}
|
||
|
||
this._$modal.on('show.bs.modal', this._config.onShow.bind(this)).on('shown.bs.modal', function () {
|
||
_this._toggleLoading(true);
|
||
_this._handle();
|
||
return _this._config.onShown.call(_this);
|
||
}).on('hide.bs.modal', this._config.onHide.bind(this)).on('hidden.bs.modal', function () {
|
||
if (_this._galleryName) {
|
||
$(document).off('keydown.ekkoLightbox');
|
||
$(window).off('resize.ekkoLightbox');
|
||
}
|
||
_this._$modal.remove();
|
||
return _this._config.onHidden.call(_this);
|
||
}).modal(this._config);
|
||
|
||
$(window).on('resize.ekkoLightbox', function () {
|
||
_this._resize(_this._wantedWidth, _this._wantedHeight);
|
||
});
|
||
this._$lightboxContainer.on('touchstart', function () {
|
||
_this._touchstartX = event.changedTouches[0].screenX;
|
||
}).on('touchend', function () {
|
||
_this._touchendX = event.changedTouches[0].screenX;
|
||
_this._swipeGesure();
|
||
});
|
||
}
|
||
|
||
_createClass(Lightbox, [{
|
||
key: 'element',
|
||
value: function element() {
|
||
return this._$element;
|
||
}
|
||
}, {
|
||
key: 'modal',
|
||
value: function modal() {
|
||
return this._$modal;
|
||
}
|
||
}, {
|
||
key: 'navigateTo',
|
||
value: function navigateTo(index) {
|
||
|
||
if (index < 0 || index > this._$galleryItems.length - 1) return this;
|
||
|
||
this._galleryIndex = index;
|
||
|
||
this.updateNavigation();
|
||
|
||
this._$element = $(this._$galleryItems.get(this._galleryIndex));
|
||
this._handle();
|
||
}
|
||
}, {
|
||
key: 'navigateLeft',
|
||
value: function navigateLeft() {
|
||
|
||
if (!this._$galleryItems) return;
|
||
|
||
if (this._$galleryItems.length === 1) return;
|
||
|
||
if (this._galleryIndex === 0) {
|
||
if (this._config.wrapping) this._galleryIndex = this._$galleryItems.length - 1;else return;
|
||
} else //circular
|
||
this._galleryIndex--;
|
||
|
||
this._config.onNavigate.call(this, 'left', this._galleryIndex);
|
||
return this.navigateTo(this._galleryIndex);
|
||
}
|
||
}, {
|
||
key: 'navigateRight',
|
||
value: function navigateRight() {
|
||
|
||
if (!this._$galleryItems) return;
|
||
|
||
if (this._$galleryItems.length === 1) return;
|
||
|
||
if (this._galleryIndex === this._$galleryItems.length - 1) {
|
||
if (this._config.wrapping) this._galleryIndex = 0;else return;
|
||
} else //circular
|
||
this._galleryIndex++;
|
||
|
||
this._config.onNavigate.call(this, 'right', this._galleryIndex);
|
||
return this.navigateTo(this._galleryIndex);
|
||
}
|
||
}, {
|
||
key: 'updateNavigation',
|
||
value: function updateNavigation() {
|
||
if (!this._config.wrapping) {
|
||
var $nav = this._$lightboxContainer.find('div.ekko-lightbox-nav-overlay');
|
||
if (this._galleryIndex === 0) $nav.find('a:first-child').addClass('disabled');else $nav.find('a:first-child').removeClass('disabled');
|
||
|
||
if (this._galleryIndex === this._$galleryItems.length - 1) $nav.find('a:last-child').addClass('disabled');else $nav.find('a:last-child').removeClass('disabled');
|
||
}
|
||
}
|
||
}, {
|
||
key: 'close',
|
||
value: function close() {
|
||
return this._$modal.modal('hide');
|
||
}
|
||
|
||
// helper private methods
|
||
}, {
|
||
key: '_navigationalBinder',
|
||
value: function _navigationalBinder(event) {
|
||
event = event || window.event;
|
||
if (event.keyCode === 39) return this.navigateRight();
|
||
if (event.keyCode === 37) return this.navigateLeft();
|
||
}
|
||
|
||
// type detection private methods
|
||
}, {
|
||
key: '_detectRemoteType',
|
||
value: function _detectRemoteType(src, type) {
|
||
|
||
type = type || false;
|
||
|
||
if (!type && this._isImage(src)) type = 'image';
|
||
if (!type && this._getYoutubeId(src)) type = 'youtube';
|
||
if (!type && this._getVimeoId(src)) type = 'vimeo';
|
||
if (!type && this._getInstagramId(src)) type = 'instagram';
|
||
|
||
if (!type || ['image', 'youtube', 'vimeo', 'instagram', 'video', 'url'].indexOf(type) < 0) type = 'url';
|
||
|
||
return type;
|
||
}
|
||
}, {
|
||
key: '_isImage',
|
||
value: function _isImage(string) {
|
||
return string && string.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i);
|
||
}
|
||
}, {
|
||
key: '_containerToUse',
|
||
value: function _containerToUse() {
|
||
var _this2 = this;
|
||
|
||
// if currently showing an image, fade it out and remove
|
||
var $toUse = this._$lightboxBodyTwo;
|
||
var $current = this._$lightboxBodyOne;
|
||
|
||
if (this._$lightboxBodyTwo.hasClass('in')) {
|
||
$toUse = this._$lightboxBodyOne;
|
||
$current = this._$lightboxBodyTwo;
|
||
}
|
||
|
||
$current.removeClass('in show');
|
||
setTimeout(function () {
|
||
if (!_this2._$lightboxBodyTwo.hasClass('in')) _this2._$lightboxBodyTwo.empty();
|
||
if (!_this2._$lightboxBodyOne.hasClass('in')) _this2._$lightboxBodyOne.empty();
|
||
}, 500);
|
||
|
||
$toUse.addClass('in show');
|
||
return $toUse;
|
||
}
|
||
}, {
|
||
key: '_handle',
|
||
value: function _handle() {
|
||
|
||
var $toUse = this._containerToUse();
|
||
this._updateTitleAndFooter();
|
||
|
||
var currentRemote = this._$element.attr('data-remote') || this._$element.attr('href');
|
||
var currentType = this._detectRemoteType(currentRemote, this._$element.attr('data-type') || false);
|
||
|
||
if (['image', 'youtube', 'vimeo', 'instagram', 'video', 'url'].indexOf(currentType) < 0) return this._error(this._config.strings.type);
|
||
|
||
switch (currentType) {
|
||
case 'image':
|
||
this._preloadImage(currentRemote, $toUse);
|
||
this._preloadImageByIndex(this._galleryIndex, 3);
|
||
break;
|
||
case 'youtube':
|
||
this._showYoutubeVideo(currentRemote, $toUse);
|
||
break;
|
||
case 'vimeo':
|
||
this._showVimeoVideo(this._getVimeoId(currentRemote), $toUse);
|
||
break;
|
||
case 'instagram':
|
||
this._showInstagramVideo(this._getInstagramId(currentRemote), $toUse);
|
||
break;
|
||
case 'video':
|
||
this._showHtml5Video(currentRemote, $toUse);
|
||
break;
|
||
default:
|
||
// url
|
||
this._loadRemoteContent(currentRemote, $toUse);
|
||
break;
|
||
}
|
||
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_getYoutubeId',
|
||
value: function _getYoutubeId(string) {
|
||
if (!string) return false;
|
||
var matches = string.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/);
|
||
return matches && matches[2].length === 11 ? matches[2] : false;
|
||
}
|
||
}, {
|
||
key: '_getVimeoId',
|
||
value: function _getVimeoId(string) {
|
||
return string && string.indexOf('vimeo') > 0 ? string : false;
|
||
}
|
||
}, {
|
||
key: '_getInstagramId',
|
||
value: function _getInstagramId(string) {
|
||
return string && string.indexOf('instagram') > 0 ? string : false;
|
||
}
|
||
|
||
// layout private methods
|
||
}, {
|
||
key: '_toggleLoading',
|
||
value: function _toggleLoading(show) {
|
||
show = show || false;
|
||
if (show) {
|
||
this._$modalDialog.css('display', 'none');
|
||
this._$modal.removeClass('in show');
|
||
$('.modal-backdrop').append(this._config.loadingMessage);
|
||
} else {
|
||
this._$modalDialog.css('display', 'block');
|
||
this._$modal.addClass('in show');
|
||
$('.modal-backdrop').find('.ekko-lightbox-loader').remove();
|
||
}
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_calculateBorders',
|
||
value: function _calculateBorders() {
|
||
return {
|
||
top: this._totalCssByAttribute('border-top-width'),
|
||
right: this._totalCssByAttribute('border-right-width'),
|
||
bottom: this._totalCssByAttribute('border-bottom-width'),
|
||
left: this._totalCssByAttribute('border-left-width')
|
||
};
|
||
}
|
||
}, {
|
||
key: '_calculatePadding',
|
||
value: function _calculatePadding() {
|
||
return {
|
||
top: this._totalCssByAttribute('padding-top'),
|
||
right: this._totalCssByAttribute('padding-right'),
|
||
bottom: this._totalCssByAttribute('padding-bottom'),
|
||
left: this._totalCssByAttribute('padding-left')
|
||
};
|
||
}
|
||
}, {
|
||
key: '_totalCssByAttribute',
|
||
value: function _totalCssByAttribute(attribute) {
|
||
return parseInt(this._$modalDialog.css(attribute), 10) + parseInt(this._$modalContent.css(attribute), 10) + parseInt(this._$modalBody.css(attribute), 10);
|
||
}
|
||
}, {
|
||
key: '_updateTitleAndFooter',
|
||
value: function _updateTitleAndFooter() {
|
||
var title = this._$element.data('title') || "";
|
||
var caption = this._$element.data('footer') || "";
|
||
|
||
this._titleIsShown = false;
|
||
if (title || this._config.alwaysShowClose) {
|
||
this._titleIsShown = true;
|
||
this._$modalHeader.css('display', '').find('.modal-title').html(title || " ");
|
||
} else this._$modalHeader.css('display', 'none');
|
||
|
||
this._footerIsShown = false;
|
||
if (caption) {
|
||
this._footerIsShown = true;
|
||
this._$modalFooter.css('display', '').html(caption);
|
||
} else this._$modalFooter.css('display', 'none');
|
||
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_showYoutubeVideo',
|
||
value: function _showYoutubeVideo(remote, $containerForElement) {
|
||
var id = this._getYoutubeId(remote);
|
||
var query = remote.indexOf('&') > 0 ? remote.substr(remote.indexOf('&')) : '';
|
||
var width = this._$element.data('width') || 560;
|
||
var height = this._$element.data('height') || width / (560 / 315);
|
||
return this._showVideoIframe('//www.youtube.com/embed/' + id + '?badge=0&autoplay=1&html5=1' + query, width, height, $containerForElement);
|
||
}
|
||
}, {
|
||
key: '_showVimeoVideo',
|
||
value: function _showVimeoVideo(id, $containerForElement) {
|
||
var width = this._$element.data('width') || 500;
|
||
var height = this._$element.data('height') || width / (560 / 315);
|
||
return this._showVideoIframe(id + '?autoplay=1', width, height, $containerForElement);
|
||
}
|
||
}, {
|
||
key: '_showInstagramVideo',
|
||
value: function _showInstagramVideo(id, $containerForElement) {
|
||
// instagram load their content into iframe's so this can be put straight into the element
|
||
var width = this._$element.data('width') || 612;
|
||
var height = width + 80;
|
||
id = id.substr(-1) !== '/' ? id + '/' : id; // ensure id has trailing slash
|
||
$containerForElement.html('<iframe width="' + width + '" height="' + height + '" src="' + id + 'embed/" frameborder="0" allowfullscreen></iframe>');
|
||
this._resize(width, height);
|
||
this._config.onContentLoaded.call(this);
|
||
if (this._$modalArrows) //hide the arrows when showing video
|
||
this._$modalArrows.css('display', 'none');
|
||
this._toggleLoading(false);
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_showVideoIframe',
|
||
value: function _showVideoIframe(url, width, height, $containerForElement) {
|
||
// should be used for videos only. for remote content use loadRemoteContent (data-type=url)
|
||
height = height || width; // default to square
|
||
$containerForElement.html('<div class="embed-responsive embed-responsive-16by9"><iframe width="' + width + '" height="' + height + '" src="' + url + '" frameborder="0" allowfullscreen class="embed-responsive-item"></iframe></div>');
|
||
this._resize(width, height);
|
||
this._config.onContentLoaded.call(this);
|
||
if (this._$modalArrows) this._$modalArrows.css('display', 'none'); //hide the arrows when showing video
|
||
this._toggleLoading(false);
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_showHtml5Video',
|
||
value: function _showHtml5Video(url, $containerForElement) {
|
||
// should be used for videos only. for remote content use loadRemoteContent (data-type=url)
|
||
var width = this._$element.data('width') || 560;
|
||
var height = this._$element.data('height') || width / (560 / 315);
|
||
$containerForElement.html('<div class="embed-responsive embed-responsive-16by9"><video width="' + width + '" height="' + height + '" src="' + url + '" preload="auto" autoplay controls class="embed-responsive-item"></video></div>');
|
||
this._resize(width, height);
|
||
this._config.onContentLoaded.call(this);
|
||
if (this._$modalArrows) this._$modalArrows.css('display', 'none'); //hide the arrows when showing video
|
||
this._toggleLoading(false);
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_loadRemoteContent',
|
||
value: function _loadRemoteContent(url, $containerForElement) {
|
||
var _this3 = this;
|
||
|
||
var width = this._$element.data('width') || 560;
|
||
var height = this._$element.data('height') || 560;
|
||
|
||
var disableExternalCheck = this._$element.data('disableExternalCheck') || false;
|
||
this._toggleLoading(false);
|
||
|
||
// external urls are loading into an iframe
|
||
// local ajax can be loaded into the container itself
|
||
if (!disableExternalCheck && !this._isExternal(url)) {
|
||
$containerForElement.load(url, $.proxy(function () {
|
||
return _this3._$element.trigger('loaded.bs.modal');l;
|
||
}));
|
||
} else {
|
||
$containerForElement.html('<iframe src="' + url + '" frameborder="0" allowfullscreen></iframe>');
|
||
this._config.onContentLoaded.call(this);
|
||
}
|
||
|
||
if (this._$modalArrows) //hide the arrows when remote content
|
||
this._$modalArrows.css('display', 'none');
|
||
|
||
this._resize(width, height);
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_isExternal',
|
||
value: function _isExternal(url) {
|
||
var match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
|
||
if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol) return true;
|
||
|
||
if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(':(' + ({
|
||
"http:": 80,
|
||
"https:": 443
|
||
})[location.protocol] + ')?$'), "") !== location.host) return true;
|
||
|
||
return false;
|
||
}
|
||
}, {
|
||
key: '_error',
|
||
value: function _error(message) {
|
||
console.error(message);
|
||
this._containerToUse().html(message);
|
||
this._resize(300, 300);
|
||
return this;
|
||
}
|
||
}, {
|
||
key: '_preloadImageByIndex',
|
||
value: function _preloadImageByIndex(startIndex, numberOfTimes) {
|
||
|
||
if (!this._$galleryItems) return;
|
||
|
||
var next = $(this._$galleryItems.get(startIndex), false);
|
||
if (typeof next == 'undefined') return;
|
||
|
||
var src = next.attr('data-remote') || next.attr('href');
|
||
if (next.attr('data-type') === 'image' || this._isImage(src)) this._preloadImage(src, false);
|
||
|
||
if (numberOfTimes > 0) return this._preloadImageByIndex(startIndex + 1, numberOfTimes - 1);
|
||
}
|
||
}, {
|
||
key: '_preloadImage',
|
||
value: function _preloadImage(src, $containerForImage) {
|
||
var _this4 = this;
|
||
|
||
$containerForImage = $containerForImage || false;
|
||
|
||
var img = new Image();
|
||
if ($containerForImage) {
|
||
(function () {
|
||
|
||
// if loading takes > 200ms show a loader
|
||
var loadingTimeout = setTimeout(function () {
|
||
$containerForImage.append(_this4._config.loadingMessage);
|
||
}, 200);
|
||
|
||
img.onload = function () {
|
||
if (loadingTimeout) clearTimeout(loadingTimeout);
|
||
loadingTimeout = null;
|
||
var image = $('<img />');
|
||
image.attr('src', img.src);
|
||
image.addClass('img-fluid');
|
||
|
||
// backward compatibility for bootstrap v3
|
||
image.css('width', '100%');
|
||
|
||
$containerForImage.html(image);
|
||
if (_this4._$modalArrows) _this4._$modalArrows.css('display', ''); // remove display to default to css property
|
||
|
||
_this4._resize(img.width, img.height);
|
||
_this4._toggleLoading(false);
|
||
return _this4._config.onContentLoaded.call(_this4);
|
||
};
|
||
img.onerror = function () {
|
||
_this4._toggleLoading(false);
|
||
return _this4._error(_this4._config.strings.fail + (' ' + src));
|
||
};
|
||
})();
|
||
}
|
||
|
||
img.src = src;
|
||
return img;
|
||
}
|
||
}, {
|
||
key: '_swipeGesure',
|
||
value: function _swipeGesure() {
|
||
if (this._touchendX < this._touchstartX) {
|
||
return this.navigateRight();
|
||
}
|
||
if (this._touchendX > this._touchstartX) {
|
||
return this.navigateLeft();
|
||
}
|
||
}
|
||
}, {
|
||
key: '_resize',
|
||
value: function _resize(width, height) {
|
||
|
||
height = height || width;
|
||
this._wantedWidth = width;
|
||
this._wantedHeight = height;
|
||
|
||
var imageAspecRatio = width / height;
|
||
|
||
// if width > the available space, scale down the expected width and height
|
||
var widthBorderAndPadding = this._padding.left + this._padding.right + this._border.left + this._border.right;
|
||
|
||
// force 10px margin if window size > 575px
|
||
var addMargin = this._config.doc.body.clientWidth > 575 ? 20 : 0;
|
||
var discountMargin = this._config.doc.body.clientWidth > 575 ? 0 : 20;
|
||
|
||
var maxWidth = Math.min(width + widthBorderAndPadding, this._config.doc.body.clientWidth - addMargin, this._config.maxWidth);
|
||
|
||
if (width + widthBorderAndPadding > maxWidth) {
|
||
height = (maxWidth - widthBorderAndPadding - discountMargin) / imageAspecRatio;
|
||
width = maxWidth;
|
||
} else width = width + widthBorderAndPadding;
|
||
|
||
var headerHeight = 0,
|
||
footerHeight = 0;
|
||
|
||
// as the resize is performed the modal is show, the calculate might fail
|
||
// if so, default to the default sizes
|
||
if (this._footerIsShown) footerHeight = this._$modalFooter.outerHeight(true) || 55;
|
||
|
||
if (this._titleIsShown) headerHeight = this._$modalHeader.outerHeight(true) || 67;
|
||
|
||
var borderPadding = this._padding.top + this._padding.bottom + this._border.bottom + this._border.top;
|
||
|
||
//calculated each time as resizing the window can cause them to change due to Bootstraps fluid margins
|
||
var margins = parseFloat(this._$modalDialog.css('margin-top')) + parseFloat(this._$modalDialog.css('margin-bottom'));
|
||
|
||
var maxHeight = Math.min(height, $(window).height() - borderPadding - margins - headerHeight - footerHeight, this._config.maxHeight - borderPadding - headerHeight - footerHeight);
|
||
|
||
if (height > maxHeight) {
|
||
// if height > the available height, scale down the width
|
||
width = Math.ceil(maxHeight * imageAspecRatio) + widthBorderAndPadding;
|
||
}
|
||
|
||
this._$lightboxContainer.css('height', maxHeight);
|
||
this._$modalDialog.css('flex', 1).css('maxWidth', width);
|
||
|
||
var modal = this._$modal.data('bs.modal');
|
||
if (modal) {
|
||
// v4 method is mistakenly protected
|
||
try {
|
||
modal._handleUpdate();
|
||
} catch (Exception) {
|
||
modal.handleUpdate();
|
||
}
|
||
}
|
||
return this;
|
||
}
|
||
}], [{
|
||
key: '_jQueryInterface',
|
||
value: function _jQueryInterface(config) {
|
||
var _this5 = this;
|
||
|
||
config = config || {};
|
||
return this.each(function () {
|
||
var $this = $(_this5);
|
||
var _config = $.extend({}, Lightbox.Default, $this.data(), typeof config === 'object' && config);
|
||
|
||
new Lightbox(_this5, _config);
|
||
});
|
||
}
|
||
}]);
|
||
|
||
return Lightbox;
|
||
})();
|
||
|
||
$.fn[NAME] = Lightbox._jQueryInterface;
|
||
$.fn[NAME].Constructor = Lightbox;
|
||
$.fn[NAME].noConflict = function () {
|
||
$.fn[NAME] = JQUERY_NO_CONFLICT;
|
||
return Lightbox._jQueryInterface;
|
||
};
|
||
|
||
return Lightbox;
|
||
})(jQuery);
|
||
//# sourceMappingURL=ekko-lightbox.js.map
|
||
|
||
}(jQuery);
|
||
|
||
/*!
|
||
* pGenerator jQuery Plugin v1.0.5
|
||
* https://github.com/M1Sh0u/pGenerator
|
||
*
|
||
* Created by Mihai MATEI <[email protected]>
|
||
* Released under the MIT License (Feel free to copy, modify or redistribute this plugin.)
|
||
*/
|
||
|
||
(function($){
|
||
|
||
var numbers_array = [],
|
||
upper_letters_array = [],
|
||
lower_letters_array = [],
|
||
special_chars_array = [],
|
||
$pGeneratorElement = null;
|
||
|
||
/**
|
||
* Plugin methods.
|
||
*
|
||
* @type {{init: init, generatePassword: generatePassword}}
|
||
*/
|
||
var methods = {
|
||
|
||
/**
|
||
* Initialize the object.
|
||
*
|
||
* @param options
|
||
* @param callbacks
|
||
*
|
||
* @returns {*}
|
||
*/
|
||
init: function(options, callbacks)
|
||
{
|
||
var settings = $.extend({
|
||
'bind': 'click',
|
||
'passwordElement': null,
|
||
'displayElement': null,
|
||
'passwordLength': 16,
|
||
'uppercase': true,
|
||
'lowercase': true,
|
||
'numbers': true,
|
||
'specialChars': true,
|
||
'additionalSpecialChars': [],
|
||
'onPasswordGenerated': function(generatedPassword) { }
|
||
}, options);
|
||
|
||
for(var i = 48; i < 58; i++) {
|
||
numbers_array.push(i);
|
||
}
|
||
|
||
for(i = 65; i < 91; i++) {
|
||
upper_letters_array.push(i);
|
||
}
|
||
|
||
for(i = 97; i < 123; i++) {
|
||
lower_letters_array.push(i);
|
||
}
|
||
|
||
special_chars_array = [33, 35, 64, 36, 38, 42, 91, 93, 123, 125, 92, 47, 63, 58, 59, 95, 45].concat(settings.additionalSpecialChars);
|
||
|
||
return this.each(function(){
|
||
|
||
$pGeneratorElement = $(this);
|
||
|
||
$pGeneratorElement.bind(settings.bind, function(e){
|
||
e.preventDefault();
|
||
methods.generatePassword(settings);
|
||
});
|
||
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Generate the password.
|
||
*
|
||
* @param {object} settings
|
||
*/
|
||
generatePassword: function(settings)
|
||
{
|
||
var password = new Array(),
|
||
selOptions = settings.uppercase + settings.lowercase + settings.numbers + settings.specialChars,
|
||
selected = 0,
|
||
no_lower_letters = new Array();
|
||
|
||
var optionLength = Math.floor(settings.passwordLength / selOptions);
|
||
|
||
if(settings.uppercase) {
|
||
// uppercase letters
|
||
for(var i = 0; i < optionLength; i++) {
|
||
password.push(String.fromCharCode(upper_letters_array[randomFromInterval(0, upper_letters_array.length - 1)]));
|
||
}
|
||
|
||
no_lower_letters = no_lower_letters.concat(upper_letters_array);
|
||
|
||
selected++;
|
||
}
|
||
|
||
if(settings.numbers) {
|
||
// numbers letters
|
||
for(var i = 0; i < optionLength; i++) {
|
||
password.push(String.fromCharCode(numbers_array[randomFromInterval(0, numbers_array.length - 1)]));
|
||
}
|
||
|
||
no_lower_letters = no_lower_letters.concat(numbers_array);
|
||
|
||
selected++;
|
||
}
|
||
|
||
if(settings.specialChars) {
|
||
// numbers letters
|
||
for(var i = 0; i < optionLength; i++) {
|
||
password.push(String.fromCharCode(special_chars_array[randomFromInterval(0, special_chars_array.length - 1)]));
|
||
}
|
||
|
||
no_lower_letters = no_lower_letters.concat(special_chars_array);
|
||
|
||
selected++;
|
||
}
|
||
|
||
var remained = settings.passwordLength - (selected * optionLength);
|
||
|
||
if(settings.lowercase) {
|
||
|
||
for(var i = 0; i < remained; i++) {
|
||
password.push(String.fromCharCode(lower_letters_array[randomFromInterval(0, lower_letters_array.length - 1)]));
|
||
}
|
||
|
||
} else {
|
||
|
||
for(var i = 0; i < remained; i++) {
|
||
password.push(String.fromCharCode(no_lower_letters[randomFromInterval(0, no_lower_letters.length - 1)]));
|
||
}
|
||
}
|
||
|
||
password = shuffle(password).join('');
|
||
|
||
if(settings.passwordElement !== null) {
|
||
$(settings.passwordElement).val(password);
|
||
}
|
||
|
||
if(settings.displayElement !== null) {
|
||
if($(settings.displayElement).is("input")) {
|
||
$(settings.displayElement).val(password);
|
||
} else {
|
||
$(settings.displayElement).text(password);
|
||
}
|
||
}
|
||
|
||
settings.onPasswordGenerated(password);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Shuffle the password.
|
||
*
|
||
* @param {Array} o
|
||
*
|
||
* @returns {Array}
|
||
*/
|
||
function shuffle(o)
|
||
{
|
||
for(var j, x, i = o.length; i; j = parseInt(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
|
||
|
||
return o;
|
||
}
|
||
|
||
/**
|
||
* Get a random number in the given interval.
|
||
*
|
||
* @param {number} from
|
||
* @param {number} to
|
||
*
|
||
* @returns {number}
|
||
*/
|
||
function randomFromInterval(from, to)
|
||
{
|
||
return Math.floor(Math.random()*(to-from+1)+from);
|
||
}
|
||
|
||
/**
|
||
* Define the pGenerator jQuery plugin.
|
||
*
|
||
* @param method
|
||
* @returns {*}
|
||
*/
|
||
$.fn.pGenerator = function(method)
|
||
{
|
||
if (methods[method]) {
|
||
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
|
||
}
|
||
else if (typeof method === 'object' || !method) {
|
||
return methods.init.apply(this, arguments);
|
||
}
|
||
else {
|
||
$.error( 'Method ' + method + ' does not exist on jQuery.pGenerator' );
|
||
}
|
||
};
|
||
|
||
})(jQuery);
|
||
/*!
|
||
* Chart.js v2.9.4
|
||
* https://www.chartjs.org
|
||
* (c) 2020 Chart.js Contributors
|
||
* Released under the MIT License
|
||
*/
|
||
(function (global, factory) {
|
||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(function() { try { return require('moment'); } catch(e) { } }()) :
|
||
typeof define === 'function' && define.amd ? define(['require'], function(require) { return factory(function() { try { return require('moment'); } catch(e) { } }()); }) :
|
||
(global = global || self, global.Chart = factory(global.moment));
|
||
}(this, (function (moment) { 'use strict';
|
||
|
||
moment = moment && moment.hasOwnProperty('default') ? moment['default'] : moment;
|
||
|
||
function createCommonjsModule(fn, module) {
|
||
return module = { exports: {} }, fn(module, module.exports), module.exports;
|
||
}
|
||
|
||
function getCjsExportFromNamespace (n) {
|
||
return n && n['default'] || n;
|
||
}
|
||
|
||
var colorName = {
|
||
"aliceblue": [240, 248, 255],
|
||
"antiquewhite": [250, 235, 215],
|
||
"aqua": [0, 255, 255],
|
||
"aquamarine": [127, 255, 212],
|
||
"azure": [240, 255, 255],
|
||
"beige": [245, 245, 220],
|
||
"bisque": [255, 228, 196],
|
||
"black": [0, 0, 0],
|
||
"blanchedalmond": [255, 235, 205],
|
||
"blue": [0, 0, 255],
|
||
"blueviolet": [138, 43, 226],
|
||
"brown": [165, 42, 42],
|
||
"burlywood": [222, 184, 135],
|
||
"cadetblue": [95, 158, 160],
|
||
"chartreuse": [127, 255, 0],
|
||
"chocolate": [210, 105, 30],
|
||
"coral": [255, 127, 80],
|
||
"cornflowerblue": [100, 149, 237],
|
||
"cornsilk": [255, 248, 220],
|
||
"crimson": [220, 20, 60],
|
||
"cyan": [0, 255, 255],
|
||
"darkblue": [0, 0, 139],
|
||
"darkcyan": [0, 139, 139],
|
||
"darkgoldenrod": [184, 134, 11],
|
||
"darkgray": [169, 169, 169],
|
||
"darkgreen": [0, 100, 0],
|
||
"darkgrey": [169, 169, 169],
|
||
"darkkhaki": [189, 183, 107],
|
||
"darkmagenta": [139, 0, 139],
|
||
"darkolivegreen": [85, 107, 47],
|
||
"darkorange": [255, 140, 0],
|
||
"darkorchid": [153, 50, 204],
|
||
"darkred": [139, 0, 0],
|
||
"darksalmon": [233, 150, 122],
|
||
"darkseagreen": [143, 188, 143],
|
||
"darkslateblue": [72, 61, 139],
|
||
"darkslategray": [47, 79, 79],
|
||
"darkslategrey": [47, 79, 79],
|
||
"darkturquoise": [0, 206, 209],
|
||
"darkviolet": [148, 0, 211],
|
||
"deeppink": [255, 20, 147],
|
||
"deepskyblue": [0, 191, 255],
|
||
"dimgray": [105, 105, 105],
|
||
"dimgrey": [105, 105, 105],
|
||
"dodgerblue": [30, 144, 255],
|
||
"firebrick": [178, 34, 34],
|
||
"floralwhite": [255, 250, 240],
|
||
"forestgreen": [34, 139, 34],
|
||
"fuchsia": [255, 0, 255],
|
||
"gainsboro": [220, 220, 220],
|
||
"ghostwhite": [248, 248, 255],
|
||
"gold": [255, 215, 0],
|
||
"goldenrod": [218, 165, 32],
|
||
"gray": [128, 128, 128],
|
||
"green": [0, 128, 0],
|
||
"greenyellow": [173, 255, 47],
|
||
"grey": [128, 128, 128],
|
||
"honeydew": [240, 255, 240],
|
||
"hotpink": [255, 105, 180],
|
||
"indianred": [205, 92, 92],
|
||
"indigo": [75, 0, 130],
|
||
"ivory": [255, 255, 240],
|
||
"khaki": [240, 230, 140],
|
||
"lavender": [230, 230, 250],
|
||
"lavenderblush": [255, 240, 245],
|
||
"lawngreen": [124, 252, 0],
|
||
"lemonchiffon": [255, 250, 205],
|
||
"lightblue": [173, 216, 230],
|
||
"lightcoral": [240, 128, 128],
|
||
"lightcyan": [224, 255, 255],
|
||
"lightgoldenrodyellow": [250, 250, 210],
|
||
"lightgray": [211, 211, 211],
|
||
"lightgreen": [144, 238, 144],
|
||
"lightgrey": [211, 211, 211],
|
||
"lightpink": [255, 182, 193],
|
||
"lightsalmon": [255, 160, 122],
|
||
"lightseagreen": [32, 178, 170],
|
||
"lightskyblue": [135, 206, 250],
|
||
"lightslategray": [119, 136, 153],
|
||
"lightslategrey": [119, 136, 153],
|
||
"lightsteelblue": [176, 196, 222],
|
||
"lightyellow": [255, 255, 224],
|
||
"lime": [0, 255, 0],
|
||
"limegreen": [50, 205, 50],
|
||
"linen": [250, 240, 230],
|
||
"magenta": [255, 0, 255],
|
||
"maroon": [128, 0, 0],
|
||
"mediumaquamarine": [102, 205, 170],
|
||
"mediumblue": [0, 0, 205],
|
||
"mediumorchid": [186, 85, 211],
|
||
"mediumpurple": [147, 112, 219],
|
||
"mediumseagreen": [60, 179, 113],
|
||
"mediumslateblue": [123, 104, 238],
|
||
"mediumspringgreen": [0, 250, 154],
|
||
"mediumturquoise": [72, 209, 204],
|
||
"mediumvioletred": [199, 21, 133],
|
||
"midnightblue": [25, 25, 112],
|
||
"mintcream": [245, 255, 250],
|
||
"mistyrose": [255, 228, 225],
|
||
"moccasin": [255, 228, 181],
|
||
"navajowhite": [255, 222, 173],
|
||
"navy": [0, 0, 128],
|
||
"oldlace": [253, 245, 230],
|
||
"olive": [128, 128, 0],
|
||
"olivedrab": [107, 142, 35],
|
||
"orange": [255, 165, 0],
|
||
"orangered": [255, 69, 0],
|
||
"orchid": [218, 112, 214],
|
||
"palegoldenrod": [238, 232, 170],
|
||
"palegreen": [152, 251, 152],
|
||
"paleturquoise": [175, 238, 238],
|
||
"palevioletred": [219, 112, 147],
|
||
"papayawhip": [255, 239, 213],
|
||
"peachpuff": [255, 218, 185],
|
||
"peru": [205, 133, 63],
|
||
"pink": [255, 192, 203],
|
||
"plum": [221, 160, 221],
|
||
"powderblue": [176, 224, 230],
|
||
"purple": [128, 0, 128],
|
||
"rebeccapurple": [102, 51, 153],
|
||
"red": [255, 0, 0],
|
||
"rosybrown": [188, 143, 143],
|
||
"royalblue": [65, 105, 225],
|
||
"saddlebrown": [139, 69, 19],
|
||
"salmon": [250, 128, 114],
|
||
"sandybrown": [244, 164, 96],
|
||
"seagreen": [46, 139, 87],
|
||
"seashell": [255, 245, 238],
|
||
"sienna": [160, 82, 45],
|
||
"silver": [192, 192, 192],
|
||
"skyblue": [135, 206, 235],
|
||
"slateblue": [106, 90, 205],
|
||
"slategray": [112, 128, 144],
|
||
"slategrey": [112, 128, 144],
|
||
"snow": [255, 250, 250],
|
||
"springgreen": [0, 255, 127],
|
||
"steelblue": [70, 130, 180],
|
||
"tan": [210, 180, 140],
|
||
"teal": [0, 128, 128],
|
||
"thistle": [216, 191, 216],
|
||
"tomato": [255, 99, 71],
|
||
"turquoise": [64, 224, 208],
|
||
"violet": [238, 130, 238],
|
||
"wheat": [245, 222, 179],
|
||
"white": [255, 255, 255],
|
||
"whitesmoke": [245, 245, 245],
|
||
"yellow": [255, 255, 0],
|
||
"yellowgreen": [154, 205, 50]
|
||
};
|
||
|
||
var conversions = createCommonjsModule(function (module) {
|
||
/* MIT license */
|
||
|
||
|
||
// NOTE: conversions should only return primitive values (i.e. arrays, or
|
||
// values that give correct `typeof` results).
|
||
// do not use box values types (i.e. Number(), String(), etc.)
|
||
|
||
var reverseKeywords = {};
|
||
for (var key in colorName) {
|
||
if (colorName.hasOwnProperty(key)) {
|
||
reverseKeywords[colorName[key]] = key;
|
||
}
|
||
}
|
||
|
||
var convert = module.exports = {
|
||
rgb: {channels: 3, labels: 'rgb'},
|
||
hsl: {channels: 3, labels: 'hsl'},
|
||
hsv: {channels: 3, labels: 'hsv'},
|
||
hwb: {channels: 3, labels: 'hwb'},
|
||
cmyk: {channels: 4, labels: 'cmyk'},
|
||
xyz: {channels: 3, labels: 'xyz'},
|
||
lab: {channels: 3, labels: 'lab'},
|
||
lch: {channels: 3, labels: 'lch'},
|
||
hex: {channels: 1, labels: ['hex']},
|
||
keyword: {channels: 1, labels: ['keyword']},
|
||
ansi16: {channels: 1, labels: ['ansi16']},
|
||
ansi256: {channels: 1, labels: ['ansi256']},
|
||
hcg: {channels: 3, labels: ['h', 'c', 'g']},
|
||
apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
|
||
gray: {channels: 1, labels: ['gray']}
|
||
};
|
||
|
||
// hide .channels and .labels properties
|
||
for (var model in convert) {
|
||
if (convert.hasOwnProperty(model)) {
|
||
if (!('channels' in convert[model])) {
|
||
throw new Error('missing channels property: ' + model);
|
||
}
|
||
|
||
if (!('labels' in convert[model])) {
|
||
throw new Error('missing channel labels property: ' + model);
|
||
}
|
||
|
||
if (convert[model].labels.length !== convert[model].channels) {
|
||
throw new Error('channel and label counts mismatch: ' + model);
|
||
}
|
||
|
||
var channels = convert[model].channels;
|
||
var labels = convert[model].labels;
|
||
delete convert[model].channels;
|
||
delete convert[model].labels;
|
||
Object.defineProperty(convert[model], 'channels', {value: channels});
|
||
Object.defineProperty(convert[model], 'labels', {value: labels});
|
||
}
|
||
}
|
||
|
||
convert.rgb.hsl = function (rgb) {
|
||
var r = rgb[0] / 255;
|
||
var g = rgb[1] / 255;
|
||
var b = rgb[2] / 255;
|
||
var min = Math.min(r, g, b);
|
||
var max = Math.max(r, g, b);
|
||
var delta = max - min;
|
||
var h;
|
||
var s;
|
||
var l;
|
||
|
||
if (max === min) {
|
||
h = 0;
|
||
} else if (r === max) {
|
||
h = (g - b) / delta;
|
||
} else if (g === max) {
|
||
h = 2 + (b - r) / delta;
|
||
} else if (b === max) {
|
||
h = 4 + (r - g) / delta;
|
||
}
|
||
|
||
h = Math.min(h * 60, 360);
|
||
|
||
if (h < 0) {
|
||
h += 360;
|
||
}
|
||
|
||
l = (min + max) / 2;
|
||
|
||
if (max === min) {
|
||
s = 0;
|
||
} else if (l <= 0.5) {
|
||
s = delta / (max + min);
|
||
} else {
|
||
s = delta / (2 - max - min);
|
||
}
|
||
|
||
return [h, s * 100, l * 100];
|
||
};
|
||
|
||
convert.rgb.hsv = function (rgb) {
|
||
var rdif;
|
||
var gdif;
|
||
var bdif;
|
||
var h;
|
||
var s;
|
||
|
||
var r = rgb[0] / 255;
|
||
var g = rgb[1] / 255;
|
||
var b = rgb[2] / 255;
|
||
var v = Math.max(r, g, b);
|
||
var diff = v - Math.min(r, g, b);
|
||
var diffc = function (c) {
|
||
return (v - c) / 6 / diff + 1 / 2;
|
||
};
|
||
|
||
if (diff === 0) {
|
||
h = s = 0;
|
||
} else {
|
||
s = diff / v;
|
||
rdif = diffc(r);
|
||
gdif = diffc(g);
|
||
bdif = diffc(b);
|
||
|
||
if (r === v) {
|
||
h = bdif - gdif;
|
||
} else if (g === v) {
|
||
h = (1 / 3) + rdif - bdif;
|
||
} else if (b === v) {
|
||
h = (2 / 3) + gdif - rdif;
|
||
}
|
||
if (h < 0) {
|
||
h += 1;
|
||
} else if (h > 1) {
|
||
h -= 1;
|
||
}
|
||
}
|
||
|
||
return [
|
||
h * 360,
|
||
s * 100,
|
||
v * 100
|
||
];
|
||
};
|
||
|
||
convert.rgb.hwb = function (rgb) {
|
||
var r = rgb[0];
|
||
var g = rgb[1];
|
||
var b = rgb[2];
|
||
var h = convert.rgb.hsl(rgb)[0];
|
||
var w = 1 / 255 * Math.min(r, Math.min(g, b));
|
||
|
||
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
|
||
|
||
return [h, w * 100, b * 100];
|
||
};
|
||
|
||
convert.rgb.cmyk = function (rgb) {
|
||
var r = rgb[0] / 255;
|
||
var g = rgb[1] / 255;
|
||
var b = rgb[2] / 255;
|
||
var c;
|
||
var m;
|
||
var y;
|
||
var k;
|
||
|
||
k = Math.min(1 - r, 1 - g, 1 - b);
|
||
c = (1 - r - k) / (1 - k) || 0;
|
||
m = (1 - g - k) / (1 - k) || 0;
|
||
y = (1 - b - k) / (1 - k) || 0;
|
||
|
||
return [c * 100, m * 100, y * 100, k * 100];
|
||
};
|
||
|
||
/**
|
||
* See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
|
||
* */
|
||
function comparativeDistance(x, y) {
|
||
return (
|
||
Math.pow(x[0] - y[0], 2) +
|
||
Math.pow(x[1] - y[1], 2) +
|
||
Math.pow(x[2] - y[2], 2)
|
||
);
|
||
}
|
||
|
||
convert.rgb.keyword = function (rgb) {
|
||
var reversed = reverseKeywords[rgb];
|
||
if (reversed) {
|
||
return reversed;
|
||
}
|
||
|
||
var currentClosestDistance = Infinity;
|
||
var currentClosestKeyword;
|
||
|
||
for (var keyword in colorName) {
|
||
if (colorName.hasOwnProperty(keyword)) {
|
||
var value = colorName[keyword];
|
||
|
||
// Compute comparative distance
|
||
var distance = comparativeDistance(rgb, value);
|
||
|
||
// Check if its less, if so set as closest
|
||
if (distance < currentClosestDistance) {
|
||
currentClosestDistance = distance;
|
||
currentClosestKeyword = keyword;
|
||
}
|
||
}
|
||
}
|
||
|
||
return currentClosestKeyword;
|
||
};
|
||
|
||
convert.keyword.rgb = function (keyword) {
|
||
return colorName[keyword];
|
||
};
|
||
|
||
convert.rgb.xyz = function (rgb) {
|
||
var r = rgb[0] / 255;
|
||
var g = rgb[1] / 255;
|
||
var b = rgb[2] / 255;
|
||
|
||
// assume sRGB
|
||
r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
|
||
g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
|
||
b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
|
||
|
||
var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
|
||
var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
|
||
var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
|
||
|
||
return [x * 100, y * 100, z * 100];
|
||
};
|
||
|
||
convert.rgb.lab = function (rgb) {
|
||
var xyz = convert.rgb.xyz(rgb);
|
||
var x = xyz[0];
|
||
var y = xyz[1];
|
||
var z = xyz[2];
|
||
var l;
|
||
var a;
|
||
var b;
|
||
|
||
x /= 95.047;
|
||
y /= 100;
|
||
z /= 108.883;
|
||
|
||
x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
|
||
y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
|
||
z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
|
||
|
||
l = (116 * y) - 16;
|
||
a = 500 * (x - y);
|
||
b = 200 * (y - z);
|
||
|
||
return [l, a, b];
|
||
};
|
||
|
||
convert.hsl.rgb = function (hsl) {
|
||
var h = hsl[0] / 360;
|
||
var s = hsl[1] / 100;
|
||
var l = hsl[2] / 100;
|
||
var t1;
|
||
var t2;
|
||
var t3;
|
||
var rgb;
|
||
var val;
|
||
|
||
if (s === 0) {
|
||
val = l * 255;
|
||
return [val, val, val];
|
||
}
|
||
|
||
if (l < 0.5) {
|
||
t2 = l * (1 + s);
|
||
} else {
|
||
t2 = l + s - l * s;
|
||
}
|
||
|
||
t1 = 2 * l - t2;
|
||
|
||
rgb = [0, 0, 0];
|
||
for (var i = 0; i < 3; i++) {
|
||
t3 = h + 1 / 3 * -(i - 1);
|
||
if (t3 < 0) {
|
||
t3++;
|
||
}
|
||
if (t3 > 1) {
|
||
t3--;
|
||
}
|
||
|
||
if (6 * t3 < 1) {
|
||
val = t1 + (t2 - t1) * 6 * t3;
|
||
} else if (2 * t3 < 1) {
|
||
val = t2;
|
||
} else if (3 * t3 < 2) {
|
||
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
|
||
} else {
|
||
val = t1;
|
||
}
|
||
|
||
rgb[i] = val * 255;
|
||
}
|
||
|
||
return rgb;
|
||
};
|
||
|
||
convert.hsl.hsv = function (hsl) {
|
||
var h = hsl[0];
|
||
var s = hsl[1] / 100;
|
||
var l = hsl[2] / 100;
|
||
var smin = s;
|
||
var lmin = Math.max(l, 0.01);
|
||
var sv;
|
||
var v;
|
||
|
||
l *= 2;
|
||
s *= (l <= 1) ? l : 2 - l;
|
||
smin *= lmin <= 1 ? lmin : 2 - lmin;
|
||
v = (l + s) / 2;
|
||
sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
|
||
|
||
return [h, sv * 100, v * 100];
|
||
};
|
||
|
||
convert.hsv.rgb = function (hsv) {
|
||
var h = hsv[0] / 60;
|
||
var s = hsv[1] / 100;
|
||
var v = hsv[2] / 100;
|
||
var hi = Math.floor(h) % 6;
|
||
|
||
var f = h - Math.floor(h);
|
||
var p = 255 * v * (1 - s);
|
||
var q = 255 * v * (1 - (s * f));
|
||
var t = 255 * v * (1 - (s * (1 - f)));
|
||
v *= 255;
|
||
|
||
switch (hi) {
|
||
case 0:
|
||
return [v, t, p];
|
||
case 1:
|
||
return [q, v, p];
|
||
case 2:
|
||
return [p, v, t];
|
||
case 3:
|
||
return [p, q, v];
|
||
case 4:
|
||
return [t, p, v];
|
||
case 5:
|
||
return [v, p, q];
|
||
}
|
||
};
|
||
|
||
convert.hsv.hsl = function (hsv) {
|
||
var h = hsv[0];
|
||
var s = hsv[1] / 100;
|
||
var v = hsv[2] / 100;
|
||
var vmin = Math.max(v, 0.01);
|
||
var lmin;
|
||
var sl;
|
||
var l;
|
||
|
||
l = (2 - s) * v;
|
||
lmin = (2 - s) * vmin;
|
||
sl = s * vmin;
|
||
sl /= (lmin <= 1) ? lmin : 2 - lmin;
|
||
sl = sl || 0;
|
||
l /= 2;
|
||
|
||
return [h, sl * 100, l * 100];
|
||
};
|
||
|
||
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
|
||
convert.hwb.rgb = function (hwb) {
|
||
var h = hwb[0] / 360;
|
||
var wh = hwb[1] / 100;
|
||
var bl = hwb[2] / 100;
|
||
var ratio = wh + bl;
|
||
var i;
|
||
var v;
|
||
var f;
|
||
var n;
|
||
|
||
// wh + bl cant be > 1
|
||
if (ratio > 1) {
|
||
wh /= ratio;
|
||
bl /= ratio;
|
||
}
|
||
|
||
i = Math.floor(6 * h);
|
||
v = 1 - bl;
|
||
f = 6 * h - i;
|
||
|
||
if ((i & 0x01) !== 0) {
|
||
f = 1 - f;
|
||
}
|
||
|
||
n = wh + f * (v - wh); // linear interpolation
|
||
|
||
var r;
|
||
var g;
|
||
var b;
|
||
switch (i) {
|
||
default:
|
||
case 6:
|
||
case 0: r = v; g = n; b = wh; break;
|
||
case 1: r = n; g = v; b = wh; break;
|
||
case 2: r = wh; g = v; b = n; break;
|
||
case 3: r = wh; g = n; b = v; break;
|
||
case 4: r = n; g = wh; b = v; break;
|
||
case 5: r = v; g = wh; b = n; break;
|
||
}
|
||
|
||
return [r * 255, g * 255, b * 255];
|
||
};
|
||
|
||
convert.cmyk.rgb = function (cmyk) {
|
||
var c = cmyk[0] / 100;
|
||
var m = cmyk[1] / 100;
|
||
var y = cmyk[2] / 100;
|
||
var k = cmyk[3] / 100;
|
||
var r;
|
||
var g;
|
||
var b;
|
||
|
||
r = 1 - Math.min(1, c * (1 - k) + k);
|
||
g = 1 - Math.min(1, m * (1 - k) + k);
|
||
b = 1 - Math.min(1, y * (1 - k) + k);
|
||
|
||
return [r * 255, g * 255, b * 255];
|
||
};
|
||
|
||
convert.xyz.rgb = function (xyz) {
|
||
var x = xyz[0] / 100;
|
||
var y = xyz[1] / 100;
|
||
var z = xyz[2] / 100;
|
||
var r;
|
||
var g;
|
||
var b;
|
||
|
||
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
|
||
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
|
||
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
|
||
|
||
// assume sRGB
|
||
r = r > 0.0031308
|
||
? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
|
||
: r * 12.92;
|
||
|
||
g = g > 0.0031308
|
||
? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
|
||
: g * 12.92;
|
||
|
||
b = b > 0.0031308
|
||
? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
|
||
: b * 12.92;
|
||
|
||
r = Math.min(Math.max(0, r), 1);
|
||
g = Math.min(Math.max(0, g), 1);
|
||
b = Math.min(Math.max(0, b), 1);
|
||
|
||
return [r * 255, g * 255, b * 255];
|
||
};
|
||
|
||
convert.xyz.lab = function (xyz) {
|
||
var x = xyz[0];
|
||
var y = xyz[1];
|
||
var z = xyz[2];
|
||
var l;
|
||
var a;
|
||
var b;
|
||
|
||
x /= 95.047;
|
||
y /= 100;
|
||
z /= 108.883;
|
||
|
||
x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116);
|
||
y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116);
|
||
z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116);
|
||
|
||
l = (116 * y) - 16;
|
||
a = 500 * (x - y);
|
||
b = 200 * (y - z);
|
||
|
||
return [l, a, b];
|
||
};
|
||
|
||
convert.lab.xyz = function (lab) {
|
||
var l = lab[0];
|
||
var a = lab[1];
|
||
var b = lab[2];
|
||
var x;
|
||
var y;
|
||
var z;
|
||
|
||
y = (l + 16) / 116;
|
||
x = a / 500 + y;
|
||
z = y - b / 200;
|
||
|
||
var y2 = Math.pow(y, 3);
|
||
var x2 = Math.pow(x, 3);
|
||
var z2 = Math.pow(z, 3);
|
||
y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
|
||
x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
|
||
z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
|
||
|
||
x *= 95.047;
|
||
y *= 100;
|
||
z *= 108.883;
|
||
|
||
return [x, y, z];
|
||
};
|
||
|
||
convert.lab.lch = function (lab) {
|
||
var l = lab[0];
|
||
var a = lab[1];
|
||
var b = lab[2];
|
||
var hr;
|
||
var h;
|
||
var c;
|
||
|
||
hr = Math.atan2(b, a);
|
||
h = hr * 360 / 2 / Math.PI;
|
||
|
||
if (h < 0) {
|
||
h += 360;
|
||
}
|
||
|
||
c = Math.sqrt(a * a + b * b);
|
||
|
||
return [l, c, h];
|
||
};
|
||
|
||
convert.lch.lab = function (lch) {
|
||
var l = lch[0];
|
||
var c = lch[1];
|
||
var h = lch[2];
|
||
var a;
|
||
var b;
|
||
var hr;
|
||
|
||
hr = h / 360 * 2 * Math.PI;
|
||
a = c * Math.cos(hr);
|
||
b = c * Math.sin(hr);
|
||
|
||
return [l, a, b];
|
||
};
|
||
|
||
convert.rgb.ansi16 = function (args) {
|
||
var r = args[0];
|
||
var g = args[1];
|
||
var b = args[2];
|
||
var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization
|
||
|
||
value = Math.round(value / 50);
|
||
|
||
if (value === 0) {
|
||
return 30;
|
||
}
|
||
|
||
var ansi = 30
|
||
+ ((Math.round(b / 255) << 2)
|
||
| (Math.round(g / 255) << 1)
|
||
| Math.round(r / 255));
|
||
|
||
if (value === 2) {
|
||
ansi += 60;
|
||
}
|
||
|
||
return ansi;
|
||
};
|
||
|
||
convert.hsv.ansi16 = function (args) {
|
||
// optimization here; we already know the value and don't need to get
|
||
// it converted for us.
|
||
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
|
||
};
|
||
|
||
convert.rgb.ansi256 = function (args) {
|
||
var r = args[0];
|
||
var g = args[1];
|
||
var b = args[2];
|
||
|
||
// we use the extended greyscale palette here, with the exception of
|
||
// black and white. normal palette only has 4 greyscale shades.
|
||
if (r === g && g === b) {
|
||
if (r < 8) {
|
||
return 16;
|
||
}
|
||
|
||
if (r > 248) {
|
||
return 231;
|
||
}
|
||
|
||
return Math.round(((r - 8) / 247) * 24) + 232;
|
||
}
|
||
|
||
var ansi = 16
|
||
+ (36 * Math.round(r / 255 * 5))
|
||
+ (6 * Math.round(g / 255 * 5))
|
||
+ Math.round(b / 255 * 5);
|
||
|
||
return ansi;
|
||
};
|
||
|
||
convert.ansi16.rgb = function (args) {
|
||
var color = args % 10;
|
||
|
||
// handle greyscale
|
||
if (color === 0 || color === 7) {
|
||
if (args > 50) {
|
||
color += 3.5;
|
||
}
|
||
|
||
color = color / 10.5 * 255;
|
||
|
||
return [color, color, color];
|
||
}
|
||
|
||
var mult = (~~(args > 50) + 1) * 0.5;
|
||
var r = ((color & 1) * mult) * 255;
|
||
var g = (((color >> 1) & 1) * mult) * 255;
|
||
var b = (((color >> 2) & 1) * mult) * 255;
|
||
|
||
return [r, g, b];
|
||
};
|
||
|
||
convert.ansi256.rgb = function (args) {
|
||
// handle greyscale
|
||
if (args >= 232) {
|
||
var c = (args - 232) * 10 + 8;
|
||
return [c, c, c];
|
||
}
|
||
|
||
args -= 16;
|
||
|
||
var rem;
|
||
var r = Math.floor(args / 36) / 5 * 255;
|
||
var g = Math.floor((rem = args % 36) / 6) / 5 * 255;
|
||
var b = (rem % 6) / 5 * 255;
|
||
|
||
return [r, g, b];
|
||
};
|
||
|
||
convert.rgb.hex = function (args) {
|
||
var integer = ((Math.round(args[0]) & 0xFF) << 16)
|
||
+ ((Math.round(args[1]) & 0xFF) << 8)
|
||
+ (Math.round(args[2]) & 0xFF);
|
||
|
||
var string = integer.toString(16).toUpperCase();
|
||
return '000000'.substring(string.length) + string;
|
||
};
|
||
|
||
convert.hex.rgb = function (args) {
|
||
var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
|
||
if (!match) {
|
||
return [0, 0, 0];
|
||
}
|
||
|
||
var colorString = match[0];
|
||
|
||
if (match[0].length === 3) {
|
||
colorString = colorString.split('').map(function (char) {
|
||
return char + char;
|
||
}).join('');
|
||
}
|
||
|
||
var integer = parseInt(colorString, 16);
|
||
var r = (integer >> 16) & 0xFF;
|
||
var g = (integer >> 8) & 0xFF;
|
||
var b = integer & 0xFF;
|
||
|
||
return [r, g, b];
|
||
};
|
||
|
||
convert.rgb.hcg = function (rgb) {
|
||
var r = rgb[0] / 255;
|
||
var g = rgb[1] / 255;
|
||
var b = rgb[2] / 255;
|
||
var max = Math.max(Math.max(r, g), b);
|
||
var min = Math.min(Math.min(r, g), b);
|
||
var chroma = (max - min);
|
||
var grayscale;
|
||
var hue;
|
||
|
||
if (chroma < 1) {
|
||
grayscale = min / (1 - chroma);
|
||
} else {
|
||
grayscale = 0;
|
||
}
|
||
|
||
if (chroma <= 0) {
|
||
hue = 0;
|
||
} else
|
||
if (max === r) {
|
||
hue = ((g - b) / chroma) % 6;
|
||
} else
|
||
if (max === g) {
|
||
hue = 2 + (b - r) / chroma;
|
||
} else {
|
||
hue = 4 + (r - g) / chroma + 4;
|
||
}
|
||
|
||
hue /= 6;
|
||
hue %= 1;
|
||
|
||
return [hue * 360, chroma * 100, grayscale * 100];
|
||
};
|
||
|
||
convert.hsl.hcg = function (hsl) {
|
||
var s = hsl[1] / 100;
|
||
var l = hsl[2] / 100;
|
||
var c = 1;
|
||
var f = 0;
|
||
|
||
if (l < 0.5) {
|
||
c = 2.0 * s * l;
|
||
} else {
|
||
c = 2.0 * s * (1.0 - l);
|
||
}
|
||
|
||
if (c < 1.0) {
|
||
f = (l - 0.5 * c) / (1.0 - c);
|
||
}
|
||
|
||
return [hsl[0], c * 100, f * 100];
|
||
};
|
||
|
||
convert.hsv.hcg = function (hsv) {
|
||
var s = hsv[1] / 100;
|
||
var v = hsv[2] / 100;
|
||
|
||
var c = s * v;
|
||
var f = 0;
|
||
|
||
if (c < 1.0) {
|
||
f = (v - c) / (1 - c);
|
||
}
|
||
|
||
return [hsv[0], c * 100, f * 100];
|
||
};
|
||
|
||
convert.hcg.rgb = function (hcg) {
|
||
var h = hcg[0] / 360;
|
||
var c = hcg[1] / 100;
|
||
var g = hcg[2] / 100;
|
||
|
||
if (c === 0.0) {
|
||
return [g * 255, g * 255, g * 255];
|
||
}
|
||
|
||
var pure = [0, 0, 0];
|
||
var hi = (h % 1) * 6;
|
||
var v = hi % 1;
|
||
var w = 1 - v;
|
||
var mg = 0;
|
||
|
||
switch (Math.floor(hi)) {
|
||
case 0:
|
||
pure[0] = 1; pure[1] = v; pure[2] = 0; break;
|
||
case 1:
|
||
pure[0] = w; pure[1] = 1; pure[2] = 0; break;
|
||
case 2:
|
||
pure[0] = 0; pure[1] = 1; pure[2] = v; break;
|
||
case 3:
|
||
pure[0] = 0; pure[1] = w; pure[2] = 1; break;
|
||
case 4:
|
||
pure[0] = v; pure[1] = 0; pure[2] = 1; break;
|
||
default:
|
||
pure[0] = 1; pure[1] = 0; pure[2] = w;
|
||
}
|
||
|
||
mg = (1.0 - c) * g;
|
||
|
||
return [
|
||
(c * pure[0] + mg) * 255,
|
||
(c * pure[1] + mg) * 255,
|
||
(c * pure[2] + mg) * 255
|
||
];
|
||
};
|
||
|
||
convert.hcg.hsv = function (hcg) {
|
||
var c = hcg[1] / 100;
|
||
var g = hcg[2] / 100;
|
||
|
||
var v = c + g * (1.0 - c);
|
||
var f = 0;
|
||
|
||
if (v > 0.0) {
|
||
f = c / v;
|
||
}
|
||
|
||
return [hcg[0], f * 100, v * 100];
|
||
};
|
||
|
||
convert.hcg.hsl = function (hcg) {
|
||
var c = hcg[1] / 100;
|
||
var g = hcg[2] / 100;
|
||
|
||
var l = g * (1.0 - c) + 0.5 * c;
|
||
var s = 0;
|
||
|
||
if (l > 0.0 && l < 0.5) {
|
||
s = c / (2 * l);
|
||
} else
|
||
if (l >= 0.5 && l < 1.0) {
|
||
s = c / (2 * (1 - l));
|
||
}
|
||
|
||
return [hcg[0], s * 100, l * 100];
|
||
};
|
||
|
||
convert.hcg.hwb = function (hcg) {
|
||
var c = hcg[1] / 100;
|
||
var g = hcg[2] / 100;
|
||
var v = c + g * (1.0 - c);
|
||
return [hcg[0], (v - c) * 100, (1 - v) * 100];
|
||
};
|
||
|
||
convert.hwb.hcg = function (hwb) {
|
||
var w = hwb[1] / 100;
|
||
var b = hwb[2] / 100;
|
||
var v = 1 - b;
|
||
var c = v - w;
|
||
var g = 0;
|
||
|
||
if (c < 1) {
|
||
g = (v - c) / (1 - c);
|
||
}
|
||
|
||
return [hwb[0], c * 100, g * 100];
|
||
};
|
||
|
||
convert.apple.rgb = function (apple) {
|
||
return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
|
||
};
|
||
|
||
convert.rgb.apple = function (rgb) {
|
||
return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
|
||
};
|
||
|
||
convert.gray.rgb = function (args) {
|
||
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
|
||
};
|
||
|
||
convert.gray.hsl = convert.gray.hsv = function (args) {
|
||
return [0, 0, args[0]];
|
||
};
|
||
|
||
convert.gray.hwb = function (gray) {
|
||
return [0, 100, gray[0]];
|
||
};
|
||
|
||
convert.gray.cmyk = function (gray) {
|
||
return [0, 0, 0, gray[0]];
|
||
};
|
||
|
||
convert.gray.lab = function (gray) {
|
||
return [gray[0], 0, 0];
|
||
};
|
||
|
||
convert.gray.hex = function (gray) {
|
||
var val = Math.round(gray[0] / 100 * 255) & 0xFF;
|
||
var integer = (val << 16) + (val << 8) + val;
|
||
|
||
var string = integer.toString(16).toUpperCase();
|
||
return '000000'.substring(string.length) + string;
|
||
};
|
||
|
||
convert.rgb.gray = function (rgb) {
|
||
var val = (rgb[0] + rgb[1] + rgb[2]) / 3;
|
||
return [val / 255 * 100];
|
||
};
|
||
});
|
||
var conversions_1 = conversions.rgb;
|
||
var conversions_2 = conversions.hsl;
|
||
var conversions_3 = conversions.hsv;
|
||
var conversions_4 = conversions.hwb;
|
||
var conversions_5 = conversions.cmyk;
|
||
var conversions_6 = conversions.xyz;
|
||
var conversions_7 = conversions.lab;
|
||
var conversions_8 = conversions.lch;
|
||
var conversions_9 = conversions.hex;
|
||
var conversions_10 = conversions.keyword;
|
||
var conversions_11 = conversions.ansi16;
|
||
var conversions_12 = conversions.ansi256;
|
||
var conversions_13 = conversions.hcg;
|
||
var conversions_14 = conversions.apple;
|
||
var conversions_15 = conversions.gray;
|
||
|
||
/*
|
||
this function routes a model to all other models.
|
||
|
||
all functions that are routed have a property `.conversion` attached
|
||
to the returned synthetic function. This property is an array
|
||
of strings, each with the steps in between the 'from' and 'to'
|
||
color models (inclusive).
|
||
|
||
conversions that are not possible simply are not included.
|
||
*/
|
||
|
||
function buildGraph() {
|
||
var graph = {};
|
||
// https://jsperf.com/object-keys-vs-for-in-with-closure/3
|
||
var models = Object.keys(conversions);
|
||
|
||
for (var len = models.length, i = 0; i < len; i++) {
|
||
graph[models[i]] = {
|
||
// http://jsperf.com/1-vs-infinity
|
||
// micro-opt, but this is simple.
|
||
distance: -1,
|
||
parent: null
|
||
};
|
||
}
|
||
|
||
return graph;
|
||
}
|
||
|
||
// https://en.wikipedia.org/wiki/Breadth-first_search
|
||
function deriveBFS(fromModel) {
|
||
var graph = buildGraph();
|
||
var queue = [fromModel]; // unshift -> queue -> pop
|
||
|
||
graph[fromModel].distance = 0;
|
||
|
||
while (queue.length) {
|
||
var current = queue.pop();
|
||
var adjacents = Object.keys(conversions[current]);
|
||
|
||
for (var len = adjacents.length, i = 0; i < len; i++) {
|
||
var adjacent = adjacents[i];
|
||
var node = graph[adjacent];
|
||
|
||
if (node.distance === -1) {
|
||
node.distance = graph[current].distance + 1;
|
||
node.parent = current;
|
||
queue.unshift(adjacent);
|
||
}
|
||
}
|
||
}
|
||
|
||
return graph;
|
||
}
|
||
|
||
function link(from, to) {
|
||
return function (args) {
|
||
return to(from(args));
|
||
};
|
||
}
|
||
|
||
function wrapConversion(toModel, graph) {
|
||
var path = [graph[toModel].parent, toModel];
|
||
var fn = conversions[graph[toModel].parent][toModel];
|
||
|
||
var cur = graph[toModel].parent;
|
||
while (graph[cur].parent) {
|
||
path.unshift(graph[cur].parent);
|
||
fn = link(conversions[graph[cur].parent][cur], fn);
|
||
cur = graph[cur].parent;
|
||
}
|
||
|
||
fn.conversion = path;
|
||
return fn;
|
||
}
|
||
|
||
var route = function (fromModel) {
|
||
var graph = deriveBFS(fromModel);
|
||
var conversion = {};
|
||
|
||
var models = Object.keys(graph);
|
||
for (var len = models.length, i = 0; i < len; i++) {
|
||
var toModel = models[i];
|
||
var node = graph[toModel];
|
||
|
||
if (node.parent === null) {
|
||
// no possible conversion, or this node is the source model.
|
||
continue;
|
||
}
|
||
|
||
conversion[toModel] = wrapConversion(toModel, graph);
|
||
}
|
||
|
||
return conversion;
|
||
};
|
||
|
||
var convert = {};
|
||
|
||
var models = Object.keys(conversions);
|
||
|
||
function wrapRaw(fn) {
|
||
var wrappedFn = function (args) {
|
||
if (args === undefined || args === null) {
|
||
return args;
|
||
}
|
||
|
||
if (arguments.length > 1) {
|
||
args = Array.prototype.slice.call(arguments);
|
||
}
|
||
|
||
return fn(args);
|
||
};
|
||
|
||
// preserve .conversion property if there is one
|
||
if ('conversion' in fn) {
|
||
wrappedFn.conversion = fn.conversion;
|
||
}
|
||
|
||
return wrappedFn;
|
||
}
|
||
|
||
function wrapRounded(fn) {
|
||
var wrappedFn = function (args) {
|
||
if (args === undefined || args === null) {
|
||
return args;
|
||
}
|
||
|
||
if (arguments.length > 1) {
|
||
args = Array.prototype.slice.call(arguments);
|
||
}
|
||
|
||
var result = fn(args);
|
||
|
||
// we're assuming the result is an array here.
|
||
// see notice in conversions.js; don't use box types
|
||
// in conversion functions.
|
||
if (typeof result === 'object') {
|
||
for (var len = result.length, i = 0; i < len; i++) {
|
||
result[i] = Math.round(result[i]);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
// preserve .conversion property if there is one
|
||
if ('conversion' in fn) {
|
||
wrappedFn.conversion = fn.conversion;
|
||
}
|
||
|
||
return wrappedFn;
|
||
}
|
||
|
||
models.forEach(function (fromModel) {
|
||
convert[fromModel] = {};
|
||
|
||
Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
|
||
Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
|
||
|
||
var routes = route(fromModel);
|
||
var routeModels = Object.keys(routes);
|
||
|
||
routeModels.forEach(function (toModel) {
|
||
var fn = routes[toModel];
|
||
|
||
convert[fromModel][toModel] = wrapRounded(fn);
|
||
convert[fromModel][toModel].raw = wrapRaw(fn);
|
||
});
|
||
});
|
||
|
||
var colorConvert = convert;
|
||
|
||
var colorName$1 = {
|
||
"aliceblue": [240, 248, 255],
|
||
"antiquewhite": [250, 235, 215],
|
||
"aqua": [0, 255, 255],
|
||
"aquamarine": [127, 255, 212],
|
||
"azure": [240, 255, 255],
|
||
"beige": [245, 245, 220],
|
||
"bisque": [255, 228, 196],
|
||
"black": [0, 0, 0],
|
||
"blanchedalmond": [255, 235, 205],
|
||
"blue": [0, 0, 255],
|
||
"blueviolet": [138, 43, 226],
|
||
"brown": [165, 42, 42],
|
||
"burlywood": [222, 184, 135],
|
||
"cadetblue": [95, 158, 160],
|
||
"chartreuse": [127, 255, 0],
|
||
"chocolate": [210, 105, 30],
|
||
"coral": [255, 127, 80],
|
||
"cornflowerblue": [100, 149, 237],
|
||
"cornsilk": [255, 248, 220],
|
||
"crimson": [220, 20, 60],
|
||
"cyan": [0, 255, 255],
|
||
"darkblue": [0, 0, 139],
|
||
"darkcyan": [0, 139, 139],
|
||
"darkgoldenrod": [184, 134, 11],
|
||
"darkgray": [169, 169, 169],
|
||
"darkgreen": [0, 100, 0],
|
||
"darkgrey": [169, 169, 169],
|
||
"darkkhaki": [189, 183, 107],
|
||
"darkmagenta": [139, 0, 139],
|
||
"darkolivegreen": [85, 107, 47],
|
||
"darkorange": [255, 140, 0],
|
||
"darkorchid": [153, 50, 204],
|
||
"darkred": [139, 0, 0],
|
||
"darksalmon": [233, 150, 122],
|
||
"darkseagreen": [143, 188, 143],
|
||
"darkslateblue": [72, 61, 139],
|
||
"darkslategray": [47, 79, 79],
|
||
"darkslategrey": [47, 79, 79],
|
||
"darkturquoise": [0, 206, 209],
|
||
"darkviolet": [148, 0, 211],
|
||
"deeppink": [255, 20, 147],
|
||
"deepskyblue": [0, 191, 255],
|
||
"dimgray": [105, 105, 105],
|
||
"dimgrey": [105, 105, 105],
|
||
"dodgerblue": [30, 144, 255],
|
||
"firebrick": [178, 34, 34],
|
||
"floralwhite": [255, 250, 240],
|
||
"forestgreen": [34, 139, 34],
|
||
"fuchsia": [255, 0, 255],
|
||
"gainsboro": [220, 220, 220],
|
||
"ghostwhite": [248, 248, 255],
|
||
"gold": [255, 215, 0],
|
||
"goldenrod": [218, 165, 32],
|
||
"gray": [128, 128, 128],
|
||
"green": [0, 128, 0],
|
||
"greenyellow": [173, 255, 47],
|
||
"grey": [128, 128, 128],
|
||
"honeydew": [240, 255, 240],
|
||
"hotpink": [255, 105, 180],
|
||
"indianred": [205, 92, 92],
|
||
"indigo": [75, 0, 130],
|
||
"ivory": [255, 255, 240],
|
||
"khaki": [240, 230, 140],
|
||
"lavender": [230, 230, 250],
|
||
"lavenderblush": [255, 240, 245],
|
||
"lawngreen": [124, 252, 0],
|
||
"lemonchiffon": [255, 250, 205],
|
||
"lightblue": [173, 216, 230],
|
||
"lightcoral": [240, 128, 128],
|
||
"lightcyan": [224, 255, 255],
|
||
"lightgoldenrodyellow": [250, 250, 210],
|
||
"lightgray": [211, 211, 211],
|
||
"lightgreen": [144, 238, 144],
|
||
"lightgrey": [211, 211, 211],
|
||
"lightpink": [255, 182, 193],
|
||
"lightsalmon": [255, 160, 122],
|
||
"lightseagreen": [32, 178, 170],
|
||
"lightskyblue": [135, 206, 250],
|
||
"lightslategray": [119, 136, 153],
|
||
"lightslategrey": [119, 136, 153],
|
||
"lightsteelblue": [176, 196, 222],
|
||
"lightyellow": [255, 255, 224],
|
||
"lime": [0, 255, 0],
|
||
"limegreen": [50, 205, 50],
|
||
"linen": [250, 240, 230],
|
||
"magenta": [255, 0, 255],
|
||
"maroon": [128, 0, 0],
|
||
"mediumaquamarine": [102, 205, 170],
|
||
"mediumblue": [0, 0, 205],
|
||
"mediumorchid": [186, 85, 211],
|
||
"mediumpurple": [147, 112, 219],
|
||
"mediumseagreen": [60, 179, 113],
|
||
"mediumslateblue": [123, 104, 238],
|
||
"mediumspringgreen": [0, 250, 154],
|
||
"mediumturquoise": [72, 209, 204],
|
||
"mediumvioletred": [199, 21, 133],
|
||
"midnightblue": [25, 25, 112],
|
||
"mintcream": [245, 255, 250],
|
||
"mistyrose": [255, 228, 225],
|
||
"moccasin": [255, 228, 181],
|
||
"navajowhite": [255, 222, 173],
|
||
"navy": [0, 0, 128],
|
||
"oldlace": [253, 245, 230],
|
||
"olive": [128, 128, 0],
|
||
"olivedrab": [107, 142, 35],
|
||
"orange": [255, 165, 0],
|
||
"orangered": [255, 69, 0],
|
||
"orchid": [218, 112, 214],
|
||
"palegoldenrod": [238, 232, 170],
|
||
"palegreen": [152, 251, 152],
|
||
"paleturquoise": [175, 238, 238],
|
||
"palevioletred": [219, 112, 147],
|
||
"papayawhip": [255, 239, 213],
|
||
"peachpuff": [255, 218, 185],
|
||
"peru": [205, 133, 63],
|
||
"pink": [255, 192, 203],
|
||
"plum": [221, 160, 221],
|
||
"powderblue": [176, 224, 230],
|
||
"purple": [128, 0, 128],
|
||
"rebeccapurple": [102, 51, 153],
|
||
"red": [255, 0, 0],
|
||
"rosybrown": [188, 143, 143],
|
||
"royalblue": [65, 105, 225],
|
||
"saddlebrown": [139, 69, 19],
|
||
"salmon": [250, 128, 114],
|
||
"sandybrown": [244, 164, 96],
|
||
"seagreen": [46, 139, 87],
|
||
"seashell": [255, 245, 238],
|
||
"sienna": [160, 82, 45],
|
||
"silver": [192, 192, 192],
|
||
"skyblue": [135, 206, 235],
|
||
"slateblue": [106, 90, 205],
|
||
"slategray": [112, 128, 144],
|
||
"slategrey": [112, 128, 144],
|
||
"snow": [255, 250, 250],
|
||
"springgreen": [0, 255, 127],
|
||
"steelblue": [70, 130, 180],
|
||
"tan": [210, 180, 140],
|
||
"teal": [0, 128, 128],
|
||
"thistle": [216, 191, 216],
|
||
"tomato": [255, 99, 71],
|
||
"turquoise": [64, 224, 208],
|
||
"violet": [238, 130, 238],
|
||
"wheat": [245, 222, 179],
|
||
"white": [255, 255, 255],
|
||
"whitesmoke": [245, 245, 245],
|
||
"yellow": [255, 255, 0],
|
||
"yellowgreen": [154, 205, 50]
|
||
};
|
||
|
||
/* MIT license */
|
||
|
||
|
||
var colorString = {
|
||
getRgba: getRgba,
|
||
getHsla: getHsla,
|
||
getRgb: getRgb,
|
||
getHsl: getHsl,
|
||
getHwb: getHwb,
|
||
getAlpha: getAlpha,
|
||
|
||
hexString: hexString,
|
||
rgbString: rgbString,
|
||
rgbaString: rgbaString,
|
||
percentString: percentString,
|
||
percentaString: percentaString,
|
||
hslString: hslString,
|
||
hslaString: hslaString,
|
||
hwbString: hwbString,
|
||
keyword: keyword
|
||
};
|
||
|
||
function getRgba(string) {
|
||
if (!string) {
|
||
return;
|
||
}
|
||
var abbr = /^#([a-fA-F0-9]{3,4})$/i,
|
||
hex = /^#([a-fA-F0-9]{6}([a-fA-F0-9]{2})?)$/i,
|
||
rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
|
||
per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/i,
|
||
keyword = /(\w+)/;
|
||
|
||
var rgb = [0, 0, 0],
|
||
a = 1,
|
||
match = string.match(abbr),
|
||
hexAlpha = "";
|
||
if (match) {
|
||
match = match[1];
|
||
hexAlpha = match[3];
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
rgb[i] = parseInt(match[i] + match[i], 16);
|
||
}
|
||
if (hexAlpha) {
|
||
a = Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
|
||
}
|
||
}
|
||
else if (match = string.match(hex)) {
|
||
hexAlpha = match[2];
|
||
match = match[1];
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
|
||
}
|
||
if (hexAlpha) {
|
||
a = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
|
||
}
|
||
}
|
||
else if (match = string.match(rgba)) {
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
rgb[i] = parseInt(match[i + 1]);
|
||
}
|
||
a = parseFloat(match[4]);
|
||
}
|
||
else if (match = string.match(per)) {
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
|
||
}
|
||
a = parseFloat(match[4]);
|
||
}
|
||
else if (match = string.match(keyword)) {
|
||
if (match[1] == "transparent") {
|
||
return [0, 0, 0, 0];
|
||
}
|
||
rgb = colorName$1[match[1]];
|
||
if (!rgb) {
|
||
return;
|
||
}
|
||
}
|
||
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
rgb[i] = scale(rgb[i], 0, 255);
|
||
}
|
||
if (!a && a != 0) {
|
||
a = 1;
|
||
}
|
||
else {
|
||
a = scale(a, 0, 1);
|
||
}
|
||
rgb[3] = a;
|
||
return rgb;
|
||
}
|
||
|
||
function getHsla(string) {
|
||
if (!string) {
|
||
return;
|
||
}
|
||
var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
||
var match = string.match(hsl);
|
||
if (match) {
|
||
var alpha = parseFloat(match[4]);
|
||
var h = scale(parseInt(match[1]), 0, 360),
|
||
s = scale(parseFloat(match[2]), 0, 100),
|
||
l = scale(parseFloat(match[3]), 0, 100),
|
||
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
||
return [h, s, l, a];
|
||
}
|
||
}
|
||
|
||
function getHwb(string) {
|
||
if (!string) {
|
||
return;
|
||
}
|
||
var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
||
var match = string.match(hwb);
|
||
if (match) {
|
||
var alpha = parseFloat(match[4]);
|
||
var h = scale(parseInt(match[1]), 0, 360),
|
||
w = scale(parseFloat(match[2]), 0, 100),
|
||
b = scale(parseFloat(match[3]), 0, 100),
|
||
a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
||
return [h, w, b, a];
|
||
}
|
||
}
|
||
|
||
function getRgb(string) {
|
||
var rgba = getRgba(string);
|
||
return rgba && rgba.slice(0, 3);
|
||
}
|
||
|
||
function getHsl(string) {
|
||
var hsla = getHsla(string);
|
||
return hsla && hsla.slice(0, 3);
|
||
}
|
||
|
||
function getAlpha(string) {
|
||
var vals = getRgba(string);
|
||
if (vals) {
|
||
return vals[3];
|
||
}
|
||
else if (vals = getHsla(string)) {
|
||
return vals[3];
|
||
}
|
||
else if (vals = getHwb(string)) {
|
||
return vals[3];
|
||
}
|
||
}
|
||
|
||
// generators
|
||
function hexString(rgba, a) {
|
||
var a = (a !== undefined && rgba.length === 3) ? a : rgba[3];
|
||
return "#" + hexDouble(rgba[0])
|
||
+ hexDouble(rgba[1])
|
||
+ hexDouble(rgba[2])
|
||
+ (
|
||
(a >= 0 && a < 1)
|
||
? hexDouble(Math.round(a * 255))
|
||
: ""
|
||
);
|
||
}
|
||
|
||
function rgbString(rgba, alpha) {
|
||
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
||
return rgbaString(rgba, alpha);
|
||
}
|
||
return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
|
||
}
|
||
|
||
function rgbaString(rgba, alpha) {
|
||
if (alpha === undefined) {
|
||
alpha = (rgba[3] !== undefined ? rgba[3] : 1);
|
||
}
|
||
return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
|
||
+ ", " + alpha + ")";
|
||
}
|
||
|
||
function percentString(rgba, alpha) {
|
||
if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
||
return percentaString(rgba, alpha);
|
||
}
|
||
var r = Math.round(rgba[0]/255 * 100),
|
||
g = Math.round(rgba[1]/255 * 100),
|
||
b = Math.round(rgba[2]/255 * 100);
|
||
|
||
return "rgb(" + r + "%, " + g + "%, " + b + "%)";
|
||
}
|
||
|
||
function percentaString(rgba, alpha) {
|
||
var r = Math.round(rgba[0]/255 * 100),
|
||
g = Math.round(rgba[1]/255 * 100),
|
||
b = Math.round(rgba[2]/255 * 100);
|
||
return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
|
||
}
|
||
|
||
function hslString(hsla, alpha) {
|
||
if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
|
||
return hslaString(hsla, alpha);
|
||
}
|
||
return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
|
||
}
|
||
|
||
function hslaString(hsla, alpha) {
|
||
if (alpha === undefined) {
|
||
alpha = (hsla[3] !== undefined ? hsla[3] : 1);
|
||
}
|
||
return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
|
||
+ alpha + ")";
|
||
}
|
||
|
||
// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
|
||
// (hwb have alpha optional & 1 is default value)
|
||
function hwbString(hwb, alpha) {
|
||
if (alpha === undefined) {
|
||
alpha = (hwb[3] !== undefined ? hwb[3] : 1);
|
||
}
|
||
return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
|
||
+ (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
|
||
}
|
||
|
||
function keyword(rgb) {
|
||
return reverseNames[rgb.slice(0, 3)];
|
||
}
|
||
|
||
// helpers
|
||
function scale(num, min, max) {
|
||
return Math.min(Math.max(min, num), max);
|
||
}
|
||
|
||
function hexDouble(num) {
|
||
var str = num.toString(16).toUpperCase();
|
||
return (str.length < 2) ? "0" + str : str;
|
||
}
|
||
|
||
|
||
//create a list of reverse color names
|
||
var reverseNames = {};
|
||
for (var name in colorName$1) {
|
||
reverseNames[colorName$1[name]] = name;
|
||
}
|
||
|
||
/* MIT license */
|
||
|
||
|
||
|
||
var Color = function (obj) {
|
||
if (obj instanceof Color) {
|
||
return obj;
|
||
}
|
||
if (!(this instanceof Color)) {
|
||
return new Color(obj);
|
||
}
|
||
|
||
this.valid = false;
|
||
this.values = {
|
||
rgb: [0, 0, 0],
|
||
hsl: [0, 0, 0],
|
||
hsv: [0, 0, 0],
|
||
hwb: [0, 0, 0],
|
||
cmyk: [0, 0, 0, 0],
|
||
alpha: 1
|
||
};
|
||
|
||
// parse Color() argument
|
||
var vals;
|
||
if (typeof obj === 'string') {
|
||
vals = colorString.getRgba(obj);
|
||
if (vals) {
|
||
this.setValues('rgb', vals);
|
||
} else if (vals = colorString.getHsla(obj)) {
|
||
this.setValues('hsl', vals);
|
||
} else if (vals = colorString.getHwb(obj)) {
|
||
this.setValues('hwb', vals);
|
||
}
|
||
} else if (typeof obj === 'object') {
|
||
vals = obj;
|
||
if (vals.r !== undefined || vals.red !== undefined) {
|
||
this.setValues('rgb', vals);
|
||
} else if (vals.l !== undefined || vals.lightness !== undefined) {
|
||
this.setValues('hsl', vals);
|
||
} else if (vals.v !== undefined || vals.value !== undefined) {
|
||
this.setValues('hsv', vals);
|
||
} else if (vals.w !== undefined || vals.whiteness !== undefined) {
|
||
this.setValues('hwb', vals);
|
||
} else if (vals.c !== undefined || vals.cyan !== undefined) {
|
||
this.setValues('cmyk', vals);
|
||
}
|
||
}
|
||
};
|
||
|
||
Color.prototype = {
|
||
isValid: function () {
|
||
return this.valid;
|
||
},
|
||
rgb: function () {
|
||
return this.setSpace('rgb', arguments);
|
||
},
|
||
hsl: function () {
|
||
return this.setSpace('hsl', arguments);
|
||
},
|
||
hsv: function () {
|
||
return this.setSpace('hsv', arguments);
|
||
},
|
||
hwb: function () {
|
||
return this.setSpace('hwb', arguments);
|
||
},
|
||
cmyk: function () {
|
||
return this.setSpace('cmyk', arguments);
|
||
},
|
||
|
||
rgbArray: function () {
|
||
return this.values.rgb;
|
||
},
|
||
hslArray: function () {
|
||
return this.values.hsl;
|
||
},
|
||
hsvArray: function () {
|
||
return this.values.hsv;
|
||
},
|
||
hwbArray: function () {
|
||
var values = this.values;
|
||
if (values.alpha !== 1) {
|
||
return values.hwb.concat([values.alpha]);
|
||
}
|
||
return values.hwb;
|
||
},
|
||
cmykArray: function () {
|
||
return this.values.cmyk;
|
||
},
|
||
rgbaArray: function () {
|
||
var values = this.values;
|
||
return values.rgb.concat([values.alpha]);
|
||
},
|
||
hslaArray: function () {
|
||
var values = this.values;
|
||
return values.hsl.concat([values.alpha]);
|
||
},
|
||
alpha: function (val) {
|
||
if (val === undefined) {
|
||
return this.values.alpha;
|
||
}
|
||
this.setValues('alpha', val);
|
||
return this;
|
||
},
|
||
|
||
red: function (val) {
|
||
return this.setChannel('rgb', 0, val);
|
||
},
|
||
green: function (val) {
|
||
return this.setChannel('rgb', 1, val);
|
||
},
|
||
blue: function (val) {
|
||
return this.setChannel('rgb', 2, val);
|
||
},
|
||
hue: function (val) {
|
||
if (val) {
|
||
val %= 360;
|
||
val = val < 0 ? 360 + val : val;
|
||
}
|
||
return this.setChannel('hsl', 0, val);
|
||
},
|
||
saturation: function (val) {
|
||
return this.setChannel('hsl', 1, val);
|
||
},
|
||
lightness: function (val) {
|
||
return this.setChannel('hsl', 2, val);
|
||
},
|
||
saturationv: function (val) {
|
||
return this.setChannel('hsv', 1, val);
|
||
},
|
||
whiteness: function (val) {
|
||
return this.setChannel('hwb', 1, val);
|
||
},
|
||
blackness: function (val) {
|
||
return this.setChannel('hwb', 2, val);
|
||
},
|
||
value: function (val) {
|
||
return this.setChannel('hsv', 2, val);
|
||
},
|
||
cyan: function (val) {
|
||
return this.setChannel('cmyk', 0, val);
|
||
},
|
||
magenta: function (val) {
|
||
return this.setChannel('cmyk', 1, val);
|
||
},
|
||
yellow: function (val) {
|
||
return this.setChannel('cmyk', 2, val);
|
||
},
|
||
black: function (val) {
|
||
return this.setChannel('cmyk', 3, val);
|
||
},
|
||
|
||
hexString: function () {
|
||
return colorString.hexString(this.values.rgb);
|
||
},
|
||
rgbString: function () {
|
||
return colorString.rgbString(this.values.rgb, this.values.alpha);
|
||
},
|
||
rgbaString: function () {
|
||
return colorString.rgbaString(this.values.rgb, this.values.alpha);
|
||
},
|
||
percentString: function () {
|
||
return colorString.percentString(this.values.rgb, this.values.alpha);
|
||
},
|
||
hslString: function () {
|
||
return colorString.hslString(this.values.hsl, this.values.alpha);
|
||
},
|
||
hslaString: function () {
|
||
return colorString.hslaString(this.values.hsl, this.values.alpha);
|
||
},
|
||
hwbString: function () {
|
||
return colorString.hwbString(this.values.hwb, this.values.alpha);
|
||
},
|
||
keyword: function () {
|
||
return colorString.keyword(this.values.rgb, this.values.alpha);
|
||
},
|
||
|
||
rgbNumber: function () {
|
||
var rgb = this.values.rgb;
|
||
return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
|
||
},
|
||
|
||
luminosity: function () {
|
||
// http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||
var rgb = this.values.rgb;
|
||
var lum = [];
|
||
for (var i = 0; i < rgb.length; i++) {
|
||
var chan = rgb[i] / 255;
|
||
lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
|
||
}
|
||
return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
|
||
},
|
||
|
||
contrast: function (color2) {
|
||
// http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
||
var lum1 = this.luminosity();
|
||
var lum2 = color2.luminosity();
|
||
if (lum1 > lum2) {
|
||
return (lum1 + 0.05) / (lum2 + 0.05);
|
||
}
|
||
return (lum2 + 0.05) / (lum1 + 0.05);
|
||
},
|
||
|
||
level: function (color2) {
|
||
var contrastRatio = this.contrast(color2);
|
||
if (contrastRatio >= 7.1) {
|
||
return 'AAA';
|
||
}
|
||
|
||
return (contrastRatio >= 4.5) ? 'AA' : '';
|
||
},
|
||
|
||
dark: function () {
|
||
// YIQ equation from http://24ways.org/2010/calculating-color-contrast
|
||
var rgb = this.values.rgb;
|
||
var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
|
||
return yiq < 128;
|
||
},
|
||
|
||
light: function () {
|
||
return !this.dark();
|
||
},
|
||
|
||
negate: function () {
|
||
var rgb = [];
|
||
for (var i = 0; i < 3; i++) {
|
||
rgb[i] = 255 - this.values.rgb[i];
|
||
}
|
||
this.setValues('rgb', rgb);
|
||
return this;
|
||
},
|
||
|
||
lighten: function (ratio) {
|
||
var hsl = this.values.hsl;
|
||
hsl[2] += hsl[2] * ratio;
|
||
this.setValues('hsl', hsl);
|
||
return this;
|
||
},
|
||
|
||
darken: function (ratio) {
|
||
var hsl = this.values.hsl;
|
||
hsl[2] -= hsl[2] * ratio;
|
||
this.setValues('hsl', hsl);
|
||
return this;
|
||
},
|
||
|
||
saturate: function (ratio) {
|
||
var hsl = this.values.hsl;
|
||
hsl[1] += hsl[1] * ratio;
|
||
this.setValues('hsl', hsl);
|
||
return this;
|
||
},
|
||
|
||
desaturate: function (ratio) {
|
||
var hsl = this.values.hsl;
|
||
hsl[1] -= hsl[1] * ratio;
|
||
this.setValues('hsl', hsl);
|
||
return this;
|
||
},
|
||
|
||
whiten: function (ratio) {
|
||
var hwb = this.values.hwb;
|
||
hwb[1] += hwb[1] * ratio;
|
||
this.setValues('hwb', hwb);
|
||
return this;
|
||
},
|
||
|
||
blacken: function (ratio) {
|
||
var hwb = this.values.hwb;
|
||
hwb[2] += hwb[2] * ratio;
|
||
this.setValues('hwb', hwb);
|
||
return this;
|
||
},
|
||
|
||
greyscale: function () {
|
||
var rgb = this.values.rgb;
|
||
// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
||
var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
|
||
this.setValues('rgb', [val, val, val]);
|
||
return this;
|
||
},
|
||
|
||
clearer: function (ratio) {
|
||
var alpha = this.values.alpha;
|
||
this.setValues('alpha', alpha - (alpha * ratio));
|
||
return this;
|
||
},
|
||
|
||
opaquer: function (ratio) {
|
||
var alpha = this.values.alpha;
|
||
this.setValues('alpha', alpha + (alpha * ratio));
|
||
return this;
|
||
},
|
||
|
||
rotate: function (degrees) {
|
||
var hsl = this.values.hsl;
|
||
var hue = (hsl[0] + degrees) % 360;
|
||
hsl[0] = hue < 0 ? 360 + hue : hue;
|
||
this.setValues('hsl', hsl);
|
||
return this;
|
||
},
|
||
|
||
/**
|
||
* Ported from sass implementation in C
|
||
* https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
|
||
*/
|
||
mix: function (mixinColor, weight) {
|
||
var color1 = this;
|
||
var color2 = mixinColor;
|
||
var p = weight === undefined ? 0.5 : weight;
|
||
|
||
var w = 2 * p - 1;
|
||
var a = color1.alpha() - color2.alpha();
|
||
|
||
var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
|
||
var w2 = 1 - w1;
|
||
|
||
return this
|
||
.rgb(
|
||
w1 * color1.red() + w2 * color2.red(),
|
||
w1 * color1.green() + w2 * color2.green(),
|
||
w1 * color1.blue() + w2 * color2.blue()
|
||
)
|
||
.alpha(color1.alpha() * p + color2.alpha() * (1 - p));
|
||
},
|
||
|
||
toJSON: function () {
|
||
return this.rgb();
|
||
},
|
||
|
||
clone: function () {
|
||
// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,
|
||
// making the final build way to big to embed in Chart.js. So let's do it manually,
|
||
// assuming that values to clone are 1 dimension arrays containing only numbers,
|
||
// except 'alpha' which is a number.
|
||
var result = new Color();
|
||
var source = this.values;
|
||
var target = result.values;
|
||
var value, type;
|
||
|
||
for (var prop in source) {
|
||
if (source.hasOwnProperty(prop)) {
|
||
value = source[prop];
|
||
type = ({}).toString.call(value);
|
||
if (type === '[object Array]') {
|
||
target[prop] = value.slice(0);
|
||
} else if (type === '[object Number]') {
|
||
target[prop] = value;
|
||
} else {
|
||
console.error('unexpected color value:', value);
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
};
|
||
|
||
Color.prototype.spaces = {
|
||
rgb: ['red', 'green', 'blue'],
|
||
hsl: ['hue', 'saturation', 'lightness'],
|
||
hsv: ['hue', 'saturation', 'value'],
|
||
hwb: ['hue', 'whiteness', 'blackness'],
|
||
cmyk: ['cyan', 'magenta', 'yellow', 'black']
|
||
};
|
||
|
||
Color.prototype.maxes = {
|
||
rgb: [255, 255, 255],
|
||
hsl: [360, 100, 100],
|
||
hsv: [360, 100, 100],
|
||
hwb: [360, 100, 100],
|
||
cmyk: [100, 100, 100, 100]
|
||
};
|
||
|
||
Color.prototype.getValues = function (space) {
|
||
var values = this.values;
|
||
var vals = {};
|
||
|
||
for (var i = 0; i < space.length; i++) {
|
||
vals[space.charAt(i)] = values[space][i];
|
||
}
|
||
|
||
if (values.alpha !== 1) {
|
||
vals.a = values.alpha;
|
||
}
|
||
|
||
// {r: 255, g: 255, b: 255, a: 0.4}
|
||
return vals;
|
||
};
|
||
|
||
Color.prototype.setValues = function (space, vals) {
|
||
var values = this.values;
|
||
var spaces = this.spaces;
|
||
var maxes = this.maxes;
|
||
var alpha = 1;
|
||
var i;
|
||
|
||
this.valid = true;
|
||
|
||
if (space === 'alpha') {
|
||
alpha = vals;
|
||
} else if (vals.length) {
|
||
// [10, 10, 10]
|
||
values[space] = vals.slice(0, space.length);
|
||
alpha = vals[space.length];
|
||
} else if (vals[space.charAt(0)] !== undefined) {
|
||
// {r: 10, g: 10, b: 10}
|
||
for (i = 0; i < space.length; i++) {
|
||
values[space][i] = vals[space.charAt(i)];
|
||
}
|
||
|
||
alpha = vals.a;
|
||
} else if (vals[spaces[space][0]] !== undefined) {
|
||
// {red: 10, green: 10, blue: 10}
|
||
var chans = spaces[space];
|
||
|
||
for (i = 0; i < space.length; i++) {
|
||
values[space][i] = vals[chans[i]];
|
||
}
|
||
|
||
alpha = vals.alpha;
|
||
}
|
||
|
||
values.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));
|
||
|
||
if (space === 'alpha') {
|
||
return false;
|
||
}
|
||
|
||
var capped;
|
||
|
||
// cap values of the space prior converting all values
|
||
for (i = 0; i < space.length; i++) {
|
||
capped = Math.max(0, Math.min(maxes[space][i], values[space][i]));
|
||
values[space][i] = Math.round(capped);
|
||
}
|
||
|
||
// convert to all the other color spaces
|
||
for (var sname in spaces) {
|
||
if (sname !== space) {
|
||
values[sname] = colorConvert[space][sname](values[space]);
|
||
}
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
Color.prototype.setSpace = function (space, args) {
|
||
var vals = args[0];
|
||
|
||
if (vals === undefined) {
|
||
// color.rgb()
|
||
return this.getValues(space);
|
||
}
|
||
|
||
// color.rgb(10, 10, 10)
|
||
if (typeof vals === 'number') {
|
||
vals = Array.prototype.slice.call(args);
|
||
}
|
||
|
||
this.setValues(space, vals);
|
||
return this;
|
||
};
|
||
|
||
Color.prototype.setChannel = function (space, index, val) {
|
||
var svalues = this.values[space];
|
||
if (val === undefined) {
|
||
// color.red()
|
||
return svalues[index];
|
||
} else if (val === svalues[index]) {
|
||
// color.red(color.red())
|
||
return this;
|
||
}
|
||
|
||
// color.red(100)
|
||
svalues[index] = val;
|
||
this.setValues(space, svalues);
|
||
|
||
return this;
|
||
};
|
||
|
||
if (typeof window !== 'undefined') {
|
||
window.Color = Color;
|
||
}
|
||
|
||
var chartjsColor = Color;
|
||
|
||
function isValidKey(key) {
|
||
return ['__proto__', 'prototype', 'constructor'].indexOf(key) === -1;
|
||
}
|
||
|
||
/**
|
||
* @namespace Chart.helpers
|
||
*/
|
||
var helpers = {
|
||
/**
|
||
* An empty function that can be used, for example, for optional callback.
|
||
*/
|
||
noop: function() {},
|
||
|
||
/**
|
||
* Returns a unique id, sequentially generated from a global variable.
|
||
* @returns {number}
|
||
* @function
|
||
*/
|
||
uid: (function() {
|
||
var id = 0;
|
||
return function() {
|
||
return id++;
|
||
};
|
||
}()),
|
||
|
||
/**
|
||
* Returns true if `value` is neither null nor undefined, else returns false.
|
||
* @param {*} value - The value to test.
|
||
* @returns {boolean}
|
||
* @since 2.7.0
|
||
*/
|
||
isNullOrUndef: function(value) {
|
||
return value === null || typeof value === 'undefined';
|
||
},
|
||
|
||
/**
|
||
* Returns true if `value` is an array (including typed arrays), else returns false.
|
||
* @param {*} value - The value to test.
|
||
* @returns {boolean}
|
||
* @function
|
||
*/
|
||
isArray: function(value) {
|
||
if (Array.isArray && Array.isArray(value)) {
|
||
return true;
|
||
}
|
||
var type = Object.prototype.toString.call(value);
|
||
if (type.substr(0, 7) === '[object' && type.substr(-6) === 'Array]') {
|
||
return true;
|
||
}
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* Returns true if `value` is an object (excluding null), else returns false.
|
||
* @param {*} value - The value to test.
|
||
* @returns {boolean}
|
||
* @since 2.7.0
|
||
*/
|
||
isObject: function(value) {
|
||
return value !== null && Object.prototype.toString.call(value) === '[object Object]';
|
||
},
|
||
|
||
/**
|
||
* Returns true if `value` is a finite number, else returns false
|
||
* @param {*} value - The value to test.
|
||
* @returns {boolean}
|
||
*/
|
||
isFinite: function(value) {
|
||
return (typeof value === 'number' || value instanceof Number) && isFinite(value);
|
||
},
|
||
|
||
/**
|
||
* Returns `value` if defined, else returns `defaultValue`.
|
||
* @param {*} value - The value to return if defined.
|
||
* @param {*} defaultValue - The value to return if `value` is undefined.
|
||
* @returns {*}
|
||
*/
|
||
valueOrDefault: function(value, defaultValue) {
|
||
return typeof value === 'undefined' ? defaultValue : value;
|
||
},
|
||
|
||
/**
|
||
* Returns value at the given `index` in array if defined, else returns `defaultValue`.
|
||
* @param {Array} value - The array to lookup for value at `index`.
|
||
* @param {number} index - The index in `value` to lookup for value.
|
||
* @param {*} defaultValue - The value to return if `value[index]` is undefined.
|
||
* @returns {*}
|
||
*/
|
||
valueAtIndexOrDefault: function(value, index, defaultValue) {
|
||
return helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);
|
||
},
|
||
|
||
/**
|
||
* Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the
|
||
* value returned by `fn`. If `fn` is not a function, this method returns undefined.
|
||
* @param {function} fn - The function to call.
|
||
* @param {Array|undefined|null} args - The arguments with which `fn` should be called.
|
||
* @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
|
||
* @returns {*}
|
||
*/
|
||
callback: function(fn, args, thisArg) {
|
||
if (fn && typeof fn.call === 'function') {
|
||
return fn.apply(thisArg, args);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Note(SB) for performance sake, this method should only be used when loopable type
|
||
* is unknown or in none intensive code (not called often and small loopable). Else
|
||
* it's preferable to use a regular for() loop and save extra function calls.
|
||
* @param {object|Array} loopable - The object or array to be iterated.
|
||
* @param {function} fn - The function to call for each item.
|
||
* @param {object} [thisArg] - The value of `this` provided for the call to `fn`.
|
||
* @param {boolean} [reverse] - If true, iterates backward on the loopable.
|
||
*/
|
||
each: function(loopable, fn, thisArg, reverse) {
|
||
var i, len, keys;
|
||
if (helpers.isArray(loopable)) {
|
||
len = loopable.length;
|
||
if (reverse) {
|
||
for (i = len - 1; i >= 0; i--) {
|
||
fn.call(thisArg, loopable[i], i);
|
||
}
|
||
} else {
|
||
for (i = 0; i < len; i++) {
|
||
fn.call(thisArg, loopable[i], i);
|
||
}
|
||
}
|
||
} else if (helpers.isObject(loopable)) {
|
||
keys = Object.keys(loopable);
|
||
len = keys.length;
|
||
for (i = 0; i < len; i++) {
|
||
fn.call(thisArg, loopable[keys[i]], keys[i]);
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Returns true if the `a0` and `a1` arrays have the same content, else returns false.
|
||
* @see https://stackoverflow.com/a/14853974
|
||
* @param {Array} a0 - The array to compare
|
||
* @param {Array} a1 - The array to compare
|
||
* @returns {boolean}
|
||
*/
|
||
arrayEquals: function(a0, a1) {
|
||
var i, ilen, v0, v1;
|
||
|
||
if (!a0 || !a1 || a0.length !== a1.length) {
|
||
return false;
|
||
}
|
||
|
||
for (i = 0, ilen = a0.length; i < ilen; ++i) {
|
||
v0 = a0[i];
|
||
v1 = a1[i];
|
||
|
||
if (v0 instanceof Array && v1 instanceof Array) {
|
||
if (!helpers.arrayEquals(v0, v1)) {
|
||
return false;
|
||
}
|
||
} else if (v0 !== v1) {
|
||
// NOTE: two different object instances will never be equal: {x:20} != {x:20}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* Returns a deep copy of `source` without keeping references on objects and arrays.
|
||
* @param {*} source - The value to clone.
|
||
* @returns {*}
|
||
*/
|
||
clone: function(source) {
|
||
if (helpers.isArray(source)) {
|
||
return source.map(helpers.clone);
|
||
}
|
||
|
||
if (helpers.isObject(source)) {
|
||
var target = Object.create(source);
|
||
var keys = Object.keys(source);
|
||
var klen = keys.length;
|
||
var k = 0;
|
||
|
||
for (; k < klen; ++k) {
|
||
target[keys[k]] = helpers.clone(source[keys[k]]);
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
return source;
|
||
},
|
||
|
||
/**
|
||
* The default merger when Chart.helpers.merge is called without merger option.
|
||
* Note(SB): also used by mergeConfig and mergeScaleConfig as fallback.
|
||
* @private
|
||
*/
|
||
_merger: function(key, target, source, options) {
|
||
if (!isValidKey(key)) {
|
||
// We want to ensure we do not copy prototypes over
|
||
// as this can pollute global namespaces
|
||
return;
|
||
}
|
||
|
||
var tval = target[key];
|
||
var sval = source[key];
|
||
|
||
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
||
helpers.merge(tval, sval, options);
|
||
} else {
|
||
target[key] = helpers.clone(sval);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Merges source[key] in target[key] only if target[key] is undefined.
|
||
* @private
|
||
*/
|
||
_mergerIf: function(key, target, source) {
|
||
if (!isValidKey(key)) {
|
||
// We want to ensure we do not copy prototypes over
|
||
// as this can pollute global namespaces
|
||
return;
|
||
}
|
||
|
||
var tval = target[key];
|
||
var sval = source[key];
|
||
|
||
if (helpers.isObject(tval) && helpers.isObject(sval)) {
|
||
helpers.mergeIf(tval, sval);
|
||
} else if (!target.hasOwnProperty(key)) {
|
||
target[key] = helpers.clone(sval);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Recursively deep copies `source` properties into `target` with the given `options`.
|
||
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
||
* @param {object} target - The target object in which all sources are merged into.
|
||
* @param {object|object[]} source - Object(s) to merge into `target`.
|
||
* @param {object} [options] - Merging options:
|
||
* @param {function} [options.merger] - The merge method (key, target, source, options)
|
||
* @returns {object} The `target` object.
|
||
*/
|
||
merge: function(target, source, options) {
|
||
var sources = helpers.isArray(source) ? source : [source];
|
||
var ilen = sources.length;
|
||
var merge, i, keys, klen, k;
|
||
|
||
if (!helpers.isObject(target)) {
|
||
return target;
|
||
}
|
||
|
||
options = options || {};
|
||
merge = options.merger || helpers._merger;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
source = sources[i];
|
||
if (!helpers.isObject(source)) {
|
||
continue;
|
||
}
|
||
|
||
keys = Object.keys(source);
|
||
for (k = 0, klen = keys.length; k < klen; ++k) {
|
||
merge(keys[k], target, source, options);
|
||
}
|
||
}
|
||
|
||
return target;
|
||
},
|
||
|
||
/**
|
||
* Recursively deep copies `source` properties into `target` *only* if not defined in target.
|
||
* IMPORTANT: `target` is not cloned and will be updated with `source` properties.
|
||
* @param {object} target - The target object in which all sources are merged into.
|
||
* @param {object|object[]} source - Object(s) to merge into `target`.
|
||
* @returns {object} The `target` object.
|
||
*/
|
||
mergeIf: function(target, source) {
|
||
return helpers.merge(target, source, {merger: helpers._mergerIf});
|
||
},
|
||
|
||
/**
|
||
* Applies the contents of two or more objects together into the first object.
|
||
* @param {object} target - The target object in which all objects are merged into.
|
||
* @param {object} arg1 - Object containing additional properties to merge in target.
|
||
* @param {object} argN - Additional objects containing properties to merge in target.
|
||
* @returns {object} The `target` object.
|
||
*/
|
||
extend: Object.assign || function(target) {
|
||
return helpers.merge(target, [].slice.call(arguments, 1), {
|
||
merger: function(key, dst, src) {
|
||
dst[key] = src[key];
|
||
}
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Basic javascript inheritance based on the model created in Backbone.js
|
||
*/
|
||
inherits: function(extensions) {
|
||
var me = this;
|
||
var ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {
|
||
return me.apply(this, arguments);
|
||
};
|
||
|
||
var Surrogate = function() {
|
||
this.constructor = ChartElement;
|
||
};
|
||
|
||
Surrogate.prototype = me.prototype;
|
||
ChartElement.prototype = new Surrogate();
|
||
ChartElement.extend = helpers.inherits;
|
||
|
||
if (extensions) {
|
||
helpers.extend(ChartElement.prototype, extensions);
|
||
}
|
||
|
||
ChartElement.__super__ = me.prototype;
|
||
return ChartElement;
|
||
},
|
||
|
||
_deprecated: function(scope, value, previous, current) {
|
||
if (value !== undefined) {
|
||
console.warn(scope + ': "' + previous +
|
||
'" is deprecated. Please use "' + current + '" instead');
|
||
}
|
||
}
|
||
};
|
||
|
||
var helpers_core = helpers;
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.callback instead.
|
||
* @function Chart.helpers.callCallback
|
||
* @deprecated since version 2.6.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers.callCallback = helpers.callback;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Array.prototype.indexOf instead.
|
||
* Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+
|
||
* @function Chart.helpers.indexOf
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers.indexOf = function(array, item, fromIndex) {
|
||
return Array.prototype.indexOf.call(array, item, fromIndex);
|
||
};
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.
|
||
* @function Chart.helpers.getValueOrDefault
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers.getValueOrDefault = helpers.valueOrDefault;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.
|
||
* @function Chart.helpers.getValueAtIndexOrDefault
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
|
||
|
||
/**
|
||
* Easing functions adapted from Robert Penner's easing equations.
|
||
* @namespace Chart.helpers.easingEffects
|
||
* @see http://www.robertpenner.com/easing/
|
||
*/
|
||
var effects = {
|
||
linear: function(t) {
|
||
return t;
|
||
},
|
||
|
||
easeInQuad: function(t) {
|
||
return t * t;
|
||
},
|
||
|
||
easeOutQuad: function(t) {
|
||
return -t * (t - 2);
|
||
},
|
||
|
||
easeInOutQuad: function(t) {
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * t * t;
|
||
}
|
||
return -0.5 * ((--t) * (t - 2) - 1);
|
||
},
|
||
|
||
easeInCubic: function(t) {
|
||
return t * t * t;
|
||
},
|
||
|
||
easeOutCubic: function(t) {
|
||
return (t = t - 1) * t * t + 1;
|
||
},
|
||
|
||
easeInOutCubic: function(t) {
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * t * t * t;
|
||
}
|
||
return 0.5 * ((t -= 2) * t * t + 2);
|
||
},
|
||
|
||
easeInQuart: function(t) {
|
||
return t * t * t * t;
|
||
},
|
||
|
||
easeOutQuart: function(t) {
|
||
return -((t = t - 1) * t * t * t - 1);
|
||
},
|
||
|
||
easeInOutQuart: function(t) {
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * t * t * t * t;
|
||
}
|
||
return -0.5 * ((t -= 2) * t * t * t - 2);
|
||
},
|
||
|
||
easeInQuint: function(t) {
|
||
return t * t * t * t * t;
|
||
},
|
||
|
||
easeOutQuint: function(t) {
|
||
return (t = t - 1) * t * t * t * t + 1;
|
||
},
|
||
|
||
easeInOutQuint: function(t) {
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * t * t * t * t * t;
|
||
}
|
||
return 0.5 * ((t -= 2) * t * t * t * t + 2);
|
||
},
|
||
|
||
easeInSine: function(t) {
|
||
return -Math.cos(t * (Math.PI / 2)) + 1;
|
||
},
|
||
|
||
easeOutSine: function(t) {
|
||
return Math.sin(t * (Math.PI / 2));
|
||
},
|
||
|
||
easeInOutSine: function(t) {
|
||
return -0.5 * (Math.cos(Math.PI * t) - 1);
|
||
},
|
||
|
||
easeInExpo: function(t) {
|
||
return (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));
|
||
},
|
||
|
||
easeOutExpo: function(t) {
|
||
return (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;
|
||
},
|
||
|
||
easeInOutExpo: function(t) {
|
||
if (t === 0) {
|
||
return 0;
|
||
}
|
||
if (t === 1) {
|
||
return 1;
|
||
}
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * Math.pow(2, 10 * (t - 1));
|
||
}
|
||
return 0.5 * (-Math.pow(2, -10 * --t) + 2);
|
||
},
|
||
|
||
easeInCirc: function(t) {
|
||
if (t >= 1) {
|
||
return t;
|
||
}
|
||
return -(Math.sqrt(1 - t * t) - 1);
|
||
},
|
||
|
||
easeOutCirc: function(t) {
|
||
return Math.sqrt(1 - (t = t - 1) * t);
|
||
},
|
||
|
||
easeInOutCirc: function(t) {
|
||
if ((t /= 0.5) < 1) {
|
||
return -0.5 * (Math.sqrt(1 - t * t) - 1);
|
||
}
|
||
return 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);
|
||
},
|
||
|
||
easeInElastic: function(t) {
|
||
var s = 1.70158;
|
||
var p = 0;
|
||
var a = 1;
|
||
if (t === 0) {
|
||
return 0;
|
||
}
|
||
if (t === 1) {
|
||
return 1;
|
||
}
|
||
if (!p) {
|
||
p = 0.3;
|
||
}
|
||
if (a < 1) {
|
||
a = 1;
|
||
s = p / 4;
|
||
} else {
|
||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||
}
|
||
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
|
||
},
|
||
|
||
easeOutElastic: function(t) {
|
||
var s = 1.70158;
|
||
var p = 0;
|
||
var a = 1;
|
||
if (t === 0) {
|
||
return 0;
|
||
}
|
||
if (t === 1) {
|
||
return 1;
|
||
}
|
||
if (!p) {
|
||
p = 0.3;
|
||
}
|
||
if (a < 1) {
|
||
a = 1;
|
||
s = p / 4;
|
||
} else {
|
||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||
}
|
||
return a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;
|
||
},
|
||
|
||
easeInOutElastic: function(t) {
|
||
var s = 1.70158;
|
||
var p = 0;
|
||
var a = 1;
|
||
if (t === 0) {
|
||
return 0;
|
||
}
|
||
if ((t /= 0.5) === 2) {
|
||
return 1;
|
||
}
|
||
if (!p) {
|
||
p = 0.45;
|
||
}
|
||
if (a < 1) {
|
||
a = 1;
|
||
s = p / 4;
|
||
} else {
|
||
s = p / (2 * Math.PI) * Math.asin(1 / a);
|
||
}
|
||
if (t < 1) {
|
||
return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));
|
||
}
|
||
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;
|
||
},
|
||
easeInBack: function(t) {
|
||
var s = 1.70158;
|
||
return t * t * ((s + 1) * t - s);
|
||
},
|
||
|
||
easeOutBack: function(t) {
|
||
var s = 1.70158;
|
||
return (t = t - 1) * t * ((s + 1) * t + s) + 1;
|
||
},
|
||
|
||
easeInOutBack: function(t) {
|
||
var s = 1.70158;
|
||
if ((t /= 0.5) < 1) {
|
||
return 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));
|
||
}
|
||
return 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
|
||
},
|
||
|
||
easeInBounce: function(t) {
|
||
return 1 - effects.easeOutBounce(1 - t);
|
||
},
|
||
|
||
easeOutBounce: function(t) {
|
||
if (t < (1 / 2.75)) {
|
||
return 7.5625 * t * t;
|
||
}
|
||
if (t < (2 / 2.75)) {
|
||
return 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;
|
||
}
|
||
if (t < (2.5 / 2.75)) {
|
||
return 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;
|
||
}
|
||
return 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;
|
||
},
|
||
|
||
easeInOutBounce: function(t) {
|
||
if (t < 0.5) {
|
||
return effects.easeInBounce(t * 2) * 0.5;
|
||
}
|
||
return effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
|
||
}
|
||
};
|
||
|
||
var helpers_easing = {
|
||
effects: effects
|
||
};
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.easing.effects instead.
|
||
* @function Chart.helpers.easingEffects
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers_core.easingEffects = effects;
|
||
|
||
var PI = Math.PI;
|
||
var RAD_PER_DEG = PI / 180;
|
||
var DOUBLE_PI = PI * 2;
|
||
var HALF_PI = PI / 2;
|
||
var QUARTER_PI = PI / 4;
|
||
var TWO_THIRDS_PI = PI * 2 / 3;
|
||
|
||
/**
|
||
* @namespace Chart.helpers.canvas
|
||
*/
|
||
var exports$1 = {
|
||
/**
|
||
* Clears the entire canvas associated to the given `chart`.
|
||
* @param {Chart} chart - The chart for which to clear the canvas.
|
||
*/
|
||
clear: function(chart) {
|
||
chart.ctx.clearRect(0, 0, chart.width, chart.height);
|
||
},
|
||
|
||
/**
|
||
* Creates a "path" for a rectangle with rounded corners at position (x, y) with a
|
||
* given size (width, height) and the same `radius` for all corners.
|
||
* @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.
|
||
* @param {number} x - The x axis of the coordinate for the rectangle starting point.
|
||
* @param {number} y - The y axis of the coordinate for the rectangle starting point.
|
||
* @param {number} width - The rectangle's width.
|
||
* @param {number} height - The rectangle's height.
|
||
* @param {number} radius - The rounded amount (in pixels) for the four corners.
|
||
* @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?
|
||
*/
|
||
roundedRect: function(ctx, x, y, width, height, radius) {
|
||
if (radius) {
|
||
var r = Math.min(radius, height / 2, width / 2);
|
||
var left = x + r;
|
||
var top = y + r;
|
||
var right = x + width - r;
|
||
var bottom = y + height - r;
|
||
|
||
ctx.moveTo(x, top);
|
||
if (left < right && top < bottom) {
|
||
ctx.arc(left, top, r, -PI, -HALF_PI);
|
||
ctx.arc(right, top, r, -HALF_PI, 0);
|
||
ctx.arc(right, bottom, r, 0, HALF_PI);
|
||
ctx.arc(left, bottom, r, HALF_PI, PI);
|
||
} else if (left < right) {
|
||
ctx.moveTo(left, y);
|
||
ctx.arc(right, top, r, -HALF_PI, HALF_PI);
|
||
ctx.arc(left, top, r, HALF_PI, PI + HALF_PI);
|
||
} else if (top < bottom) {
|
||
ctx.arc(left, top, r, -PI, 0);
|
||
ctx.arc(left, bottom, r, 0, PI);
|
||
} else {
|
||
ctx.arc(left, top, r, -PI, PI);
|
||
}
|
||
ctx.closePath();
|
||
ctx.moveTo(x, y);
|
||
} else {
|
||
ctx.rect(x, y, width, height);
|
||
}
|
||
},
|
||
|
||
drawPoint: function(ctx, style, radius, x, y, rotation) {
|
||
var type, xOffset, yOffset, size, cornerRadius;
|
||
var rad = (rotation || 0) * RAD_PER_DEG;
|
||
|
||
if (style && typeof style === 'object') {
|
||
type = style.toString();
|
||
if (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {
|
||
ctx.save();
|
||
ctx.translate(x, y);
|
||
ctx.rotate(rad);
|
||
ctx.drawImage(style, -style.width / 2, -style.height / 2, style.width, style.height);
|
||
ctx.restore();
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (isNaN(radius) || radius <= 0) {
|
||
return;
|
||
}
|
||
|
||
ctx.beginPath();
|
||
|
||
switch (style) {
|
||
// Default includes circle
|
||
default:
|
||
ctx.arc(x, y, radius, 0, DOUBLE_PI);
|
||
ctx.closePath();
|
||
break;
|
||
case 'triangle':
|
||
ctx.moveTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||
rad += TWO_THIRDS_PI;
|
||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||
rad += TWO_THIRDS_PI;
|
||
ctx.lineTo(x + Math.sin(rad) * radius, y - Math.cos(rad) * radius);
|
||
ctx.closePath();
|
||
break;
|
||
case 'rectRounded':
|
||
// NOTE: the rounded rect implementation changed to use `arc` instead of
|
||
// `quadraticCurveTo` since it generates better results when rect is
|
||
// almost a circle. 0.516 (instead of 0.5) produces results with visually
|
||
// closer proportion to the previous impl and it is inscribed in the
|
||
// circle with `radius`. For more details, see the following PRs:
|
||
// https://github.com/chartjs/Chart.js/issues/5597
|
||
// https://github.com/chartjs/Chart.js/issues/5858
|
||
cornerRadius = radius * 0.516;
|
||
size = radius - cornerRadius;
|
||
xOffset = Math.cos(rad + QUARTER_PI) * size;
|
||
yOffset = Math.sin(rad + QUARTER_PI) * size;
|
||
ctx.arc(x - xOffset, y - yOffset, cornerRadius, rad - PI, rad - HALF_PI);
|
||
ctx.arc(x + yOffset, y - xOffset, cornerRadius, rad - HALF_PI, rad);
|
||
ctx.arc(x + xOffset, y + yOffset, cornerRadius, rad, rad + HALF_PI);
|
||
ctx.arc(x - yOffset, y + xOffset, cornerRadius, rad + HALF_PI, rad + PI);
|
||
ctx.closePath();
|
||
break;
|
||
case 'rect':
|
||
if (!rotation) {
|
||
size = Math.SQRT1_2 * radius;
|
||
ctx.rect(x - size, y - size, 2 * size, 2 * size);
|
||
break;
|
||
}
|
||
rad += QUARTER_PI;
|
||
/* falls through */
|
||
case 'rectRot':
|
||
xOffset = Math.cos(rad) * radius;
|
||
yOffset = Math.sin(rad) * radius;
|
||
ctx.moveTo(x - xOffset, y - yOffset);
|
||
ctx.lineTo(x + yOffset, y - xOffset);
|
||
ctx.lineTo(x + xOffset, y + yOffset);
|
||
ctx.lineTo(x - yOffset, y + xOffset);
|
||
ctx.closePath();
|
||
break;
|
||
case 'crossRot':
|
||
rad += QUARTER_PI;
|
||
/* falls through */
|
||
case 'cross':
|
||
xOffset = Math.cos(rad) * radius;
|
||
yOffset = Math.sin(rad) * radius;
|
||
ctx.moveTo(x - xOffset, y - yOffset);
|
||
ctx.lineTo(x + xOffset, y + yOffset);
|
||
ctx.moveTo(x + yOffset, y - xOffset);
|
||
ctx.lineTo(x - yOffset, y + xOffset);
|
||
break;
|
||
case 'star':
|
||
xOffset = Math.cos(rad) * radius;
|
||
yOffset = Math.sin(rad) * radius;
|
||
ctx.moveTo(x - xOffset, y - yOffset);
|
||
ctx.lineTo(x + xOffset, y + yOffset);
|
||
ctx.moveTo(x + yOffset, y - xOffset);
|
||
ctx.lineTo(x - yOffset, y + xOffset);
|
||
rad += QUARTER_PI;
|
||
xOffset = Math.cos(rad) * radius;
|
||
yOffset = Math.sin(rad) * radius;
|
||
ctx.moveTo(x - xOffset, y - yOffset);
|
||
ctx.lineTo(x + xOffset, y + yOffset);
|
||
ctx.moveTo(x + yOffset, y - xOffset);
|
||
ctx.lineTo(x - yOffset, y + xOffset);
|
||
break;
|
||
case 'line':
|
||
xOffset = Math.cos(rad) * radius;
|
||
yOffset = Math.sin(rad) * radius;
|
||
ctx.moveTo(x - xOffset, y - yOffset);
|
||
ctx.lineTo(x + xOffset, y + yOffset);
|
||
break;
|
||
case 'dash':
|
||
ctx.moveTo(x, y);
|
||
ctx.lineTo(x + Math.cos(rad) * radius, y + Math.sin(rad) * radius);
|
||
break;
|
||
}
|
||
|
||
ctx.fill();
|
||
ctx.stroke();
|
||
},
|
||
|
||
/**
|
||
* Returns true if the point is inside the rectangle
|
||
* @param {object} point - The point to test
|
||
* @param {object} area - The rectangle
|
||
* @returns {boolean}
|
||
* @private
|
||
*/
|
||
_isPointInArea: function(point, area) {
|
||
var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
|
||
|
||
return point.x > area.left - epsilon && point.x < area.right + epsilon &&
|
||
point.y > area.top - epsilon && point.y < area.bottom + epsilon;
|
||
},
|
||
|
||
clipArea: function(ctx, area) {
|
||
ctx.save();
|
||
ctx.beginPath();
|
||
ctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);
|
||
ctx.clip();
|
||
},
|
||
|
||
unclipArea: function(ctx) {
|
||
ctx.restore();
|
||
},
|
||
|
||
lineTo: function(ctx, previous, target, flip) {
|
||
var stepped = target.steppedLine;
|
||
if (stepped) {
|
||
if (stepped === 'middle') {
|
||
var midpoint = (previous.x + target.x) / 2.0;
|
||
ctx.lineTo(midpoint, flip ? target.y : previous.y);
|
||
ctx.lineTo(midpoint, flip ? previous.y : target.y);
|
||
} else if ((stepped === 'after' && !flip) || (stepped !== 'after' && flip)) {
|
||
ctx.lineTo(previous.x, target.y);
|
||
} else {
|
||
ctx.lineTo(target.x, previous.y);
|
||
}
|
||
ctx.lineTo(target.x, target.y);
|
||
return;
|
||
}
|
||
|
||
if (!target.tension) {
|
||
ctx.lineTo(target.x, target.y);
|
||
return;
|
||
}
|
||
|
||
ctx.bezierCurveTo(
|
||
flip ? previous.controlPointPreviousX : previous.controlPointNextX,
|
||
flip ? previous.controlPointPreviousY : previous.controlPointNextY,
|
||
flip ? target.controlPointNextX : target.controlPointPreviousX,
|
||
flip ? target.controlPointNextY : target.controlPointPreviousY,
|
||
target.x,
|
||
target.y);
|
||
}
|
||
};
|
||
|
||
var helpers_canvas = exports$1;
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.canvas.clear instead.
|
||
* @namespace Chart.helpers.clear
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers_core.clear = exports$1.clear;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.
|
||
* @namespace Chart.helpers.drawRoundedRectangle
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers_core.drawRoundedRectangle = function(ctx) {
|
||
ctx.beginPath();
|
||
exports$1.roundedRect.apply(exports$1, arguments);
|
||
};
|
||
|
||
var defaults = {
|
||
/**
|
||
* @private
|
||
*/
|
||
_set: function(scope, values) {
|
||
return helpers_core.merge(this[scope] || (this[scope] = {}), values);
|
||
}
|
||
};
|
||
|
||
// TODO(v3): remove 'global' from namespace. all default are global and
|
||
// there's inconsistency around which options are under 'global'
|
||
defaults._set('global', {
|
||
defaultColor: 'rgba(0,0,0,0.1)',
|
||
defaultFontColor: '#666',
|
||
defaultFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
|
||
defaultFontSize: 12,
|
||
defaultFontStyle: 'normal',
|
||
defaultLineHeight: 1.2,
|
||
showLines: true
|
||
});
|
||
|
||
var core_defaults = defaults;
|
||
|
||
var valueOrDefault = helpers_core.valueOrDefault;
|
||
|
||
/**
|
||
* Converts the given font object into a CSS font string.
|
||
* @param {object} font - A font object.
|
||
* @return {string} The CSS font string. See https://developer.mozilla.org/en-US/docs/Web/CSS/font
|
||
* @private
|
||
*/
|
||
function toFontString(font) {
|
||
if (!font || helpers_core.isNullOrUndef(font.size) || helpers_core.isNullOrUndef(font.family)) {
|
||
return null;
|
||
}
|
||
|
||
return (font.style ? font.style + ' ' : '')
|
||
+ (font.weight ? font.weight + ' ' : '')
|
||
+ font.size + 'px '
|
||
+ font.family;
|
||
}
|
||
|
||
/**
|
||
* @alias Chart.helpers.options
|
||
* @namespace
|
||
*/
|
||
var helpers_options = {
|
||
/**
|
||
* Converts the given line height `value` in pixels for a specific font `size`.
|
||
* @param {number|string} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
|
||
* @param {number} size - The font size (in pixels) used to resolve relative `value`.
|
||
* @returns {number} The effective line height in pixels (size * 1.2 if value is invalid).
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
|
||
* @since 2.7.0
|
||
*/
|
||
toLineHeight: function(value, size) {
|
||
var matches = ('' + value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
|
||
if (!matches || matches[1] === 'normal') {
|
||
return size * 1.2;
|
||
}
|
||
|
||
value = +matches[2];
|
||
|
||
switch (matches[3]) {
|
||
case 'px':
|
||
return value;
|
||
case '%':
|
||
value /= 100;
|
||
break;
|
||
}
|
||
|
||
return size * value;
|
||
},
|
||
|
||
/**
|
||
* Converts the given value into a padding object with pre-computed width/height.
|
||
* @param {number|object} value - If a number, set the value to all TRBL component,
|
||
* else, if and object, use defined properties and sets undefined ones to 0.
|
||
* @returns {object} The padding values (top, right, bottom, left, width, height)
|
||
* @since 2.7.0
|
||
*/
|
||
toPadding: function(value) {
|
||
var t, r, b, l;
|
||
|
||
if (helpers_core.isObject(value)) {
|
||
t = +value.top || 0;
|
||
r = +value.right || 0;
|
||
b = +value.bottom || 0;
|
||
l = +value.left || 0;
|
||
} else {
|
||
t = r = b = l = +value || 0;
|
||
}
|
||
|
||
return {
|
||
top: t,
|
||
right: r,
|
||
bottom: b,
|
||
left: l,
|
||
height: t + b,
|
||
width: l + r
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Parses font options and returns the font object.
|
||
* @param {object} options - A object that contains font options to be parsed.
|
||
* @return {object} The font object.
|
||
* @todo Support font.* options and renamed to toFont().
|
||
* @private
|
||
*/
|
||
_parseFont: function(options) {
|
||
var globalDefaults = core_defaults.global;
|
||
var size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);
|
||
var font = {
|
||
family: valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily),
|
||
lineHeight: helpers_core.options.toLineHeight(valueOrDefault(options.lineHeight, globalDefaults.defaultLineHeight), size),
|
||
size: size,
|
||
style: valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle),
|
||
weight: null,
|
||
string: ''
|
||
};
|
||
|
||
font.string = toFontString(font);
|
||
return font;
|
||
},
|
||
|
||
/**
|
||
* Evaluates the given `inputs` sequentially and returns the first defined value.
|
||
* @param {Array} inputs - An array of values, falling back to the last value.
|
||
* @param {object} [context] - If defined and the current value is a function, the value
|
||
* is called with `context` as first argument and the result becomes the new input.
|
||
* @param {number} [index] - If defined and the current value is an array, the value
|
||
* at `index` become the new input.
|
||
* @param {object} [info] - object to return information about resolution in
|
||
* @param {boolean} [info.cacheable] - Will be set to `false` if option is not cacheable.
|
||
* @since 2.7.0
|
||
*/
|
||
resolve: function(inputs, context, index, info) {
|
||
var cacheable = true;
|
||
var i, ilen, value;
|
||
|
||
for (i = 0, ilen = inputs.length; i < ilen; ++i) {
|
||
value = inputs[i];
|
||
if (value === undefined) {
|
||
continue;
|
||
}
|
||
if (context !== undefined && typeof value === 'function') {
|
||
value = value(context);
|
||
cacheable = false;
|
||
}
|
||
if (index !== undefined && helpers_core.isArray(value)) {
|
||
value = value[index];
|
||
cacheable = false;
|
||
}
|
||
if (value !== undefined) {
|
||
if (info && !cacheable) {
|
||
info.cacheable = false;
|
||
}
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* @alias Chart.helpers.math
|
||
* @namespace
|
||
*/
|
||
var exports$2 = {
|
||
/**
|
||
* Returns an array of factors sorted from 1 to sqrt(value)
|
||
* @private
|
||
*/
|
||
_factorize: function(value) {
|
||
var result = [];
|
||
var sqrt = Math.sqrt(value);
|
||
var i;
|
||
|
||
for (i = 1; i < sqrt; i++) {
|
||
if (value % i === 0) {
|
||
result.push(i);
|
||
result.push(value / i);
|
||
}
|
||
}
|
||
if (sqrt === (sqrt | 0)) { // if value is a square number
|
||
result.push(sqrt);
|
||
}
|
||
|
||
result.sort(function(a, b) {
|
||
return a - b;
|
||
}).pop();
|
||
return result;
|
||
},
|
||
|
||
log10: Math.log10 || function(x) {
|
||
var exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.
|
||
// Check for whole powers of 10,
|
||
// which due to floating point rounding error should be corrected.
|
||
var powerOf10 = Math.round(exponent);
|
||
var isPowerOf10 = x === Math.pow(10, powerOf10);
|
||
|
||
return isPowerOf10 ? powerOf10 : exponent;
|
||
}
|
||
};
|
||
|
||
var helpers_math = exports$2;
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.math.log10 instead.
|
||
* @namespace Chart.helpers.log10
|
||
* @deprecated since version 2.9.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers_core.log10 = exports$2.log10;
|
||
|
||
var getRtlAdapter = function(rectX, width) {
|
||
return {
|
||
x: function(x) {
|
||
return rectX + rectX + width - x;
|
||
},
|
||
setWidth: function(w) {
|
||
width = w;
|
||
},
|
||
textAlign: function(align) {
|
||
if (align === 'center') {
|
||
return align;
|
||
}
|
||
return align === 'right' ? 'left' : 'right';
|
||
},
|
||
xPlus: function(x, value) {
|
||
return x - value;
|
||
},
|
||
leftForLtr: function(x, itemWidth) {
|
||
return x - itemWidth;
|
||
},
|
||
};
|
||
};
|
||
|
||
var getLtrAdapter = function() {
|
||
return {
|
||
x: function(x) {
|
||
return x;
|
||
},
|
||
setWidth: function(w) { // eslint-disable-line no-unused-vars
|
||
},
|
||
textAlign: function(align) {
|
||
return align;
|
||
},
|
||
xPlus: function(x, value) {
|
||
return x + value;
|
||
},
|
||
leftForLtr: function(x, _itemWidth) { // eslint-disable-line no-unused-vars
|
||
return x;
|
||
},
|
||
};
|
||
};
|
||
|
||
var getAdapter = function(rtl, rectX, width) {
|
||
return rtl ? getRtlAdapter(rectX, width) : getLtrAdapter();
|
||
};
|
||
|
||
var overrideTextDirection = function(ctx, direction) {
|
||
var style, original;
|
||
if (direction === 'ltr' || direction === 'rtl') {
|
||
style = ctx.canvas.style;
|
||
original = [
|
||
style.getPropertyValue('direction'),
|
||
style.getPropertyPriority('direction'),
|
||
];
|
||
|
||
style.setProperty('direction', direction, 'important');
|
||
ctx.prevTextDirection = original;
|
||
}
|
||
};
|
||
|
||
var restoreTextDirection = function(ctx) {
|
||
var original = ctx.prevTextDirection;
|
||
if (original !== undefined) {
|
||
delete ctx.prevTextDirection;
|
||
ctx.canvas.style.setProperty('direction', original[0], original[1]);
|
||
}
|
||
};
|
||
|
||
var helpers_rtl = {
|
||
getRtlAdapter: getAdapter,
|
||
overrideTextDirection: overrideTextDirection,
|
||
restoreTextDirection: restoreTextDirection,
|
||
};
|
||
|
||
var helpers$1 = helpers_core;
|
||
var easing = helpers_easing;
|
||
var canvas = helpers_canvas;
|
||
var options = helpers_options;
|
||
var math = helpers_math;
|
||
var rtl = helpers_rtl;
|
||
helpers$1.easing = easing;
|
||
helpers$1.canvas = canvas;
|
||
helpers$1.options = options;
|
||
helpers$1.math = math;
|
||
helpers$1.rtl = rtl;
|
||
|
||
function interpolate(start, view, model, ease) {
|
||
var keys = Object.keys(model);
|
||
var i, ilen, key, actual, origin, target, type, c0, c1;
|
||
|
||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||
key = keys[i];
|
||
|
||
target = model[key];
|
||
|
||
// if a value is added to the model after pivot() has been called, the view
|
||
// doesn't contain it, so let's initialize the view to the target value.
|
||
if (!view.hasOwnProperty(key)) {
|
||
view[key] = target;
|
||
}
|
||
|
||
actual = view[key];
|
||
|
||
if (actual === target || key[0] === '_') {
|
||
continue;
|
||
}
|
||
|
||
if (!start.hasOwnProperty(key)) {
|
||
start[key] = actual;
|
||
}
|
||
|
||
origin = start[key];
|
||
|
||
type = typeof target;
|
||
|
||
if (type === typeof origin) {
|
||
if (type === 'string') {
|
||
c0 = chartjsColor(origin);
|
||
if (c0.valid) {
|
||
c1 = chartjsColor(target);
|
||
if (c1.valid) {
|
||
view[key] = c1.mix(c0, ease).rgbString();
|
||
continue;
|
||
}
|
||
}
|
||
} else if (helpers$1.isFinite(origin) && helpers$1.isFinite(target)) {
|
||
view[key] = origin + (target - origin) * ease;
|
||
continue;
|
||
}
|
||
}
|
||
|
||
view[key] = target;
|
||
}
|
||
}
|
||
|
||
var Element = function(configuration) {
|
||
helpers$1.extend(this, configuration);
|
||
this.initialize.apply(this, arguments);
|
||
};
|
||
|
||
helpers$1.extend(Element.prototype, {
|
||
_type: undefined,
|
||
|
||
initialize: function() {
|
||
this.hidden = false;
|
||
},
|
||
|
||
pivot: function() {
|
||
var me = this;
|
||
if (!me._view) {
|
||
me._view = helpers$1.extend({}, me._model);
|
||
}
|
||
me._start = {};
|
||
return me;
|
||
},
|
||
|
||
transition: function(ease) {
|
||
var me = this;
|
||
var model = me._model;
|
||
var start = me._start;
|
||
var view = me._view;
|
||
|
||
// No animation -> No Transition
|
||
if (!model || ease === 1) {
|
||
me._view = helpers$1.extend({}, model);
|
||
me._start = null;
|
||
return me;
|
||
}
|
||
|
||
if (!view) {
|
||
view = me._view = {};
|
||
}
|
||
|
||
if (!start) {
|
||
start = me._start = {};
|
||
}
|
||
|
||
interpolate(start, view, model, ease);
|
||
|
||
return me;
|
||
},
|
||
|
||
tooltipPosition: function() {
|
||
return {
|
||
x: this._model.x,
|
||
y: this._model.y
|
||
};
|
||
},
|
||
|
||
hasValue: function() {
|
||
return helpers$1.isNumber(this._model.x) && helpers$1.isNumber(this._model.y);
|
||
}
|
||
});
|
||
|
||
Element.extend = helpers$1.inherits;
|
||
|
||
var core_element = Element;
|
||
|
||
var exports$3 = core_element.extend({
|
||
chart: null, // the animation associated chart instance
|
||
currentStep: 0, // the current animation step
|
||
numSteps: 60, // default number of steps
|
||
easing: '', // the easing to use for this animation
|
||
render: null, // render function used by the animation service
|
||
|
||
onAnimationProgress: null, // user specified callback to fire on each step of the animation
|
||
onAnimationComplete: null, // user specified callback to fire when the animation finishes
|
||
});
|
||
|
||
var core_animation = exports$3;
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.Animation instead
|
||
* @prop Chart.Animation#animationObject
|
||
* @deprecated since version 2.6.0
|
||
* @todo remove at version 3
|
||
*/
|
||
Object.defineProperty(exports$3.prototype, 'animationObject', {
|
||
get: function() {
|
||
return this;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.Animation#chart instead
|
||
* @prop Chart.Animation#chartInstance
|
||
* @deprecated since version 2.6.0
|
||
* @todo remove at version 3
|
||
*/
|
||
Object.defineProperty(exports$3.prototype, 'chartInstance', {
|
||
get: function() {
|
||
return this.chart;
|
||
},
|
||
set: function(value) {
|
||
this.chart = value;
|
||
}
|
||
});
|
||
|
||
core_defaults._set('global', {
|
||
animation: {
|
||
duration: 1000,
|
||
easing: 'easeOutQuart',
|
||
onProgress: helpers$1.noop,
|
||
onComplete: helpers$1.noop
|
||
}
|
||
});
|
||
|
||
var core_animations = {
|
||
animations: [],
|
||
request: null,
|
||
|
||
/**
|
||
* @param {Chart} chart - The chart to animate.
|
||
* @param {Chart.Animation} animation - The animation that we will animate.
|
||
* @param {number} duration - The animation duration in ms.
|
||
* @param {boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions
|
||
*/
|
||
addAnimation: function(chart, animation, duration, lazy) {
|
||
var animations = this.animations;
|
||
var i, ilen;
|
||
|
||
animation.chart = chart;
|
||
animation.startTime = Date.now();
|
||
animation.duration = duration;
|
||
|
||
if (!lazy) {
|
||
chart.animating = true;
|
||
}
|
||
|
||
for (i = 0, ilen = animations.length; i < ilen; ++i) {
|
||
if (animations[i].chart === chart) {
|
||
animations[i] = animation;
|
||
return;
|
||
}
|
||
}
|
||
|
||
animations.push(animation);
|
||
|
||
// If there are no animations queued, manually kickstart a digest, for lack of a better word
|
||
if (animations.length === 1) {
|
||
this.requestAnimationFrame();
|
||
}
|
||
},
|
||
|
||
cancelAnimation: function(chart) {
|
||
var index = helpers$1.findIndex(this.animations, function(animation) {
|
||
return animation.chart === chart;
|
||
});
|
||
|
||
if (index !== -1) {
|
||
this.animations.splice(index, 1);
|
||
chart.animating = false;
|
||
}
|
||
},
|
||
|
||
requestAnimationFrame: function() {
|
||
var me = this;
|
||
if (me.request === null) {
|
||
// Skip animation frame requests until the active one is executed.
|
||
// This can happen when processing mouse events, e.g. 'mousemove'
|
||
// and 'mouseout' events will trigger multiple renders.
|
||
me.request = helpers$1.requestAnimFrame.call(window, function() {
|
||
me.request = null;
|
||
me.startDigest();
|
||
});
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
startDigest: function() {
|
||
var me = this;
|
||
|
||
me.advance();
|
||
|
||
// Do we have more stuff to animate?
|
||
if (me.animations.length > 0) {
|
||
me.requestAnimationFrame();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
advance: function() {
|
||
var animations = this.animations;
|
||
var animation, chart, numSteps, nextStep;
|
||
var i = 0;
|
||
|
||
// 1 animation per chart, so we are looping charts here
|
||
while (i < animations.length) {
|
||
animation = animations[i];
|
||
chart = animation.chart;
|
||
numSteps = animation.numSteps;
|
||
|
||
// Make sure that currentStep starts at 1
|
||
// https://github.com/chartjs/Chart.js/issues/6104
|
||
nextStep = Math.floor((Date.now() - animation.startTime) / animation.duration * numSteps) + 1;
|
||
animation.currentStep = Math.min(nextStep, numSteps);
|
||
|
||
helpers$1.callback(animation.render, [chart, animation], chart);
|
||
helpers$1.callback(animation.onAnimationProgress, [animation], chart);
|
||
|
||
if (animation.currentStep >= numSteps) {
|
||
helpers$1.callback(animation.onAnimationComplete, [animation], chart);
|
||
chart.animating = false;
|
||
animations.splice(i, 1);
|
||
} else {
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
var resolve = helpers$1.options.resolve;
|
||
|
||
var arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];
|
||
|
||
/**
|
||
* Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',
|
||
* 'unshift') and notify the listener AFTER the array has been altered. Listeners are
|
||
* called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.
|
||
*/
|
||
function listenArrayEvents(array, listener) {
|
||
if (array._chartjs) {
|
||
array._chartjs.listeners.push(listener);
|
||
return;
|
||
}
|
||
|
||
Object.defineProperty(array, '_chartjs', {
|
||
configurable: true,
|
||
enumerable: false,
|
||
value: {
|
||
listeners: [listener]
|
||
}
|
||
});
|
||
|
||
arrayEvents.forEach(function(key) {
|
||
var method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);
|
||
var base = array[key];
|
||
|
||
Object.defineProperty(array, key, {
|
||
configurable: true,
|
||
enumerable: false,
|
||
value: function() {
|
||
var args = Array.prototype.slice.call(arguments);
|
||
var res = base.apply(this, args);
|
||
|
||
helpers$1.each(array._chartjs.listeners, function(object) {
|
||
if (typeof object[method] === 'function') {
|
||
object[method].apply(object, args);
|
||
}
|
||
});
|
||
|
||
return res;
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Removes the given array event listener and cleanup extra attached properties (such as
|
||
* the _chartjs stub and overridden methods) if array doesn't have any more listeners.
|
||
*/
|
||
function unlistenArrayEvents(array, listener) {
|
||
var stub = array._chartjs;
|
||
if (!stub) {
|
||
return;
|
||
}
|
||
|
||
var listeners = stub.listeners;
|
||
var index = listeners.indexOf(listener);
|
||
if (index !== -1) {
|
||
listeners.splice(index, 1);
|
||
}
|
||
|
||
if (listeners.length > 0) {
|
||
return;
|
||
}
|
||
|
||
arrayEvents.forEach(function(key) {
|
||
delete array[key];
|
||
});
|
||
|
||
delete array._chartjs;
|
||
}
|
||
|
||
// Base class for all dataset controllers (line, bar, etc)
|
||
var DatasetController = function(chart, datasetIndex) {
|
||
this.initialize(chart, datasetIndex);
|
||
};
|
||
|
||
helpers$1.extend(DatasetController.prototype, {
|
||
|
||
/**
|
||
* Element type used to generate a meta dataset (e.g. Chart.element.Line).
|
||
* @type {Chart.core.element}
|
||
*/
|
||
datasetElementType: null,
|
||
|
||
/**
|
||
* Element type used to generate a meta data (e.g. Chart.element.Point).
|
||
* @type {Chart.core.element}
|
||
*/
|
||
dataElementType: null,
|
||
|
||
/**
|
||
* Dataset element option keys to be resolved in _resolveDatasetElementOptions.
|
||
* A derived controller may override this to resolve controller-specific options.
|
||
* The keys defined here are for backward compatibility for legend styles.
|
||
* @private
|
||
*/
|
||
_datasetElementOptions: [
|
||
'backgroundColor',
|
||
'borderCapStyle',
|
||
'borderColor',
|
||
'borderDash',
|
||
'borderDashOffset',
|
||
'borderJoinStyle',
|
||
'borderWidth'
|
||
],
|
||
|
||
/**
|
||
* Data element option keys to be resolved in _resolveDataElementOptions.
|
||
* A derived controller may override this to resolve controller-specific options.
|
||
* The keys defined here are for backward compatibility for legend styles.
|
||
* @private
|
||
*/
|
||
_dataElementOptions: [
|
||
'backgroundColor',
|
||
'borderColor',
|
||
'borderWidth',
|
||
'pointStyle'
|
||
],
|
||
|
||
initialize: function(chart, datasetIndex) {
|
||
var me = this;
|
||
me.chart = chart;
|
||
me.index = datasetIndex;
|
||
me.linkScales();
|
||
me.addElements();
|
||
me._type = me.getMeta().type;
|
||
},
|
||
|
||
updateIndex: function(datasetIndex) {
|
||
this.index = datasetIndex;
|
||
},
|
||
|
||
linkScales: function() {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var chart = me.chart;
|
||
var scales = chart.scales;
|
||
var dataset = me.getDataset();
|
||
var scalesOpts = chart.options.scales;
|
||
|
||
if (meta.xAxisID === null || !(meta.xAxisID in scales) || dataset.xAxisID) {
|
||
meta.xAxisID = dataset.xAxisID || scalesOpts.xAxes[0].id;
|
||
}
|
||
if (meta.yAxisID === null || !(meta.yAxisID in scales) || dataset.yAxisID) {
|
||
meta.yAxisID = dataset.yAxisID || scalesOpts.yAxes[0].id;
|
||
}
|
||
},
|
||
|
||
getDataset: function() {
|
||
return this.chart.data.datasets[this.index];
|
||
},
|
||
|
||
getMeta: function() {
|
||
return this.chart.getDatasetMeta(this.index);
|
||
},
|
||
|
||
getScaleForId: function(scaleID) {
|
||
return this.chart.scales[scaleID];
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getValueScaleId: function() {
|
||
return this.getMeta().yAxisID;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getIndexScaleId: function() {
|
||
return this.getMeta().xAxisID;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getValueScale: function() {
|
||
return this.getScaleForId(this._getValueScaleId());
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getIndexScale: function() {
|
||
return this.getScaleForId(this._getIndexScaleId());
|
||
},
|
||
|
||
reset: function() {
|
||
this._update(true);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
destroy: function() {
|
||
if (this._data) {
|
||
unlistenArrayEvents(this._data, this);
|
||
}
|
||
},
|
||
|
||
createMetaDataset: function() {
|
||
var me = this;
|
||
var type = me.datasetElementType;
|
||
return type && new type({
|
||
_chart: me.chart,
|
||
_datasetIndex: me.index
|
||
});
|
||
},
|
||
|
||
createMetaData: function(index) {
|
||
var me = this;
|
||
var type = me.dataElementType;
|
||
return type && new type({
|
||
_chart: me.chart,
|
||
_datasetIndex: me.index,
|
||
_index: index
|
||
});
|
||
},
|
||
|
||
addElements: function() {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var data = me.getDataset().data || [];
|
||
var metaData = meta.data;
|
||
var i, ilen;
|
||
|
||
for (i = 0, ilen = data.length; i < ilen; ++i) {
|
||
metaData[i] = metaData[i] || me.createMetaData(i);
|
||
}
|
||
|
||
meta.dataset = meta.dataset || me.createMetaDataset();
|
||
},
|
||
|
||
addElementAndReset: function(index) {
|
||
var element = this.createMetaData(index);
|
||
this.getMeta().data.splice(index, 0, element);
|
||
this.updateElement(element, index, true);
|
||
},
|
||
|
||
buildOrUpdateElements: function() {
|
||
var me = this;
|
||
var dataset = me.getDataset();
|
||
var data = dataset.data || (dataset.data = []);
|
||
|
||
// In order to correctly handle data addition/deletion animation (an thus simulate
|
||
// real-time charts), we need to monitor these data modifications and synchronize
|
||
// the internal meta data accordingly.
|
||
if (me._data !== data) {
|
||
if (me._data) {
|
||
// This case happens when the user replaced the data array instance.
|
||
unlistenArrayEvents(me._data, me);
|
||
}
|
||
|
||
if (data && Object.isExtensible(data)) {
|
||
listenArrayEvents(data, me);
|
||
}
|
||
me._data = data;
|
||
}
|
||
|
||
// Re-sync meta data in case the user replaced the data array or if we missed
|
||
// any updates and so make sure that we handle number of datapoints changing.
|
||
me.resyncElements();
|
||
},
|
||
|
||
/**
|
||
* Returns the merged user-supplied and default dataset-level options
|
||
* @private
|
||
*/
|
||
_configure: function() {
|
||
var me = this;
|
||
me._config = helpers$1.merge(Object.create(null), [
|
||
me.chart.options.datasets[me._type],
|
||
me.getDataset(),
|
||
], {
|
||
merger: function(key, target, source) {
|
||
if (key !== '_meta' && key !== 'data') {
|
||
helpers$1._merger(key, target, source);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
_update: function(reset) {
|
||
var me = this;
|
||
me._configure();
|
||
me._cachedDataOpts = null;
|
||
me.update(reset);
|
||
},
|
||
|
||
update: helpers$1.noop,
|
||
|
||
transition: function(easingValue) {
|
||
var meta = this.getMeta();
|
||
var elements = meta.data || [];
|
||
var ilen = elements.length;
|
||
var i = 0;
|
||
|
||
for (; i < ilen; ++i) {
|
||
elements[i].transition(easingValue);
|
||
}
|
||
|
||
if (meta.dataset) {
|
||
meta.dataset.transition(easingValue);
|
||
}
|
||
},
|
||
|
||
draw: function() {
|
||
var meta = this.getMeta();
|
||
var elements = meta.data || [];
|
||
var ilen = elements.length;
|
||
var i = 0;
|
||
|
||
if (meta.dataset) {
|
||
meta.dataset.draw();
|
||
}
|
||
|
||
for (; i < ilen; ++i) {
|
||
elements[i].draw();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Returns a set of predefined style properties that should be used to represent the dataset
|
||
* or the data if the index is specified
|
||
* @param {number} index - data index
|
||
* @return {IStyleInterface} style object
|
||
*/
|
||
getStyle: function(index) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var dataset = meta.dataset;
|
||
var style;
|
||
|
||
me._configure();
|
||
if (dataset && index === undefined) {
|
||
style = me._resolveDatasetElementOptions(dataset || {});
|
||
} else {
|
||
index = index || 0;
|
||
style = me._resolveDataElementOptions(meta.data[index] || {}, index);
|
||
}
|
||
|
||
if (style.fill === false || style.fill === null) {
|
||
style.backgroundColor = style.borderColor;
|
||
}
|
||
|
||
return style;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDatasetElementOptions: function(element, hover) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var datasetOpts = me._config;
|
||
var custom = element.custom || {};
|
||
var options = chart.options.elements[me.datasetElementType.prototype._type] || {};
|
||
var elementOptions = me._datasetElementOptions;
|
||
var values = {};
|
||
var i, ilen, key, readKey;
|
||
|
||
// Scriptable options
|
||
var context = {
|
||
chart: chart,
|
||
dataset: me.getDataset(),
|
||
datasetIndex: me.index,
|
||
hover: hover
|
||
};
|
||
|
||
for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
|
||
key = elementOptions[i];
|
||
readKey = hover ? 'hover' + key.charAt(0).toUpperCase() + key.slice(1) : key;
|
||
values[key] = resolve([
|
||
custom[readKey],
|
||
datasetOpts[readKey],
|
||
options[readKey]
|
||
], context);
|
||
}
|
||
|
||
return values;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDataElementOptions: function(element, index) {
|
||
var me = this;
|
||
var custom = element && element.custom;
|
||
var cached = me._cachedDataOpts;
|
||
if (cached && !custom) {
|
||
return cached;
|
||
}
|
||
var chart = me.chart;
|
||
var datasetOpts = me._config;
|
||
var options = chart.options.elements[me.dataElementType.prototype._type] || {};
|
||
var elementOptions = me._dataElementOptions;
|
||
var values = {};
|
||
|
||
// Scriptable options
|
||
var context = {
|
||
chart: chart,
|
||
dataIndex: index,
|
||
dataset: me.getDataset(),
|
||
datasetIndex: me.index
|
||
};
|
||
|
||
// `resolve` sets cacheable to `false` if any option is indexed or scripted
|
||
var info = {cacheable: !custom};
|
||
|
||
var keys, i, ilen, key;
|
||
|
||
custom = custom || {};
|
||
|
||
if (helpers$1.isArray(elementOptions)) {
|
||
for (i = 0, ilen = elementOptions.length; i < ilen; ++i) {
|
||
key = elementOptions[i];
|
||
values[key] = resolve([
|
||
custom[key],
|
||
datasetOpts[key],
|
||
options[key]
|
||
], context, index, info);
|
||
}
|
||
} else {
|
||
keys = Object.keys(elementOptions);
|
||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||
key = keys[i];
|
||
values[key] = resolve([
|
||
custom[key],
|
||
datasetOpts[elementOptions[key]],
|
||
datasetOpts[key],
|
||
options[key]
|
||
], context, index, info);
|
||
}
|
||
}
|
||
|
||
if (info.cacheable) {
|
||
me._cachedDataOpts = Object.freeze(values);
|
||
}
|
||
|
||
return values;
|
||
},
|
||
|
||
removeHoverStyle: function(element) {
|
||
helpers$1.merge(element._model, element.$previousStyle || {});
|
||
delete element.$previousStyle;
|
||
},
|
||
|
||
setHoverStyle: function(element) {
|
||
var dataset = this.chart.data.datasets[element._datasetIndex];
|
||
var index = element._index;
|
||
var custom = element.custom || {};
|
||
var model = element._model;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
|
||
element.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth
|
||
};
|
||
|
||
model.backgroundColor = resolve([custom.hoverBackgroundColor, dataset.hoverBackgroundColor, getHoverColor(model.backgroundColor)], undefined, index);
|
||
model.borderColor = resolve([custom.hoverBorderColor, dataset.hoverBorderColor, getHoverColor(model.borderColor)], undefined, index);
|
||
model.borderWidth = resolve([custom.hoverBorderWidth, dataset.hoverBorderWidth, model.borderWidth], undefined, index);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_removeDatasetHoverStyle: function() {
|
||
var element = this.getMeta().dataset;
|
||
|
||
if (element) {
|
||
this.removeHoverStyle(element);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_setDatasetHoverStyle: function() {
|
||
var element = this.getMeta().dataset;
|
||
var prev = {};
|
||
var i, ilen, key, keys, hoverOptions, model;
|
||
|
||
if (!element) {
|
||
return;
|
||
}
|
||
|
||
model = element._model;
|
||
hoverOptions = this._resolveDatasetElementOptions(element, true);
|
||
|
||
keys = Object.keys(hoverOptions);
|
||
for (i = 0, ilen = keys.length; i < ilen; ++i) {
|
||
key = keys[i];
|
||
prev[key] = model[key];
|
||
model[key] = hoverOptions[key];
|
||
}
|
||
|
||
element.$previousStyle = prev;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
resyncElements: function() {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var data = me.getDataset().data;
|
||
var numMeta = meta.data.length;
|
||
var numData = data.length;
|
||
|
||
if (numData < numMeta) {
|
||
meta.data.splice(numData, numMeta - numData);
|
||
} else if (numData > numMeta) {
|
||
me.insertElements(numMeta, numData - numMeta);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
insertElements: function(start, count) {
|
||
for (var i = 0; i < count; ++i) {
|
||
this.addElementAndReset(start + i);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
onDataPush: function() {
|
||
var count = arguments.length;
|
||
this.insertElements(this.getDataset().data.length - count, count);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
onDataPop: function() {
|
||
this.getMeta().data.pop();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
onDataShift: function() {
|
||
this.getMeta().data.shift();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
onDataSplice: function(start, count) {
|
||
this.getMeta().data.splice(start, count);
|
||
this.insertElements(start, arguments.length - 2);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
onDataUnshift: function() {
|
||
this.insertElements(0, arguments.length);
|
||
}
|
||
});
|
||
|
||
DatasetController.extend = helpers$1.inherits;
|
||
|
||
var core_datasetController = DatasetController;
|
||
|
||
var TAU = Math.PI * 2;
|
||
|
||
core_defaults._set('global', {
|
||
elements: {
|
||
arc: {
|
||
backgroundColor: core_defaults.global.defaultColor,
|
||
borderColor: '#fff',
|
||
borderWidth: 2,
|
||
borderAlign: 'center'
|
||
}
|
||
}
|
||
});
|
||
|
||
function clipArc(ctx, arc) {
|
||
var startAngle = arc.startAngle;
|
||
var endAngle = arc.endAngle;
|
||
var pixelMargin = arc.pixelMargin;
|
||
var angleMargin = pixelMargin / arc.outerRadius;
|
||
var x = arc.x;
|
||
var y = arc.y;
|
||
|
||
// Draw an inner border by cliping the arc and drawing a double-width border
|
||
// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
|
||
ctx.beginPath();
|
||
ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin);
|
||
if (arc.innerRadius > pixelMargin) {
|
||
angleMargin = pixelMargin / arc.innerRadius;
|
||
ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true);
|
||
} else {
|
||
ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);
|
||
}
|
||
ctx.closePath();
|
||
ctx.clip();
|
||
}
|
||
|
||
function drawFullCircleBorders(ctx, vm, arc, inner) {
|
||
var endAngle = arc.endAngle;
|
||
var i;
|
||
|
||
if (inner) {
|
||
arc.endAngle = arc.startAngle + TAU;
|
||
clipArc(ctx, arc);
|
||
arc.endAngle = endAngle;
|
||
if (arc.endAngle === arc.startAngle && arc.fullCircles) {
|
||
arc.endAngle += TAU;
|
||
arc.fullCircles--;
|
||
}
|
||
}
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true);
|
||
for (i = 0; i < arc.fullCircles; ++i) {
|
||
ctx.stroke();
|
||
}
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU);
|
||
for (i = 0; i < arc.fullCircles; ++i) {
|
||
ctx.stroke();
|
||
}
|
||
}
|
||
|
||
function drawBorder(ctx, vm, arc) {
|
||
var inner = vm.borderAlign === 'inner';
|
||
|
||
if (inner) {
|
||
ctx.lineWidth = vm.borderWidth * 2;
|
||
ctx.lineJoin = 'round';
|
||
} else {
|
||
ctx.lineWidth = vm.borderWidth;
|
||
ctx.lineJoin = 'bevel';
|
||
}
|
||
|
||
if (arc.fullCircles) {
|
||
drawFullCircleBorders(ctx, vm, arc, inner);
|
||
}
|
||
|
||
if (inner) {
|
||
clipArc(ctx, arc);
|
||
}
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle);
|
||
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
||
ctx.closePath();
|
||
ctx.stroke();
|
||
}
|
||
|
||
var element_arc = core_element.extend({
|
||
_type: 'arc',
|
||
|
||
inLabelRange: function(mouseX) {
|
||
var vm = this._view;
|
||
|
||
if (vm) {
|
||
return (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));
|
||
}
|
||
return false;
|
||
},
|
||
|
||
inRange: function(chartX, chartY) {
|
||
var vm = this._view;
|
||
|
||
if (vm) {
|
||
var pointRelativePosition = helpers$1.getAngleFromPoint(vm, {x: chartX, y: chartY});
|
||
var angle = pointRelativePosition.angle;
|
||
var distance = pointRelativePosition.distance;
|
||
|
||
// Sanitise angle range
|
||
var startAngle = vm.startAngle;
|
||
var endAngle = vm.endAngle;
|
||
while (endAngle < startAngle) {
|
||
endAngle += TAU;
|
||
}
|
||
while (angle > endAngle) {
|
||
angle -= TAU;
|
||
}
|
||
while (angle < startAngle) {
|
||
angle += TAU;
|
||
}
|
||
|
||
// Check if within the range of the open/close angle
|
||
var betweenAngles = (angle >= startAngle && angle <= endAngle);
|
||
var withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);
|
||
|
||
return (betweenAngles && withinRadius);
|
||
}
|
||
return false;
|
||
},
|
||
|
||
getCenterPoint: function() {
|
||
var vm = this._view;
|
||
var halfAngle = (vm.startAngle + vm.endAngle) / 2;
|
||
var halfRadius = (vm.innerRadius + vm.outerRadius) / 2;
|
||
return {
|
||
x: vm.x + Math.cos(halfAngle) * halfRadius,
|
||
y: vm.y + Math.sin(halfAngle) * halfRadius
|
||
};
|
||
},
|
||
|
||
getArea: function() {
|
||
var vm = this._view;
|
||
return Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));
|
||
},
|
||
|
||
tooltipPosition: function() {
|
||
var vm = this._view;
|
||
var centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);
|
||
var rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;
|
||
|
||
return {
|
||
x: vm.x + (Math.cos(centreAngle) * rangeFromCentre),
|
||
y: vm.y + (Math.sin(centreAngle) * rangeFromCentre)
|
||
};
|
||
},
|
||
|
||
draw: function() {
|
||
var ctx = this._chart.ctx;
|
||
var vm = this._view;
|
||
var pixelMargin = (vm.borderAlign === 'inner') ? 0.33 : 0;
|
||
var arc = {
|
||
x: vm.x,
|
||
y: vm.y,
|
||
innerRadius: vm.innerRadius,
|
||
outerRadius: Math.max(vm.outerRadius - pixelMargin, 0),
|
||
pixelMargin: pixelMargin,
|
||
startAngle: vm.startAngle,
|
||
endAngle: vm.endAngle,
|
||
fullCircles: Math.floor(vm.circumference / TAU)
|
||
};
|
||
var i;
|
||
|
||
ctx.save();
|
||
|
||
ctx.fillStyle = vm.backgroundColor;
|
||
ctx.strokeStyle = vm.borderColor;
|
||
|
||
if (arc.fullCircles) {
|
||
arc.endAngle = arc.startAngle + TAU;
|
||
ctx.beginPath();
|
||
ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
|
||
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
||
ctx.closePath();
|
||
for (i = 0; i < arc.fullCircles; ++i) {
|
||
ctx.fill();
|
||
}
|
||
arc.endAngle = arc.startAngle + vm.circumference % TAU;
|
||
}
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
|
||
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
|
||
if (vm.borderWidth) {
|
||
drawBorder(ctx, vm, arc);
|
||
}
|
||
|
||
ctx.restore();
|
||
}
|
||
});
|
||
|
||
var valueOrDefault$1 = helpers$1.valueOrDefault;
|
||
|
||
var defaultColor = core_defaults.global.defaultColor;
|
||
|
||
core_defaults._set('global', {
|
||
elements: {
|
||
line: {
|
||
tension: 0.4,
|
||
backgroundColor: defaultColor,
|
||
borderWidth: 3,
|
||
borderColor: defaultColor,
|
||
borderCapStyle: 'butt',
|
||
borderDash: [],
|
||
borderDashOffset: 0.0,
|
||
borderJoinStyle: 'miter',
|
||
capBezierPoints: true,
|
||
fill: true, // do we fill in the area between the line and its base axis
|
||
}
|
||
}
|
||
});
|
||
|
||
var element_line = core_element.extend({
|
||
_type: 'line',
|
||
|
||
draw: function() {
|
||
var me = this;
|
||
var vm = me._view;
|
||
var ctx = me._chart.ctx;
|
||
var spanGaps = vm.spanGaps;
|
||
var points = me._children.slice(); // clone array
|
||
var globalDefaults = core_defaults.global;
|
||
var globalOptionLineElements = globalDefaults.elements.line;
|
||
var lastDrawnIndex = -1;
|
||
var closePath = me._loop;
|
||
var index, previous, currentVM;
|
||
|
||
if (!points.length) {
|
||
return;
|
||
}
|
||
|
||
if (me._loop) {
|
||
for (index = 0; index < points.length; ++index) {
|
||
previous = helpers$1.previousItem(points, index);
|
||
// If the line has an open path, shift the point array
|
||
if (!points[index]._view.skip && previous._view.skip) {
|
||
points = points.slice(index).concat(points.slice(0, index));
|
||
closePath = spanGaps;
|
||
break;
|
||
}
|
||
}
|
||
// If the line has a close path, add the first point again
|
||
if (closePath) {
|
||
points.push(points[0]);
|
||
}
|
||
}
|
||
|
||
ctx.save();
|
||
|
||
// Stroke Line Options
|
||
ctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;
|
||
|
||
// IE 9 and 10 do not support line dash
|
||
if (ctx.setLineDash) {
|
||
ctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);
|
||
}
|
||
|
||
ctx.lineDashOffset = valueOrDefault$1(vm.borderDashOffset, globalOptionLineElements.borderDashOffset);
|
||
ctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;
|
||
ctx.lineWidth = valueOrDefault$1(vm.borderWidth, globalOptionLineElements.borderWidth);
|
||
ctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;
|
||
|
||
// Stroke Line
|
||
ctx.beginPath();
|
||
|
||
// First point moves to it's starting position no matter what
|
||
currentVM = points[0]._view;
|
||
if (!currentVM.skip) {
|
||
ctx.moveTo(currentVM.x, currentVM.y);
|
||
lastDrawnIndex = 0;
|
||
}
|
||
|
||
for (index = 1; index < points.length; ++index) {
|
||
currentVM = points[index]._view;
|
||
previous = lastDrawnIndex === -1 ? helpers$1.previousItem(points, index) : points[lastDrawnIndex];
|
||
|
||
if (!currentVM.skip) {
|
||
if ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {
|
||
// There was a gap and this is the first point after the gap
|
||
ctx.moveTo(currentVM.x, currentVM.y);
|
||
} else {
|
||
// Line to next point
|
||
helpers$1.canvas.lineTo(ctx, previous._view, currentVM);
|
||
}
|
||
lastDrawnIndex = index;
|
||
}
|
||
}
|
||
|
||
if (closePath) {
|
||
ctx.closePath();
|
||
}
|
||
|
||
ctx.stroke();
|
||
ctx.restore();
|
||
}
|
||
});
|
||
|
||
var valueOrDefault$2 = helpers$1.valueOrDefault;
|
||
|
||
var defaultColor$1 = core_defaults.global.defaultColor;
|
||
|
||
core_defaults._set('global', {
|
||
elements: {
|
||
point: {
|
||
radius: 3,
|
||
pointStyle: 'circle',
|
||
backgroundColor: defaultColor$1,
|
||
borderColor: defaultColor$1,
|
||
borderWidth: 1,
|
||
// Hover
|
||
hitRadius: 1,
|
||
hoverRadius: 4,
|
||
hoverBorderWidth: 1
|
||
}
|
||
}
|
||
});
|
||
|
||
function xRange(mouseX) {
|
||
var vm = this._view;
|
||
return vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;
|
||
}
|
||
|
||
function yRange(mouseY) {
|
||
var vm = this._view;
|
||
return vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;
|
||
}
|
||
|
||
var element_point = core_element.extend({
|
||
_type: 'point',
|
||
|
||
inRange: function(mouseX, mouseY) {
|
||
var vm = this._view;
|
||
return vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;
|
||
},
|
||
|
||
inLabelRange: xRange,
|
||
inXRange: xRange,
|
||
inYRange: yRange,
|
||
|
||
getCenterPoint: function() {
|
||
var vm = this._view;
|
||
return {
|
||
x: vm.x,
|
||
y: vm.y
|
||
};
|
||
},
|
||
|
||
getArea: function() {
|
||
return Math.PI * Math.pow(this._view.radius, 2);
|
||
},
|
||
|
||
tooltipPosition: function() {
|
||
var vm = this._view;
|
||
return {
|
||
x: vm.x,
|
||
y: vm.y,
|
||
padding: vm.radius + vm.borderWidth
|
||
};
|
||
},
|
||
|
||
draw: function(chartArea) {
|
||
var vm = this._view;
|
||
var ctx = this._chart.ctx;
|
||
var pointStyle = vm.pointStyle;
|
||
var rotation = vm.rotation;
|
||
var radius = vm.radius;
|
||
var x = vm.x;
|
||
var y = vm.y;
|
||
var globalDefaults = core_defaults.global;
|
||
var defaultColor = globalDefaults.defaultColor; // eslint-disable-line no-shadow
|
||
|
||
if (vm.skip) {
|
||
return;
|
||
}
|
||
|
||
// Clipping for Points.
|
||
if (chartArea === undefined || helpers$1.canvas._isPointInArea(vm, chartArea)) {
|
||
ctx.strokeStyle = vm.borderColor || defaultColor;
|
||
ctx.lineWidth = valueOrDefault$2(vm.borderWidth, globalDefaults.elements.point.borderWidth);
|
||
ctx.fillStyle = vm.backgroundColor || defaultColor;
|
||
helpers$1.canvas.drawPoint(ctx, pointStyle, radius, x, y, rotation);
|
||
}
|
||
}
|
||
});
|
||
|
||
var defaultColor$2 = core_defaults.global.defaultColor;
|
||
|
||
core_defaults._set('global', {
|
||
elements: {
|
||
rectangle: {
|
||
backgroundColor: defaultColor$2,
|
||
borderColor: defaultColor$2,
|
||
borderSkipped: 'bottom',
|
||
borderWidth: 0
|
||
}
|
||
}
|
||
});
|
||
|
||
function isVertical(vm) {
|
||
return vm && vm.width !== undefined;
|
||
}
|
||
|
||
/**
|
||
* Helper function to get the bounds of the bar regardless of the orientation
|
||
* @param bar {Chart.Element.Rectangle} the bar
|
||
* @return {Bounds} bounds of the bar
|
||
* @private
|
||
*/
|
||
function getBarBounds(vm) {
|
||
var x1, x2, y1, y2, half;
|
||
|
||
if (isVertical(vm)) {
|
||
half = vm.width / 2;
|
||
x1 = vm.x - half;
|
||
x2 = vm.x + half;
|
||
y1 = Math.min(vm.y, vm.base);
|
||
y2 = Math.max(vm.y, vm.base);
|
||
} else {
|
||
half = vm.height / 2;
|
||
x1 = Math.min(vm.x, vm.base);
|
||
x2 = Math.max(vm.x, vm.base);
|
||
y1 = vm.y - half;
|
||
y2 = vm.y + half;
|
||
}
|
||
|
||
return {
|
||
left: x1,
|
||
top: y1,
|
||
right: x2,
|
||
bottom: y2
|
||
};
|
||
}
|
||
|
||
function swap(orig, v1, v2) {
|
||
return orig === v1 ? v2 : orig === v2 ? v1 : orig;
|
||
}
|
||
|
||
function parseBorderSkipped(vm) {
|
||
var edge = vm.borderSkipped;
|
||
var res = {};
|
||
|
||
if (!edge) {
|
||
return res;
|
||
}
|
||
|
||
if (vm.horizontal) {
|
||
if (vm.base > vm.x) {
|
||
edge = swap(edge, 'left', 'right');
|
||
}
|
||
} else if (vm.base < vm.y) {
|
||
edge = swap(edge, 'bottom', 'top');
|
||
}
|
||
|
||
res[edge] = true;
|
||
return res;
|
||
}
|
||
|
||
function parseBorderWidth(vm, maxW, maxH) {
|
||
var value = vm.borderWidth;
|
||
var skip = parseBorderSkipped(vm);
|
||
var t, r, b, l;
|
||
|
||
if (helpers$1.isObject(value)) {
|
||
t = +value.top || 0;
|
||
r = +value.right || 0;
|
||
b = +value.bottom || 0;
|
||
l = +value.left || 0;
|
||
} else {
|
||
t = r = b = l = +value || 0;
|
||
}
|
||
|
||
return {
|
||
t: skip.top || (t < 0) ? 0 : t > maxH ? maxH : t,
|
||
r: skip.right || (r < 0) ? 0 : r > maxW ? maxW : r,
|
||
b: skip.bottom || (b < 0) ? 0 : b > maxH ? maxH : b,
|
||
l: skip.left || (l < 0) ? 0 : l > maxW ? maxW : l
|
||
};
|
||
}
|
||
|
||
function boundingRects(vm) {
|
||
var bounds = getBarBounds(vm);
|
||
var width = bounds.right - bounds.left;
|
||
var height = bounds.bottom - bounds.top;
|
||
var border = parseBorderWidth(vm, width / 2, height / 2);
|
||
|
||
return {
|
||
outer: {
|
||
x: bounds.left,
|
||
y: bounds.top,
|
||
w: width,
|
||
h: height
|
||
},
|
||
inner: {
|
||
x: bounds.left + border.l,
|
||
y: bounds.top + border.t,
|
||
w: width - border.l - border.r,
|
||
h: height - border.t - border.b
|
||
}
|
||
};
|
||
}
|
||
|
||
function inRange(vm, x, y) {
|
||
var skipX = x === null;
|
||
var skipY = y === null;
|
||
var bounds = !vm || (skipX && skipY) ? false : getBarBounds(vm);
|
||
|
||
return bounds
|
||
&& (skipX || x >= bounds.left && x <= bounds.right)
|
||
&& (skipY || y >= bounds.top && y <= bounds.bottom);
|
||
}
|
||
|
||
var element_rectangle = core_element.extend({
|
||
_type: 'rectangle',
|
||
|
||
draw: function() {
|
||
var ctx = this._chart.ctx;
|
||
var vm = this._view;
|
||
var rects = boundingRects(vm);
|
||
var outer = rects.outer;
|
||
var inner = rects.inner;
|
||
|
||
ctx.fillStyle = vm.backgroundColor;
|
||
ctx.fillRect(outer.x, outer.y, outer.w, outer.h);
|
||
|
||
if (outer.w === inner.w && outer.h === inner.h) {
|
||
return;
|
||
}
|
||
|
||
ctx.save();
|
||
ctx.beginPath();
|
||
ctx.rect(outer.x, outer.y, outer.w, outer.h);
|
||
ctx.clip();
|
||
ctx.fillStyle = vm.borderColor;
|
||
ctx.rect(inner.x, inner.y, inner.w, inner.h);
|
||
ctx.fill('evenodd');
|
||
ctx.restore();
|
||
},
|
||
|
||
height: function() {
|
||
var vm = this._view;
|
||
return vm.base - vm.y;
|
||
},
|
||
|
||
inRange: function(mouseX, mouseY) {
|
||
return inRange(this._view, mouseX, mouseY);
|
||
},
|
||
|
||
inLabelRange: function(mouseX, mouseY) {
|
||
var vm = this._view;
|
||
return isVertical(vm)
|
||
? inRange(vm, mouseX, null)
|
||
: inRange(vm, null, mouseY);
|
||
},
|
||
|
||
inXRange: function(mouseX) {
|
||
return inRange(this._view, mouseX, null);
|
||
},
|
||
|
||
inYRange: function(mouseY) {
|
||
return inRange(this._view, null, mouseY);
|
||
},
|
||
|
||
getCenterPoint: function() {
|
||
var vm = this._view;
|
||
var x, y;
|
||
if (isVertical(vm)) {
|
||
x = vm.x;
|
||
y = (vm.y + vm.base) / 2;
|
||
} else {
|
||
x = (vm.x + vm.base) / 2;
|
||
y = vm.y;
|
||
}
|
||
|
||
return {x: x, y: y};
|
||
},
|
||
|
||
getArea: function() {
|
||
var vm = this._view;
|
||
|
||
return isVertical(vm)
|
||
? vm.width * Math.abs(vm.y - vm.base)
|
||
: vm.height * Math.abs(vm.x - vm.base);
|
||
},
|
||
|
||
tooltipPosition: function() {
|
||
var vm = this._view;
|
||
return {
|
||
x: vm.x,
|
||
y: vm.y
|
||
};
|
||
}
|
||
});
|
||
|
||
var elements = {};
|
||
var Arc = element_arc;
|
||
var Line = element_line;
|
||
var Point = element_point;
|
||
var Rectangle = element_rectangle;
|
||
elements.Arc = Arc;
|
||
elements.Line = Line;
|
||
elements.Point = Point;
|
||
elements.Rectangle = Rectangle;
|
||
|
||
var deprecated = helpers$1._deprecated;
|
||
var valueOrDefault$3 = helpers$1.valueOrDefault;
|
||
|
||
core_defaults._set('bar', {
|
||
hover: {
|
||
mode: 'label'
|
||
},
|
||
|
||
scales: {
|
||
xAxes: [{
|
||
type: 'category',
|
||
offset: true,
|
||
gridLines: {
|
||
offsetGridLines: true
|
||
}
|
||
}],
|
||
|
||
yAxes: [{
|
||
type: 'linear'
|
||
}]
|
||
}
|
||
});
|
||
|
||
core_defaults._set('global', {
|
||
datasets: {
|
||
bar: {
|
||
categoryPercentage: 0.8,
|
||
barPercentage: 0.9
|
||
}
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Computes the "optimal" sample size to maintain bars equally sized while preventing overlap.
|
||
* @private
|
||
*/
|
||
function computeMinSampleSize(scale, pixels) {
|
||
var min = scale._length;
|
||
var prev, curr, i, ilen;
|
||
|
||
for (i = 1, ilen = pixels.length; i < ilen; ++i) {
|
||
min = Math.min(min, Math.abs(pixels[i] - pixels[i - 1]));
|
||
}
|
||
|
||
for (i = 0, ilen = scale.getTicks().length; i < ilen; ++i) {
|
||
curr = scale.getPixelForTick(i);
|
||
min = i > 0 ? Math.min(min, Math.abs(curr - prev)) : min;
|
||
prev = curr;
|
||
}
|
||
|
||
return min;
|
||
}
|
||
|
||
/**
|
||
* Computes an "ideal" category based on the absolute bar thickness or, if undefined or null,
|
||
* uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This
|
||
* mode currently always generates bars equally sized (until we introduce scriptable options?).
|
||
* @private
|
||
*/
|
||
function computeFitCategoryTraits(index, ruler, options) {
|
||
var thickness = options.barThickness;
|
||
var count = ruler.stackCount;
|
||
var curr = ruler.pixels[index];
|
||
var min = helpers$1.isNullOrUndef(thickness)
|
||
? computeMinSampleSize(ruler.scale, ruler.pixels)
|
||
: -1;
|
||
var size, ratio;
|
||
|
||
if (helpers$1.isNullOrUndef(thickness)) {
|
||
size = min * options.categoryPercentage;
|
||
ratio = options.barPercentage;
|
||
} else {
|
||
// When bar thickness is enforced, category and bar percentages are ignored.
|
||
// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')
|
||
// and deprecate barPercentage since this value is ignored when thickness is absolute.
|
||
size = thickness * count;
|
||
ratio = 1;
|
||
}
|
||
|
||
return {
|
||
chunk: size / count,
|
||
ratio: ratio,
|
||
start: curr - (size / 2)
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Computes an "optimal" category that globally arranges bars side by side (no gap when
|
||
* percentage options are 1), based on the previous and following categories. This mode
|
||
* generates bars with different widths when data are not evenly spaced.
|
||
* @private
|
||
*/
|
||
function computeFlexCategoryTraits(index, ruler, options) {
|
||
var pixels = ruler.pixels;
|
||
var curr = pixels[index];
|
||
var prev = index > 0 ? pixels[index - 1] : null;
|
||
var next = index < pixels.length - 1 ? pixels[index + 1] : null;
|
||
var percent = options.categoryPercentage;
|
||
var start, size;
|
||
|
||
if (prev === null) {
|
||
// first data: its size is double based on the next point or,
|
||
// if it's also the last data, we use the scale size.
|
||
prev = curr - (next === null ? ruler.end - ruler.start : next - curr);
|
||
}
|
||
|
||
if (next === null) {
|
||
// last data: its size is also double based on the previous point.
|
||
next = curr + curr - prev;
|
||
}
|
||
|
||
start = curr - (curr - Math.min(prev, next)) / 2 * percent;
|
||
size = Math.abs(next - prev) / 2 * percent;
|
||
|
||
return {
|
||
chunk: size / ruler.stackCount,
|
||
ratio: options.barPercentage,
|
||
start: start
|
||
};
|
||
}
|
||
|
||
var controller_bar = core_datasetController.extend({
|
||
|
||
dataElementType: elements.Rectangle,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: [
|
||
'backgroundColor',
|
||
'borderColor',
|
||
'borderSkipped',
|
||
'borderWidth',
|
||
'barPercentage',
|
||
'barThickness',
|
||
'categoryPercentage',
|
||
'maxBarThickness',
|
||
'minBarLength'
|
||
],
|
||
|
||
initialize: function() {
|
||
var me = this;
|
||
var meta, scaleOpts;
|
||
|
||
core_datasetController.prototype.initialize.apply(me, arguments);
|
||
|
||
meta = me.getMeta();
|
||
meta.stack = me.getDataset().stack;
|
||
meta.bar = true;
|
||
|
||
scaleOpts = me._getIndexScale().options;
|
||
deprecated('bar chart', scaleOpts.barPercentage, 'scales.[x/y]Axes.barPercentage', 'dataset.barPercentage');
|
||
deprecated('bar chart', scaleOpts.barThickness, 'scales.[x/y]Axes.barThickness', 'dataset.barThickness');
|
||
deprecated('bar chart', scaleOpts.categoryPercentage, 'scales.[x/y]Axes.categoryPercentage', 'dataset.categoryPercentage');
|
||
deprecated('bar chart', me._getValueScale().options.minBarLength, 'scales.[x/y]Axes.minBarLength', 'dataset.minBarLength');
|
||
deprecated('bar chart', scaleOpts.maxBarThickness, 'scales.[x/y]Axes.maxBarThickness', 'dataset.maxBarThickness');
|
||
},
|
||
|
||
update: function(reset) {
|
||
var me = this;
|
||
var rects = me.getMeta().data;
|
||
var i, ilen;
|
||
|
||
me._ruler = me.getRuler();
|
||
|
||
for (i = 0, ilen = rects.length; i < ilen; ++i) {
|
||
me.updateElement(rects[i], i, reset);
|
||
}
|
||
},
|
||
|
||
updateElement: function(rectangle, index, reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var dataset = me.getDataset();
|
||
var options = me._resolveDataElementOptions(rectangle, index);
|
||
|
||
rectangle._xScale = me.getScaleForId(meta.xAxisID);
|
||
rectangle._yScale = me.getScaleForId(meta.yAxisID);
|
||
rectangle._datasetIndex = me.index;
|
||
rectangle._index = index;
|
||
rectangle._model = {
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderSkipped: options.borderSkipped,
|
||
borderWidth: options.borderWidth,
|
||
datasetLabel: dataset.label,
|
||
label: me.chart.data.labels[index]
|
||
};
|
||
|
||
if (helpers$1.isArray(dataset.data[index])) {
|
||
rectangle._model.borderSkipped = null;
|
||
}
|
||
|
||
me._updateElementGeometry(rectangle, index, reset, options);
|
||
|
||
rectangle.pivot();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_updateElementGeometry: function(rectangle, index, reset, options) {
|
||
var me = this;
|
||
var model = rectangle._model;
|
||
var vscale = me._getValueScale();
|
||
var base = vscale.getBasePixel();
|
||
var horizontal = vscale.isHorizontal();
|
||
var ruler = me._ruler || me.getRuler();
|
||
var vpixels = me.calculateBarValuePixels(me.index, index, options);
|
||
var ipixels = me.calculateBarIndexPixels(me.index, index, ruler, options);
|
||
|
||
model.horizontal = horizontal;
|
||
model.base = reset ? base : vpixels.base;
|
||
model.x = horizontal ? reset ? base : vpixels.head : ipixels.center;
|
||
model.y = horizontal ? ipixels.center : reset ? base : vpixels.head;
|
||
model.height = horizontal ? ipixels.size : undefined;
|
||
model.width = horizontal ? undefined : ipixels.size;
|
||
},
|
||
|
||
/**
|
||
* Returns the stacks based on groups and bar visibility.
|
||
* @param {number} [last] - The dataset index
|
||
* @returns {string[]} The list of stack IDs
|
||
* @private
|
||
*/
|
||
_getStacks: function(last) {
|
||
var me = this;
|
||
var scale = me._getIndexScale();
|
||
var metasets = scale._getMatchingVisibleMetas(me._type);
|
||
var stacked = scale.options.stacked;
|
||
var ilen = metasets.length;
|
||
var stacks = [];
|
||
var i, meta;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
meta = metasets[i];
|
||
// stacked | meta.stack
|
||
// | found | not found | undefined
|
||
// false | x | x | x
|
||
// true | | x |
|
||
// undefined | | x | x
|
||
if (stacked === false || stacks.indexOf(meta.stack) === -1 ||
|
||
(stacked === undefined && meta.stack === undefined)) {
|
||
stacks.push(meta.stack);
|
||
}
|
||
if (meta.index === last) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
return stacks;
|
||
},
|
||
|
||
/**
|
||
* Returns the effective number of stacks based on groups and bar visibility.
|
||
* @private
|
||
*/
|
||
getStackCount: function() {
|
||
return this._getStacks().length;
|
||
},
|
||
|
||
/**
|
||
* Returns the stack index for the given dataset based on groups and bar visibility.
|
||
* @param {number} [datasetIndex] - The dataset index
|
||
* @param {string} [name] - The stack name to find
|
||
* @returns {number} The stack index
|
||
* @private
|
||
*/
|
||
getStackIndex: function(datasetIndex, name) {
|
||
var stacks = this._getStacks(datasetIndex);
|
||
var index = (name !== undefined)
|
||
? stacks.indexOf(name)
|
||
: -1; // indexOf returns -1 if element is not present
|
||
|
||
return (index === -1)
|
||
? stacks.length - 1
|
||
: index;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
getRuler: function() {
|
||
var me = this;
|
||
var scale = me._getIndexScale();
|
||
var pixels = [];
|
||
var i, ilen;
|
||
|
||
for (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {
|
||
pixels.push(scale.getPixelForValue(null, i, me.index));
|
||
}
|
||
|
||
return {
|
||
pixels: pixels,
|
||
start: scale._startPixel,
|
||
end: scale._endPixel,
|
||
stackCount: me.getStackCount(),
|
||
scale: scale
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Note: pixel values are not clamped to the scale area.
|
||
* @private
|
||
*/
|
||
calculateBarValuePixels: function(datasetIndex, index, options) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var scale = me._getValueScale();
|
||
var isHorizontal = scale.isHorizontal();
|
||
var datasets = chart.data.datasets;
|
||
var metasets = scale._getMatchingVisibleMetas(me._type);
|
||
var value = scale._parseValue(datasets[datasetIndex].data[index]);
|
||
var minBarLength = options.minBarLength;
|
||
var stacked = scale.options.stacked;
|
||
var stack = me.getMeta().stack;
|
||
var start = value.start === undefined ? 0 : value.max >= 0 && value.min >= 0 ? value.min : value.max;
|
||
var length = value.start === undefined ? value.end : value.max >= 0 && value.min >= 0 ? value.max - value.min : value.min - value.max;
|
||
var ilen = metasets.length;
|
||
var i, imeta, ivalue, base, head, size, stackLength;
|
||
|
||
if (stacked || (stacked === undefined && stack !== undefined)) {
|
||
for (i = 0; i < ilen; ++i) {
|
||
imeta = metasets[i];
|
||
|
||
if (imeta.index === datasetIndex) {
|
||
break;
|
||
}
|
||
|
||
if (imeta.stack === stack) {
|
||
stackLength = scale._parseValue(datasets[imeta.index].data[index]);
|
||
ivalue = stackLength.start === undefined ? stackLength.end : stackLength.min >= 0 && stackLength.max >= 0 ? stackLength.max : stackLength.min;
|
||
|
||
if ((value.min < 0 && ivalue < 0) || (value.max >= 0 && ivalue > 0)) {
|
||
start += ivalue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
base = scale.getPixelForValue(start);
|
||
head = scale.getPixelForValue(start + length);
|
||
size = head - base;
|
||
|
||
if (minBarLength !== undefined && Math.abs(size) < minBarLength) {
|
||
size = minBarLength;
|
||
if (length >= 0 && !isHorizontal || length < 0 && isHorizontal) {
|
||
head = base - minBarLength;
|
||
} else {
|
||
head = base + minBarLength;
|
||
}
|
||
}
|
||
|
||
return {
|
||
size: size,
|
||
base: base,
|
||
head: head,
|
||
center: head + size / 2
|
||
};
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
calculateBarIndexPixels: function(datasetIndex, index, ruler, options) {
|
||
var me = this;
|
||
var range = options.barThickness === 'flex'
|
||
? computeFlexCategoryTraits(index, ruler, options)
|
||
: computeFitCategoryTraits(index, ruler, options);
|
||
|
||
var stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);
|
||
var center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);
|
||
var size = Math.min(
|
||
valueOrDefault$3(options.maxBarThickness, Infinity),
|
||
range.chunk * range.ratio);
|
||
|
||
return {
|
||
base: center - size / 2,
|
||
head: center + size / 2,
|
||
center: center,
|
||
size: size
|
||
};
|
||
},
|
||
|
||
draw: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var scale = me._getValueScale();
|
||
var rects = me.getMeta().data;
|
||
var dataset = me.getDataset();
|
||
var ilen = rects.length;
|
||
var i = 0;
|
||
|
||
helpers$1.canvas.clipArea(chart.ctx, chart.chartArea);
|
||
|
||
for (; i < ilen; ++i) {
|
||
var val = scale._parseValue(dataset.data[i]);
|
||
if (!isNaN(val.min) && !isNaN(val.max)) {
|
||
rects[i].draw();
|
||
}
|
||
}
|
||
|
||
helpers$1.canvas.unclipArea(chart.ctx);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDataElementOptions: function() {
|
||
var me = this;
|
||
var values = helpers$1.extend({}, core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments));
|
||
var indexOpts = me._getIndexScale().options;
|
||
var valueOpts = me._getValueScale().options;
|
||
|
||
values.barPercentage = valueOrDefault$3(indexOpts.barPercentage, values.barPercentage);
|
||
values.barThickness = valueOrDefault$3(indexOpts.barThickness, values.barThickness);
|
||
values.categoryPercentage = valueOrDefault$3(indexOpts.categoryPercentage, values.categoryPercentage);
|
||
values.maxBarThickness = valueOrDefault$3(indexOpts.maxBarThickness, values.maxBarThickness);
|
||
values.minBarLength = valueOrDefault$3(valueOpts.minBarLength, values.minBarLength);
|
||
|
||
return values;
|
||
}
|
||
|
||
});
|
||
|
||
var valueOrDefault$4 = helpers$1.valueOrDefault;
|
||
var resolve$1 = helpers$1.options.resolve;
|
||
|
||
core_defaults._set('bubble', {
|
||
hover: {
|
||
mode: 'single'
|
||
},
|
||
|
||
scales: {
|
||
xAxes: [{
|
||
type: 'linear', // bubble should probably use a linear scale by default
|
||
position: 'bottom',
|
||
id: 'x-axis-0' // need an ID so datasets can reference the scale
|
||
}],
|
||
yAxes: [{
|
||
type: 'linear',
|
||
position: 'left',
|
||
id: 'y-axis-0'
|
||
}]
|
||
},
|
||
|
||
tooltips: {
|
||
callbacks: {
|
||
title: function() {
|
||
// Title doesn't make sense for scatter since we format the data as a point
|
||
return '';
|
||
},
|
||
label: function(item, data) {
|
||
var datasetLabel = data.datasets[item.datasetIndex].label || '';
|
||
var dataPoint = data.datasets[item.datasetIndex].data[item.index];
|
||
return datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
var controller_bubble = core_datasetController.extend({
|
||
/**
|
||
* @protected
|
||
*/
|
||
dataElementType: elements.Point,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: [
|
||
'backgroundColor',
|
||
'borderColor',
|
||
'borderWidth',
|
||
'hoverBackgroundColor',
|
||
'hoverBorderColor',
|
||
'hoverBorderWidth',
|
||
'hoverRadius',
|
||
'hitRadius',
|
||
'pointStyle',
|
||
'rotation'
|
||
],
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
update: function(reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var points = meta.data;
|
||
|
||
// Update Points
|
||
helpers$1.each(points, function(point, index) {
|
||
me.updateElement(point, index, reset);
|
||
});
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
updateElement: function(point, index, reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var custom = point.custom || {};
|
||
var xScale = me.getScaleForId(meta.xAxisID);
|
||
var yScale = me.getScaleForId(meta.yAxisID);
|
||
var options = me._resolveDataElementOptions(point, index);
|
||
var data = me.getDataset().data[index];
|
||
var dsIndex = me.index;
|
||
|
||
var x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);
|
||
var y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);
|
||
|
||
point._xScale = xScale;
|
||
point._yScale = yScale;
|
||
point._options = options;
|
||
point._datasetIndex = dsIndex;
|
||
point._index = index;
|
||
point._model = {
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderWidth: options.borderWidth,
|
||
hitRadius: options.hitRadius,
|
||
pointStyle: options.pointStyle,
|
||
rotation: options.rotation,
|
||
radius: reset ? 0 : options.radius,
|
||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||
x: x,
|
||
y: y,
|
||
};
|
||
|
||
point.pivot();
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
setHoverStyle: function(point) {
|
||
var model = point._model;
|
||
var options = point._options;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
|
||
point.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth,
|
||
radius: model.radius
|
||
};
|
||
|
||
model.backgroundColor = valueOrDefault$4(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||
model.borderColor = valueOrDefault$4(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||
model.borderWidth = valueOrDefault$4(options.hoverBorderWidth, options.borderWidth);
|
||
model.radius = options.radius + options.hoverRadius;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDataElementOptions: function(point, index) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var dataset = me.getDataset();
|
||
var custom = point.custom || {};
|
||
var data = dataset.data[index] || {};
|
||
var values = core_datasetController.prototype._resolveDataElementOptions.apply(me, arguments);
|
||
|
||
// Scriptable options
|
||
var context = {
|
||
chart: chart,
|
||
dataIndex: index,
|
||
dataset: dataset,
|
||
datasetIndex: me.index
|
||
};
|
||
|
||
// In case values were cached (and thus frozen), we need to clone the values
|
||
if (me._cachedDataOpts === values) {
|
||
values = helpers$1.extend({}, values);
|
||
}
|
||
|
||
// Custom radius resolution
|
||
values.radius = resolve$1([
|
||
custom.radius,
|
||
data.r,
|
||
me._config.radius,
|
||
chart.options.elements.point.radius
|
||
], context, index);
|
||
|
||
return values;
|
||
}
|
||
});
|
||
|
||
var valueOrDefault$5 = helpers$1.valueOrDefault;
|
||
|
||
var PI$1 = Math.PI;
|
||
var DOUBLE_PI$1 = PI$1 * 2;
|
||
var HALF_PI$1 = PI$1 / 2;
|
||
|
||
core_defaults._set('doughnut', {
|
||
animation: {
|
||
// Boolean - Whether we animate the rotation of the Doughnut
|
||
animateRotate: true,
|
||
// Boolean - Whether we animate scaling the Doughnut from the centre
|
||
animateScale: false
|
||
},
|
||
hover: {
|
||
mode: 'single'
|
||
},
|
||
legendCallback: function(chart) {
|
||
var list = document.createElement('ul');
|
||
var data = chart.data;
|
||
var datasets = data.datasets;
|
||
var labels = data.labels;
|
||
var i, ilen, listItem, listItemSpan;
|
||
|
||
list.setAttribute('class', chart.id + '-legend');
|
||
if (datasets.length) {
|
||
for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
|
||
listItem = list.appendChild(document.createElement('li'));
|
||
listItemSpan = listItem.appendChild(document.createElement('span'));
|
||
listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
|
||
if (labels[i]) {
|
||
listItem.appendChild(document.createTextNode(labels[i]));
|
||
}
|
||
}
|
||
}
|
||
|
||
return list.outerHTML;
|
||
},
|
||
legend: {
|
||
labels: {
|
||
generateLabels: function(chart) {
|
||
var data = chart.data;
|
||
if (data.labels.length && data.datasets.length) {
|
||
return data.labels.map(function(label, i) {
|
||
var meta = chart.getDatasetMeta(0);
|
||
var style = meta.controller.getStyle(i);
|
||
|
||
return {
|
||
text: label,
|
||
fillStyle: style.backgroundColor,
|
||
strokeStyle: style.borderColor,
|
||
lineWidth: style.borderWidth,
|
||
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
|
||
|
||
// Extra data used for toggling the correct item
|
||
index: i
|
||
};
|
||
});
|
||
}
|
||
return [];
|
||
}
|
||
},
|
||
|
||
onClick: function(e, legendItem) {
|
||
var index = legendItem.index;
|
||
var chart = this.chart;
|
||
var i, ilen, meta;
|
||
|
||
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
||
meta = chart.getDatasetMeta(i);
|
||
// toggle visibility of index if exists
|
||
if (meta.data[index]) {
|
||
meta.data[index].hidden = !meta.data[index].hidden;
|
||
}
|
||
}
|
||
|
||
chart.update();
|
||
}
|
||
},
|
||
|
||
// The percentage of the chart that we cut out of the middle.
|
||
cutoutPercentage: 50,
|
||
|
||
// The rotation of the chart, where the first data arc begins.
|
||
rotation: -HALF_PI$1,
|
||
|
||
// The total circumference of the chart.
|
||
circumference: DOUBLE_PI$1,
|
||
|
||
// Need to override these to give a nice default
|
||
tooltips: {
|
||
callbacks: {
|
||
title: function() {
|
||
return '';
|
||
},
|
||
label: function(tooltipItem, data) {
|
||
var dataLabel = data.labels[tooltipItem.index];
|
||
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
||
|
||
if (helpers$1.isArray(dataLabel)) {
|
||
// show value on first line of multiline label
|
||
// need to clone because we are changing the value
|
||
dataLabel = dataLabel.slice();
|
||
dataLabel[0] += value;
|
||
} else {
|
||
dataLabel += value;
|
||
}
|
||
|
||
return dataLabel;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
var controller_doughnut = core_datasetController.extend({
|
||
|
||
dataElementType: elements.Arc,
|
||
|
||
linkScales: helpers$1.noop,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: [
|
||
'backgroundColor',
|
||
'borderColor',
|
||
'borderWidth',
|
||
'borderAlign',
|
||
'hoverBackgroundColor',
|
||
'hoverBorderColor',
|
||
'hoverBorderWidth',
|
||
],
|
||
|
||
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
|
||
getRingIndex: function(datasetIndex) {
|
||
var ringIndex = 0;
|
||
|
||
for (var j = 0; j < datasetIndex; ++j) {
|
||
if (this.chart.isDatasetVisible(j)) {
|
||
++ringIndex;
|
||
}
|
||
}
|
||
|
||
return ringIndex;
|
||
},
|
||
|
||
update: function(reset) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var chartArea = chart.chartArea;
|
||
var opts = chart.options;
|
||
var ratioX = 1;
|
||
var ratioY = 1;
|
||
var offsetX = 0;
|
||
var offsetY = 0;
|
||
var meta = me.getMeta();
|
||
var arcs = meta.data;
|
||
var cutout = opts.cutoutPercentage / 100 || 0;
|
||
var circumference = opts.circumference;
|
||
var chartWeight = me._getRingWeight(me.index);
|
||
var maxWidth, maxHeight, i, ilen;
|
||
|
||
// If the chart's circumference isn't a full circle, calculate size as a ratio of the width/height of the arc
|
||
if (circumference < DOUBLE_PI$1) {
|
||
var startAngle = opts.rotation % DOUBLE_PI$1;
|
||
startAngle += startAngle >= PI$1 ? -DOUBLE_PI$1 : startAngle < -PI$1 ? DOUBLE_PI$1 : 0;
|
||
var endAngle = startAngle + circumference;
|
||
var startX = Math.cos(startAngle);
|
||
var startY = Math.sin(startAngle);
|
||
var endX = Math.cos(endAngle);
|
||
var endY = Math.sin(endAngle);
|
||
var contains0 = (startAngle <= 0 && endAngle >= 0) || endAngle >= DOUBLE_PI$1;
|
||
var contains90 = (startAngle <= HALF_PI$1 && endAngle >= HALF_PI$1) || endAngle >= DOUBLE_PI$1 + HALF_PI$1;
|
||
var contains180 = startAngle === -PI$1 || endAngle >= PI$1;
|
||
var contains270 = (startAngle <= -HALF_PI$1 && endAngle >= -HALF_PI$1) || endAngle >= PI$1 + HALF_PI$1;
|
||
var minX = contains180 ? -1 : Math.min(startX, startX * cutout, endX, endX * cutout);
|
||
var minY = contains270 ? -1 : Math.min(startY, startY * cutout, endY, endY * cutout);
|
||
var maxX = contains0 ? 1 : Math.max(startX, startX * cutout, endX, endX * cutout);
|
||
var maxY = contains90 ? 1 : Math.max(startY, startY * cutout, endY, endY * cutout);
|
||
ratioX = (maxX - minX) / 2;
|
||
ratioY = (maxY - minY) / 2;
|
||
offsetX = -(maxX + minX) / 2;
|
||
offsetY = -(maxY + minY) / 2;
|
||
}
|
||
|
||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||
arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
|
||
}
|
||
|
||
chart.borderWidth = me.getMaxBorderWidth();
|
||
maxWidth = (chartArea.right - chartArea.left - chart.borderWidth) / ratioX;
|
||
maxHeight = (chartArea.bottom - chartArea.top - chart.borderWidth) / ratioY;
|
||
chart.outerRadius = Math.max(Math.min(maxWidth, maxHeight) / 2, 0);
|
||
chart.innerRadius = Math.max(chart.outerRadius * cutout, 0);
|
||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / (me._getVisibleDatasetWeightTotal() || 1);
|
||
chart.offsetX = offsetX * chart.outerRadius;
|
||
chart.offsetY = offsetY * chart.outerRadius;
|
||
|
||
meta.total = me.calculateTotal();
|
||
|
||
me.outerRadius = chart.outerRadius - chart.radiusLength * me._getRingWeightOffset(me.index);
|
||
me.innerRadius = Math.max(me.outerRadius - chart.radiusLength * chartWeight, 0);
|
||
|
||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||
me.updateElement(arcs[i], i, reset);
|
||
}
|
||
},
|
||
|
||
updateElement: function(arc, index, reset) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var chartArea = chart.chartArea;
|
||
var opts = chart.options;
|
||
var animationOpts = opts.animation;
|
||
var centerX = (chartArea.left + chartArea.right) / 2;
|
||
var centerY = (chartArea.top + chartArea.bottom) / 2;
|
||
var startAngle = opts.rotation; // non reset case handled later
|
||
var endAngle = opts.rotation; // non reset case handled later
|
||
var dataset = me.getDataset();
|
||
var circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / DOUBLE_PI$1);
|
||
var innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;
|
||
var outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;
|
||
var options = arc._options || {};
|
||
|
||
helpers$1.extend(arc, {
|
||
// Utility
|
||
_datasetIndex: me.index,
|
||
_index: index,
|
||
|
||
// Desired view properties
|
||
_model: {
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderWidth: options.borderWidth,
|
||
borderAlign: options.borderAlign,
|
||
x: centerX + chart.offsetX,
|
||
y: centerY + chart.offsetY,
|
||
startAngle: startAngle,
|
||
endAngle: endAngle,
|
||
circumference: circumference,
|
||
outerRadius: outerRadius,
|
||
innerRadius: innerRadius,
|
||
label: helpers$1.valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
|
||
}
|
||
});
|
||
|
||
var model = arc._model;
|
||
|
||
// Set correct angles if not resetting
|
||
if (!reset || !animationOpts.animateRotate) {
|
||
if (index === 0) {
|
||
model.startAngle = opts.rotation;
|
||
} else {
|
||
model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
|
||
}
|
||
|
||
model.endAngle = model.startAngle + model.circumference;
|
||
}
|
||
|
||
arc.pivot();
|
||
},
|
||
|
||
calculateTotal: function() {
|
||
var dataset = this.getDataset();
|
||
var meta = this.getMeta();
|
||
var total = 0;
|
||
var value;
|
||
|
||
helpers$1.each(meta.data, function(element, index) {
|
||
value = dataset.data[index];
|
||
if (!isNaN(value) && !element.hidden) {
|
||
total += Math.abs(value);
|
||
}
|
||
});
|
||
|
||
/* if (total === 0) {
|
||
total = NaN;
|
||
}*/
|
||
|
||
return total;
|
||
},
|
||
|
||
calculateCircumference: function(value) {
|
||
var total = this.getMeta().total;
|
||
if (total > 0 && !isNaN(value)) {
|
||
return DOUBLE_PI$1 * (Math.abs(value) / total);
|
||
}
|
||
return 0;
|
||
},
|
||
|
||
// gets the max border or hover width to properly scale pie charts
|
||
getMaxBorderWidth: function(arcs) {
|
||
var me = this;
|
||
var max = 0;
|
||
var chart = me.chart;
|
||
var i, ilen, meta, arc, controller, options, borderWidth, hoverWidth;
|
||
|
||
if (!arcs) {
|
||
// Find the outmost visible dataset
|
||
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
|
||
if (chart.isDatasetVisible(i)) {
|
||
meta = chart.getDatasetMeta(i);
|
||
arcs = meta.data;
|
||
if (i !== me.index) {
|
||
controller = meta.controller;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!arcs) {
|
||
return 0;
|
||
}
|
||
|
||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||
arc = arcs[i];
|
||
if (controller) {
|
||
controller._configure();
|
||
options = controller._resolveDataElementOptions(arc, i);
|
||
} else {
|
||
options = arc._options;
|
||
}
|
||
if (options.borderAlign !== 'inner') {
|
||
borderWidth = options.borderWidth;
|
||
hoverWidth = options.hoverBorderWidth;
|
||
|
||
max = borderWidth > max ? borderWidth : max;
|
||
max = hoverWidth > max ? hoverWidth : max;
|
||
}
|
||
}
|
||
return max;
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
setHoverStyle: function(arc) {
|
||
var model = arc._model;
|
||
var options = arc._options;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
|
||
arc.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth,
|
||
};
|
||
|
||
model.backgroundColor = valueOrDefault$5(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||
model.borderColor = valueOrDefault$5(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||
model.borderWidth = valueOrDefault$5(options.hoverBorderWidth, options.borderWidth);
|
||
},
|
||
|
||
/**
|
||
* Get radius length offset of the dataset in relation to the visible datasets weights. This allows determining the inner and outer radius correctly
|
||
* @private
|
||
*/
|
||
_getRingWeightOffset: function(datasetIndex) {
|
||
var ringWeightOffset = 0;
|
||
|
||
for (var i = 0; i < datasetIndex; ++i) {
|
||
if (this.chart.isDatasetVisible(i)) {
|
||
ringWeightOffset += this._getRingWeight(i);
|
||
}
|
||
}
|
||
|
||
return ringWeightOffset;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getRingWeight: function(dataSetIndex) {
|
||
return Math.max(valueOrDefault$5(this.chart.data.datasets[dataSetIndex].weight, 1), 0);
|
||
},
|
||
|
||
/**
|
||
* Returns the sum of all visibile data set weights. This value can be 0.
|
||
* @private
|
||
*/
|
||
_getVisibleDatasetWeightTotal: function() {
|
||
return this._getRingWeightOffset(this.chart.data.datasets.length);
|
||
}
|
||
});
|
||
|
||
core_defaults._set('horizontalBar', {
|
||
hover: {
|
||
mode: 'index',
|
||
axis: 'y'
|
||
},
|
||
|
||
scales: {
|
||
xAxes: [{
|
||
type: 'linear',
|
||
position: 'bottom'
|
||
}],
|
||
|
||
yAxes: [{
|
||
type: 'category',
|
||
position: 'left',
|
||
offset: true,
|
||
gridLines: {
|
||
offsetGridLines: true
|
||
}
|
||
}]
|
||
},
|
||
|
||
elements: {
|
||
rectangle: {
|
||
borderSkipped: 'left'
|
||
}
|
||
},
|
||
|
||
tooltips: {
|
||
mode: 'index',
|
||
axis: 'y'
|
||
}
|
||
});
|
||
|
||
core_defaults._set('global', {
|
||
datasets: {
|
||
horizontalBar: {
|
||
categoryPercentage: 0.8,
|
||
barPercentage: 0.9
|
||
}
|
||
}
|
||
});
|
||
|
||
var controller_horizontalBar = controller_bar.extend({
|
||
/**
|
||
* @private
|
||
*/
|
||
_getValueScaleId: function() {
|
||
return this.getMeta().xAxisID;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getIndexScaleId: function() {
|
||
return this.getMeta().yAxisID;
|
||
}
|
||
});
|
||
|
||
var valueOrDefault$6 = helpers$1.valueOrDefault;
|
||
var resolve$2 = helpers$1.options.resolve;
|
||
var isPointInArea = helpers$1.canvas._isPointInArea;
|
||
|
||
core_defaults._set('line', {
|
||
showLines: true,
|
||
spanGaps: false,
|
||
|
||
hover: {
|
||
mode: 'label'
|
||
},
|
||
|
||
scales: {
|
||
xAxes: [{
|
||
type: 'category',
|
||
id: 'x-axis-0'
|
||
}],
|
||
yAxes: [{
|
||
type: 'linear',
|
||
id: 'y-axis-0'
|
||
}]
|
||
}
|
||
});
|
||
|
||
function scaleClip(scale, halfBorderWidth) {
|
||
var tickOpts = scale && scale.options.ticks || {};
|
||
var reverse = tickOpts.reverse;
|
||
var min = tickOpts.min === undefined ? halfBorderWidth : 0;
|
||
var max = tickOpts.max === undefined ? halfBorderWidth : 0;
|
||
return {
|
||
start: reverse ? max : min,
|
||
end: reverse ? min : max
|
||
};
|
||
}
|
||
|
||
function defaultClip(xScale, yScale, borderWidth) {
|
||
var halfBorderWidth = borderWidth / 2;
|
||
var x = scaleClip(xScale, halfBorderWidth);
|
||
var y = scaleClip(yScale, halfBorderWidth);
|
||
|
||
return {
|
||
top: y.end,
|
||
right: x.end,
|
||
bottom: y.start,
|
||
left: x.start
|
||
};
|
||
}
|
||
|
||
function toClip(value) {
|
||
var t, r, b, l;
|
||
|
||
if (helpers$1.isObject(value)) {
|
||
t = value.top;
|
||
r = value.right;
|
||
b = value.bottom;
|
||
l = value.left;
|
||
} else {
|
||
t = r = b = l = value;
|
||
}
|
||
|
||
return {
|
||
top: t,
|
||
right: r,
|
||
bottom: b,
|
||
left: l
|
||
};
|
||
}
|
||
|
||
|
||
var controller_line = core_datasetController.extend({
|
||
|
||
datasetElementType: elements.Line,
|
||
|
||
dataElementType: elements.Point,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_datasetElementOptions: [
|
||
'backgroundColor',
|
||
'borderCapStyle',
|
||
'borderColor',
|
||
'borderDash',
|
||
'borderDashOffset',
|
||
'borderJoinStyle',
|
||
'borderWidth',
|
||
'cubicInterpolationMode',
|
||
'fill'
|
||
],
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: {
|
||
backgroundColor: 'pointBackgroundColor',
|
||
borderColor: 'pointBorderColor',
|
||
borderWidth: 'pointBorderWidth',
|
||
hitRadius: 'pointHitRadius',
|
||
hoverBackgroundColor: 'pointHoverBackgroundColor',
|
||
hoverBorderColor: 'pointHoverBorderColor',
|
||
hoverBorderWidth: 'pointHoverBorderWidth',
|
||
hoverRadius: 'pointHoverRadius',
|
||
pointStyle: 'pointStyle',
|
||
radius: 'pointRadius',
|
||
rotation: 'pointRotation'
|
||
},
|
||
|
||
update: function(reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var line = meta.dataset;
|
||
var points = meta.data || [];
|
||
var options = me.chart.options;
|
||
var config = me._config;
|
||
var showLine = me._showLine = valueOrDefault$6(config.showLine, options.showLines);
|
||
var i, ilen;
|
||
|
||
me._xScale = me.getScaleForId(meta.xAxisID);
|
||
me._yScale = me.getScaleForId(meta.yAxisID);
|
||
|
||
// Update Line
|
||
if (showLine) {
|
||
// Compatibility: If the properties are defined with only the old name, use those values
|
||
if (config.tension !== undefined && config.lineTension === undefined) {
|
||
config.lineTension = config.tension;
|
||
}
|
||
|
||
// Utility
|
||
line._scale = me._yScale;
|
||
line._datasetIndex = me.index;
|
||
// Data
|
||
line._children = points;
|
||
// Model
|
||
line._model = me._resolveDatasetElementOptions(line);
|
||
|
||
line.pivot();
|
||
}
|
||
|
||
// Update Points
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
me.updateElement(points[i], i, reset);
|
||
}
|
||
|
||
if (showLine && line._model.tension !== 0) {
|
||
me.updateBezierControlPoints();
|
||
}
|
||
|
||
// Now pivot the point for animation
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
points[i].pivot();
|
||
}
|
||
},
|
||
|
||
updateElement: function(point, index, reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var custom = point.custom || {};
|
||
var dataset = me.getDataset();
|
||
var datasetIndex = me.index;
|
||
var value = dataset.data[index];
|
||
var xScale = me._xScale;
|
||
var yScale = me._yScale;
|
||
var lineModel = meta.dataset._model;
|
||
var x, y;
|
||
|
||
var options = me._resolveDataElementOptions(point, index);
|
||
|
||
x = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);
|
||
y = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);
|
||
|
||
// Utility
|
||
point._xScale = xScale;
|
||
point._yScale = yScale;
|
||
point._options = options;
|
||
point._datasetIndex = datasetIndex;
|
||
point._index = index;
|
||
|
||
// Desired view properties
|
||
point._model = {
|
||
x: x,
|
||
y: y,
|
||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||
// Appearance
|
||
radius: options.radius,
|
||
pointStyle: options.pointStyle,
|
||
rotation: options.rotation,
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderWidth: options.borderWidth,
|
||
tension: valueOrDefault$6(custom.tension, lineModel ? lineModel.tension : 0),
|
||
steppedLine: lineModel ? lineModel.steppedLine : false,
|
||
// Tooltip
|
||
hitRadius: options.hitRadius
|
||
};
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDatasetElementOptions: function(element) {
|
||
var me = this;
|
||
var config = me._config;
|
||
var custom = element.custom || {};
|
||
var options = me.chart.options;
|
||
var lineOptions = options.elements.line;
|
||
var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
|
||
|
||
// The default behavior of lines is to break at null values, according
|
||
// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158
|
||
// This option gives lines the ability to span gaps
|
||
values.spanGaps = valueOrDefault$6(config.spanGaps, options.spanGaps);
|
||
values.tension = valueOrDefault$6(config.lineTension, lineOptions.tension);
|
||
values.steppedLine = resolve$2([custom.steppedLine, config.steppedLine, lineOptions.stepped]);
|
||
values.clip = toClip(valueOrDefault$6(config.clip, defaultClip(me._xScale, me._yScale, values.borderWidth)));
|
||
|
||
return values;
|
||
},
|
||
|
||
calculatePointY: function(value, index, datasetIndex) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var yScale = me._yScale;
|
||
var sumPos = 0;
|
||
var sumNeg = 0;
|
||
var i, ds, dsMeta, stackedRightValue, rightValue, metasets, ilen;
|
||
|
||
if (yScale.options.stacked) {
|
||
rightValue = +yScale.getRightValue(value);
|
||
metasets = chart._getSortedVisibleDatasetMetas();
|
||
ilen = metasets.length;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
dsMeta = metasets[i];
|
||
if (dsMeta.index === datasetIndex) {
|
||
break;
|
||
}
|
||
|
||
ds = chart.data.datasets[dsMeta.index];
|
||
if (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id) {
|
||
stackedRightValue = +yScale.getRightValue(ds.data[index]);
|
||
if (stackedRightValue < 0) {
|
||
sumNeg += stackedRightValue || 0;
|
||
} else {
|
||
sumPos += stackedRightValue || 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (rightValue < 0) {
|
||
return yScale.getPixelForValue(sumNeg + rightValue);
|
||
}
|
||
return yScale.getPixelForValue(sumPos + rightValue);
|
||
}
|
||
return yScale.getPixelForValue(value);
|
||
},
|
||
|
||
updateBezierControlPoints: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var meta = me.getMeta();
|
||
var lineModel = meta.dataset._model;
|
||
var area = chart.chartArea;
|
||
var points = meta.data || [];
|
||
var i, ilen, model, controlPoints;
|
||
|
||
// Only consider points that are drawn in case the spanGaps option is used
|
||
if (lineModel.spanGaps) {
|
||
points = points.filter(function(pt) {
|
||
return !pt._model.skip;
|
||
});
|
||
}
|
||
|
||
function capControlPoint(pt, min, max) {
|
||
return Math.max(Math.min(pt, max), min);
|
||
}
|
||
|
||
if (lineModel.cubicInterpolationMode === 'monotone') {
|
||
helpers$1.splineCurveMonotone(points);
|
||
} else {
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
model = points[i]._model;
|
||
controlPoints = helpers$1.splineCurve(
|
||
helpers$1.previousItem(points, i)._model,
|
||
model,
|
||
helpers$1.nextItem(points, i)._model,
|
||
lineModel.tension
|
||
);
|
||
model.controlPointPreviousX = controlPoints.previous.x;
|
||
model.controlPointPreviousY = controlPoints.previous.y;
|
||
model.controlPointNextX = controlPoints.next.x;
|
||
model.controlPointNextY = controlPoints.next.y;
|
||
}
|
||
}
|
||
|
||
if (chart.options.elements.line.capBezierPoints) {
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
model = points[i]._model;
|
||
if (isPointInArea(model, area)) {
|
||
if (i > 0 && isPointInArea(points[i - 1]._model, area)) {
|
||
model.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);
|
||
model.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);
|
||
}
|
||
if (i < points.length - 1 && isPointInArea(points[i + 1]._model, area)) {
|
||
model.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);
|
||
model.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
draw: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var meta = me.getMeta();
|
||
var points = meta.data || [];
|
||
var area = chart.chartArea;
|
||
var canvas = chart.canvas;
|
||
var i = 0;
|
||
var ilen = points.length;
|
||
var clip;
|
||
|
||
if (me._showLine) {
|
||
clip = meta.dataset._model.clip;
|
||
|
||
helpers$1.canvas.clipArea(chart.ctx, {
|
||
left: clip.left === false ? 0 : area.left - clip.left,
|
||
right: clip.right === false ? canvas.width : area.right + clip.right,
|
||
top: clip.top === false ? 0 : area.top - clip.top,
|
||
bottom: clip.bottom === false ? canvas.height : area.bottom + clip.bottom
|
||
});
|
||
|
||
meta.dataset.draw();
|
||
|
||
helpers$1.canvas.unclipArea(chart.ctx);
|
||
}
|
||
|
||
// Draw the points
|
||
for (; i < ilen; ++i) {
|
||
points[i].draw(area);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
setHoverStyle: function(point) {
|
||
var model = point._model;
|
||
var options = point._options;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
|
||
point.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth,
|
||
radius: model.radius
|
||
};
|
||
|
||
model.backgroundColor = valueOrDefault$6(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||
model.borderColor = valueOrDefault$6(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||
model.borderWidth = valueOrDefault$6(options.hoverBorderWidth, options.borderWidth);
|
||
model.radius = valueOrDefault$6(options.hoverRadius, options.radius);
|
||
},
|
||
});
|
||
|
||
var resolve$3 = helpers$1.options.resolve;
|
||
|
||
core_defaults._set('polarArea', {
|
||
scale: {
|
||
type: 'radialLinear',
|
||
angleLines: {
|
||
display: false
|
||
},
|
||
gridLines: {
|
||
circular: true
|
||
},
|
||
pointLabels: {
|
||
display: false
|
||
},
|
||
ticks: {
|
||
beginAtZero: true
|
||
}
|
||
},
|
||
|
||
// Boolean - Whether to animate the rotation of the chart
|
||
animation: {
|
||
animateRotate: true,
|
||
animateScale: true
|
||
},
|
||
|
||
startAngle: -0.5 * Math.PI,
|
||
legendCallback: function(chart) {
|
||
var list = document.createElement('ul');
|
||
var data = chart.data;
|
||
var datasets = data.datasets;
|
||
var labels = data.labels;
|
||
var i, ilen, listItem, listItemSpan;
|
||
|
||
list.setAttribute('class', chart.id + '-legend');
|
||
if (datasets.length) {
|
||
for (i = 0, ilen = datasets[0].data.length; i < ilen; ++i) {
|
||
listItem = list.appendChild(document.createElement('li'));
|
||
listItemSpan = listItem.appendChild(document.createElement('span'));
|
||
listItemSpan.style.backgroundColor = datasets[0].backgroundColor[i];
|
||
if (labels[i]) {
|
||
listItem.appendChild(document.createTextNode(labels[i]));
|
||
}
|
||
}
|
||
}
|
||
|
||
return list.outerHTML;
|
||
},
|
||
legend: {
|
||
labels: {
|
||
generateLabels: function(chart) {
|
||
var data = chart.data;
|
||
if (data.labels.length && data.datasets.length) {
|
||
return data.labels.map(function(label, i) {
|
||
var meta = chart.getDatasetMeta(0);
|
||
var style = meta.controller.getStyle(i);
|
||
|
||
return {
|
||
text: label,
|
||
fillStyle: style.backgroundColor,
|
||
strokeStyle: style.borderColor,
|
||
lineWidth: style.borderWidth,
|
||
hidden: isNaN(data.datasets[0].data[i]) || meta.data[i].hidden,
|
||
|
||
// Extra data used for toggling the correct item
|
||
index: i
|
||
};
|
||
});
|
||
}
|
||
return [];
|
||
}
|
||
},
|
||
|
||
onClick: function(e, legendItem) {
|
||
var index = legendItem.index;
|
||
var chart = this.chart;
|
||
var i, ilen, meta;
|
||
|
||
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
||
meta = chart.getDatasetMeta(i);
|
||
meta.data[index].hidden = !meta.data[index].hidden;
|
||
}
|
||
|
||
chart.update();
|
||
}
|
||
},
|
||
|
||
// Need to override these to give a nice default
|
||
tooltips: {
|
||
callbacks: {
|
||
title: function() {
|
||
return '';
|
||
},
|
||
label: function(item, data) {
|
||
return data.labels[item.index] + ': ' + item.yLabel;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
var controller_polarArea = core_datasetController.extend({
|
||
|
||
dataElementType: elements.Arc,
|
||
|
||
linkScales: helpers$1.noop,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: [
|
||
'backgroundColor',
|
||
'borderColor',
|
||
'borderWidth',
|
||
'borderAlign',
|
||
'hoverBackgroundColor',
|
||
'hoverBorderColor',
|
||
'hoverBorderWidth',
|
||
],
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getIndexScaleId: function() {
|
||
return this.chart.scale.id;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getValueScaleId: function() {
|
||
return this.chart.scale.id;
|
||
},
|
||
|
||
update: function(reset) {
|
||
var me = this;
|
||
var dataset = me.getDataset();
|
||
var meta = me.getMeta();
|
||
var start = me.chart.options.startAngle || 0;
|
||
var starts = me._starts = [];
|
||
var angles = me._angles = [];
|
||
var arcs = meta.data;
|
||
var i, ilen, angle;
|
||
|
||
me._updateRadius();
|
||
|
||
meta.count = me.countVisibleElements();
|
||
|
||
for (i = 0, ilen = dataset.data.length; i < ilen; i++) {
|
||
starts[i] = start;
|
||
angle = me._computeAngle(i);
|
||
angles[i] = angle;
|
||
start += angle;
|
||
}
|
||
|
||
for (i = 0, ilen = arcs.length; i < ilen; ++i) {
|
||
arcs[i]._options = me._resolveDataElementOptions(arcs[i], i);
|
||
me.updateElement(arcs[i], i, reset);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_updateRadius: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var chartArea = chart.chartArea;
|
||
var opts = chart.options;
|
||
var minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
||
|
||
chart.outerRadius = Math.max(minSize / 2, 0);
|
||
chart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
|
||
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
||
|
||
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);
|
||
me.innerRadius = me.outerRadius - chart.radiusLength;
|
||
},
|
||
|
||
updateElement: function(arc, index, reset) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var dataset = me.getDataset();
|
||
var opts = chart.options;
|
||
var animationOpts = opts.animation;
|
||
var scale = chart.scale;
|
||
var labels = chart.data.labels;
|
||
|
||
var centerX = scale.xCenter;
|
||
var centerY = scale.yCenter;
|
||
|
||
// var negHalfPI = -0.5 * Math.PI;
|
||
var datasetStartAngle = opts.startAngle;
|
||
var distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||
var startAngle = me._starts[index];
|
||
var endAngle = startAngle + (arc.hidden ? 0 : me._angles[index]);
|
||
|
||
var resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);
|
||
var options = arc._options || {};
|
||
|
||
helpers$1.extend(arc, {
|
||
// Utility
|
||
_datasetIndex: me.index,
|
||
_index: index,
|
||
_scale: scale,
|
||
|
||
// Desired view properties
|
||
_model: {
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderWidth: options.borderWidth,
|
||
borderAlign: options.borderAlign,
|
||
x: centerX,
|
||
y: centerY,
|
||
innerRadius: 0,
|
||
outerRadius: reset ? resetRadius : distance,
|
||
startAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,
|
||
endAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,
|
||
label: helpers$1.valueAtIndexOrDefault(labels, index, labels[index])
|
||
}
|
||
});
|
||
|
||
arc.pivot();
|
||
},
|
||
|
||
countVisibleElements: function() {
|
||
var dataset = this.getDataset();
|
||
var meta = this.getMeta();
|
||
var count = 0;
|
||
|
||
helpers$1.each(meta.data, function(element, index) {
|
||
if (!isNaN(dataset.data[index]) && !element.hidden) {
|
||
count++;
|
||
}
|
||
});
|
||
|
||
return count;
|
||
},
|
||
|
||
/**
|
||
* @protected
|
||
*/
|
||
setHoverStyle: function(arc) {
|
||
var model = arc._model;
|
||
var options = arc._options;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
var valueOrDefault = helpers$1.valueOrDefault;
|
||
|
||
arc.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth,
|
||
};
|
||
|
||
model.backgroundColor = valueOrDefault(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||
model.borderColor = valueOrDefault(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||
model.borderWidth = valueOrDefault(options.hoverBorderWidth, options.borderWidth);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_computeAngle: function(index) {
|
||
var me = this;
|
||
var count = this.getMeta().count;
|
||
var dataset = me.getDataset();
|
||
var meta = me.getMeta();
|
||
|
||
if (isNaN(dataset.data[index]) || meta.data[index].hidden) {
|
||
return 0;
|
||
}
|
||
|
||
// Scriptable options
|
||
var context = {
|
||
chart: me.chart,
|
||
dataIndex: index,
|
||
dataset: dataset,
|
||
datasetIndex: me.index
|
||
};
|
||
|
||
return resolve$3([
|
||
me.chart.options.elements.arc.angle,
|
||
(2 * Math.PI) / count
|
||
], context, index);
|
||
}
|
||
});
|
||
|
||
core_defaults._set('pie', helpers$1.clone(core_defaults.doughnut));
|
||
core_defaults._set('pie', {
|
||
cutoutPercentage: 0
|
||
});
|
||
|
||
// Pie charts are Doughnut chart with different defaults
|
||
var controller_pie = controller_doughnut;
|
||
|
||
var valueOrDefault$7 = helpers$1.valueOrDefault;
|
||
|
||
core_defaults._set('radar', {
|
||
spanGaps: false,
|
||
scale: {
|
||
type: 'radialLinear'
|
||
},
|
||
elements: {
|
||
line: {
|
||
fill: 'start',
|
||
tension: 0 // no bezier in radar
|
||
}
|
||
}
|
||
});
|
||
|
||
var controller_radar = core_datasetController.extend({
|
||
datasetElementType: elements.Line,
|
||
|
||
dataElementType: elements.Point,
|
||
|
||
linkScales: helpers$1.noop,
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_datasetElementOptions: [
|
||
'backgroundColor',
|
||
'borderWidth',
|
||
'borderColor',
|
||
'borderCapStyle',
|
||
'borderDash',
|
||
'borderDashOffset',
|
||
'borderJoinStyle',
|
||
'fill'
|
||
],
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_dataElementOptions: {
|
||
backgroundColor: 'pointBackgroundColor',
|
||
borderColor: 'pointBorderColor',
|
||
borderWidth: 'pointBorderWidth',
|
||
hitRadius: 'pointHitRadius',
|
||
hoverBackgroundColor: 'pointHoverBackgroundColor',
|
||
hoverBorderColor: 'pointHoverBorderColor',
|
||
hoverBorderWidth: 'pointHoverBorderWidth',
|
||
hoverRadius: 'pointHoverRadius',
|
||
pointStyle: 'pointStyle',
|
||
radius: 'pointRadius',
|
||
rotation: 'pointRotation'
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getIndexScaleId: function() {
|
||
return this.chart.scale.id;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getValueScaleId: function() {
|
||
return this.chart.scale.id;
|
||
},
|
||
|
||
update: function(reset) {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var line = meta.dataset;
|
||
var points = meta.data || [];
|
||
var scale = me.chart.scale;
|
||
var config = me._config;
|
||
var i, ilen;
|
||
|
||
// Compatibility: If the properties are defined with only the old name, use those values
|
||
if (config.tension !== undefined && config.lineTension === undefined) {
|
||
config.lineTension = config.tension;
|
||
}
|
||
|
||
// Utility
|
||
line._scale = scale;
|
||
line._datasetIndex = me.index;
|
||
// Data
|
||
line._children = points;
|
||
line._loop = true;
|
||
// Model
|
||
line._model = me._resolveDatasetElementOptions(line);
|
||
|
||
line.pivot();
|
||
|
||
// Update Points
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
me.updateElement(points[i], i, reset);
|
||
}
|
||
|
||
// Update bezier control points
|
||
me.updateBezierControlPoints();
|
||
|
||
// Now pivot the point for animation
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
points[i].pivot();
|
||
}
|
||
},
|
||
|
||
updateElement: function(point, index, reset) {
|
||
var me = this;
|
||
var custom = point.custom || {};
|
||
var dataset = me.getDataset();
|
||
var scale = me.chart.scale;
|
||
var pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);
|
||
var options = me._resolveDataElementOptions(point, index);
|
||
var lineModel = me.getMeta().dataset._model;
|
||
var x = reset ? scale.xCenter : pointPosition.x;
|
||
var y = reset ? scale.yCenter : pointPosition.y;
|
||
|
||
// Utility
|
||
point._scale = scale;
|
||
point._options = options;
|
||
point._datasetIndex = me.index;
|
||
point._index = index;
|
||
|
||
// Desired view properties
|
||
point._model = {
|
||
x: x, // value not used in dataset scale, but we want a consistent API between scales
|
||
y: y,
|
||
skip: custom.skip || isNaN(x) || isNaN(y),
|
||
// Appearance
|
||
radius: options.radius,
|
||
pointStyle: options.pointStyle,
|
||
rotation: options.rotation,
|
||
backgroundColor: options.backgroundColor,
|
||
borderColor: options.borderColor,
|
||
borderWidth: options.borderWidth,
|
||
tension: valueOrDefault$7(custom.tension, lineModel ? lineModel.tension : 0),
|
||
|
||
// Tooltip
|
||
hitRadius: options.hitRadius
|
||
};
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_resolveDatasetElementOptions: function() {
|
||
var me = this;
|
||
var config = me._config;
|
||
var options = me.chart.options;
|
||
var values = core_datasetController.prototype._resolveDatasetElementOptions.apply(me, arguments);
|
||
|
||
values.spanGaps = valueOrDefault$7(config.spanGaps, options.spanGaps);
|
||
values.tension = valueOrDefault$7(config.lineTension, options.elements.line.tension);
|
||
|
||
return values;
|
||
},
|
||
|
||
updateBezierControlPoints: function() {
|
||
var me = this;
|
||
var meta = me.getMeta();
|
||
var area = me.chart.chartArea;
|
||
var points = meta.data || [];
|
||
var i, ilen, model, controlPoints;
|
||
|
||
// Only consider points that are drawn in case the spanGaps option is used
|
||
if (meta.dataset._model.spanGaps) {
|
||
points = points.filter(function(pt) {
|
||
return !pt._model.skip;
|
||
});
|
||
}
|
||
|
||
function capControlPoint(pt, min, max) {
|
||
return Math.max(Math.min(pt, max), min);
|
||
}
|
||
|
||
for (i = 0, ilen = points.length; i < ilen; ++i) {
|
||
model = points[i]._model;
|
||
controlPoints = helpers$1.splineCurve(
|
||
helpers$1.previousItem(points, i, true)._model,
|
||
model,
|
||
helpers$1.nextItem(points, i, true)._model,
|
||
model.tension
|
||
);
|
||
|
||
// Prevent the bezier going outside of the bounds of the graph
|
||
model.controlPointPreviousX = capControlPoint(controlPoints.previous.x, area.left, area.right);
|
||
model.controlPointPreviousY = capControlPoint(controlPoints.previous.y, area.top, area.bottom);
|
||
model.controlPointNextX = capControlPoint(controlPoints.next.x, area.left, area.right);
|
||
model.controlPointNextY = capControlPoint(controlPoints.next.y, area.top, area.bottom);
|
||
}
|
||
},
|
||
|
||
setHoverStyle: function(point) {
|
||
var model = point._model;
|
||
var options = point._options;
|
||
var getHoverColor = helpers$1.getHoverColor;
|
||
|
||
point.$previousStyle = {
|
||
backgroundColor: model.backgroundColor,
|
||
borderColor: model.borderColor,
|
||
borderWidth: model.borderWidth,
|
||
radius: model.radius
|
||
};
|
||
|
||
model.backgroundColor = valueOrDefault$7(options.hoverBackgroundColor, getHoverColor(options.backgroundColor));
|
||
model.borderColor = valueOrDefault$7(options.hoverBorderColor, getHoverColor(options.borderColor));
|
||
model.borderWidth = valueOrDefault$7(options.hoverBorderWidth, options.borderWidth);
|
||
model.radius = valueOrDefault$7(options.hoverRadius, options.radius);
|
||
}
|
||
});
|
||
|
||
core_defaults._set('scatter', {
|
||
hover: {
|
||
mode: 'single'
|
||
},
|
||
|
||
scales: {
|
||
xAxes: [{
|
||
id: 'x-axis-1', // need an ID so datasets can reference the scale
|
||
type: 'linear', // scatter should not use a category axis
|
||
position: 'bottom'
|
||
}],
|
||
yAxes: [{
|
||
id: 'y-axis-1',
|
||
type: 'linear',
|
||
position: 'left'
|
||
}]
|
||
},
|
||
|
||
tooltips: {
|
||
callbacks: {
|
||
title: function() {
|
||
return ''; // doesn't make sense for scatter since data are formatted as a point
|
||
},
|
||
label: function(item) {
|
||
return '(' + item.xLabel + ', ' + item.yLabel + ')';
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
core_defaults._set('global', {
|
||
datasets: {
|
||
scatter: {
|
||
showLine: false
|
||
}
|
||
}
|
||
});
|
||
|
||
// Scatter charts use line controllers
|
||
var controller_scatter = controller_line;
|
||
|
||
// NOTE export a map in which the key represents the controller type, not
|
||
// the class, and so must be CamelCase in order to be correctly retrieved
|
||
// by the controller in core.controller.js (`controllers[meta.type]`).
|
||
|
||
var controllers = {
|
||
bar: controller_bar,
|
||
bubble: controller_bubble,
|
||
doughnut: controller_doughnut,
|
||
horizontalBar: controller_horizontalBar,
|
||
line: controller_line,
|
||
polarArea: controller_polarArea,
|
||
pie: controller_pie,
|
||
radar: controller_radar,
|
||
scatter: controller_scatter
|
||
};
|
||
|
||
/**
|
||
* Helper function to get relative position for an event
|
||
* @param {Event|IEvent} event - The event to get the position for
|
||
* @param {Chart} chart - The chart
|
||
* @returns {object} the event position
|
||
*/
|
||
function getRelativePosition(e, chart) {
|
||
if (e.native) {
|
||
return {
|
||
x: e.x,
|
||
y: e.y
|
||
};
|
||
}
|
||
|
||
return helpers$1.getRelativePosition(e, chart);
|
||
}
|
||
|
||
/**
|
||
* Helper function to traverse all of the visible elements in the chart
|
||
* @param {Chart} chart - the chart
|
||
* @param {function} handler - the callback to execute for each visible item
|
||
*/
|
||
function parseVisibleItems(chart, handler) {
|
||
var metasets = chart._getSortedVisibleDatasetMetas();
|
||
var metadata, i, j, ilen, jlen, element;
|
||
|
||
for (i = 0, ilen = metasets.length; i < ilen; ++i) {
|
||
metadata = metasets[i].data;
|
||
for (j = 0, jlen = metadata.length; j < jlen; ++j) {
|
||
element = metadata[j];
|
||
if (!element._view.skip) {
|
||
handler(element);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Helper function to get the items that intersect the event position
|
||
* @param {ChartElement[]} items - elements to filter
|
||
* @param {object} position - the point to be nearest to
|
||
* @return {ChartElement[]} the nearest items
|
||
*/
|
||
function getIntersectItems(chart, position) {
|
||
var elements = [];
|
||
|
||
parseVisibleItems(chart, function(element) {
|
||
if (element.inRange(position.x, position.y)) {
|
||
elements.push(element);
|
||
}
|
||
});
|
||
|
||
return elements;
|
||
}
|
||
|
||
/**
|
||
* Helper function to get the items nearest to the event position considering all visible items in teh chart
|
||
* @param {Chart} chart - the chart to look at elements from
|
||
* @param {object} position - the point to be nearest to
|
||
* @param {boolean} intersect - if true, only consider items that intersect the position
|
||
* @param {function} distanceMetric - function to provide the distance between points
|
||
* @return {ChartElement[]} the nearest items
|
||
*/
|
||
function getNearestItems(chart, position, intersect, distanceMetric) {
|
||
var minDistance = Number.POSITIVE_INFINITY;
|
||
var nearestItems = [];
|
||
|
||
parseVisibleItems(chart, function(element) {
|
||
if (intersect && !element.inRange(position.x, position.y)) {
|
||
return;
|
||
}
|
||
|
||
var center = element.getCenterPoint();
|
||
var distance = distanceMetric(position, center);
|
||
if (distance < minDistance) {
|
||
nearestItems = [element];
|
||
minDistance = distance;
|
||
} else if (distance === minDistance) {
|
||
// Can have multiple items at the same distance in which case we sort by size
|
||
nearestItems.push(element);
|
||
}
|
||
});
|
||
|
||
return nearestItems;
|
||
}
|
||
|
||
/**
|
||
* Get a distance metric function for two points based on the
|
||
* axis mode setting
|
||
* @param {string} axis - the axis mode. x|y|xy
|
||
*/
|
||
function getDistanceMetricForAxis(axis) {
|
||
var useX = axis.indexOf('x') !== -1;
|
||
var useY = axis.indexOf('y') !== -1;
|
||
|
||
return function(pt1, pt2) {
|
||
var deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;
|
||
var deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;
|
||
return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
|
||
};
|
||
}
|
||
|
||
function indexMode(chart, e, options) {
|
||
var position = getRelativePosition(e, chart);
|
||
// Default axis for index mode is 'x' to match old behaviour
|
||
options.axis = options.axis || 'x';
|
||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
||
var elements = [];
|
||
|
||
if (!items.length) {
|
||
return [];
|
||
}
|
||
|
||
chart._getSortedVisibleDatasetMetas().forEach(function(meta) {
|
||
var element = meta.data[items[0]._index];
|
||
|
||
// don't count items that are skipped (null data)
|
||
if (element && !element._view.skip) {
|
||
elements.push(element);
|
||
}
|
||
});
|
||
|
||
return elements;
|
||
}
|
||
|
||
/**
|
||
* @interface IInteractionOptions
|
||
*/
|
||
/**
|
||
* If true, only consider items that intersect the point
|
||
* @name IInterfaceOptions#boolean
|
||
* @type Boolean
|
||
*/
|
||
|
||
/**
|
||
* Contains interaction related functions
|
||
* @namespace Chart.Interaction
|
||
*/
|
||
var core_interaction = {
|
||
// Helper function for different modes
|
||
modes: {
|
||
single: function(chart, e) {
|
||
var position = getRelativePosition(e, chart);
|
||
var elements = [];
|
||
|
||
parseVisibleItems(chart, function(element) {
|
||
if (element.inRange(position.x, position.y)) {
|
||
elements.push(element);
|
||
return elements;
|
||
}
|
||
});
|
||
|
||
return elements.slice(0, 1);
|
||
},
|
||
|
||
/**
|
||
* @function Chart.Interaction.modes.label
|
||
* @deprecated since version 2.4.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
label: indexMode,
|
||
|
||
/**
|
||
* Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something
|
||
* If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item
|
||
* @function Chart.Interaction.modes.index
|
||
* @since v2.4.0
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @param {IInteractionOptions} options - options to use during interaction
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
index: indexMode,
|
||
|
||
/**
|
||
* Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something
|
||
* If the options.intersect is false, we find the nearest item and return the items in that dataset
|
||
* @function Chart.Interaction.modes.dataset
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @param {IInteractionOptions} options - options to use during interaction
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
dataset: function(chart, e, options) {
|
||
var position = getRelativePosition(e, chart);
|
||
options.axis = options.axis || 'xy';
|
||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||
var items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);
|
||
|
||
if (items.length > 0) {
|
||
items = chart.getDatasetMeta(items[0]._datasetIndex).data;
|
||
}
|
||
|
||
return items;
|
||
},
|
||
|
||
/**
|
||
* @function Chart.Interaction.modes.x-axis
|
||
* @deprecated since version 2.4.0. Use index mode and intersect == true
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
'x-axis': function(chart, e) {
|
||
return indexMode(chart, e, {intersect: false});
|
||
},
|
||
|
||
/**
|
||
* Point mode returns all elements that hit test based on the event position
|
||
* of the event
|
||
* @function Chart.Interaction.modes.intersect
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
point: function(chart, e) {
|
||
var position = getRelativePosition(e, chart);
|
||
return getIntersectItems(chart, position);
|
||
},
|
||
|
||
/**
|
||
* nearest mode returns the element closest to the point
|
||
* @function Chart.Interaction.modes.intersect
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @param {IInteractionOptions} options - options to use
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
nearest: function(chart, e, options) {
|
||
var position = getRelativePosition(e, chart);
|
||
options.axis = options.axis || 'xy';
|
||
var distanceMetric = getDistanceMetricForAxis(options.axis);
|
||
return getNearestItems(chart, position, options.intersect, distanceMetric);
|
||
},
|
||
|
||
/**
|
||
* x mode returns the elements that hit-test at the current x coordinate
|
||
* @function Chart.Interaction.modes.x
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @param {IInteractionOptions} options - options to use
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
x: function(chart, e, options) {
|
||
var position = getRelativePosition(e, chart);
|
||
var items = [];
|
||
var intersectsItem = false;
|
||
|
||
parseVisibleItems(chart, function(element) {
|
||
if (element.inXRange(position.x)) {
|
||
items.push(element);
|
||
}
|
||
|
||
if (element.inRange(position.x, position.y)) {
|
||
intersectsItem = true;
|
||
}
|
||
});
|
||
|
||
// If we want to trigger on an intersect and we don't have any items
|
||
// that intersect the position, return nothing
|
||
if (options.intersect && !intersectsItem) {
|
||
items = [];
|
||
}
|
||
return items;
|
||
},
|
||
|
||
/**
|
||
* y mode returns the elements that hit-test at the current y coordinate
|
||
* @function Chart.Interaction.modes.y
|
||
* @param {Chart} chart - the chart we are returning items from
|
||
* @param {Event} e - the event we are find things at
|
||
* @param {IInteractionOptions} options - options to use
|
||
* @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned
|
||
*/
|
||
y: function(chart, e, options) {
|
||
var position = getRelativePosition(e, chart);
|
||
var items = [];
|
||
var intersectsItem = false;
|
||
|
||
parseVisibleItems(chart, function(element) {
|
||
if (element.inYRange(position.y)) {
|
||
items.push(element);
|
||
}
|
||
|
||
if (element.inRange(position.x, position.y)) {
|
||
intersectsItem = true;
|
||
}
|
||
});
|
||
|
||
// If we want to trigger on an intersect and we don't have any items
|
||
// that intersect the position, return nothing
|
||
if (options.intersect && !intersectsItem) {
|
||
items = [];
|
||
}
|
||
return items;
|
||
}
|
||
}
|
||
};
|
||
|
||
var extend = helpers$1.extend;
|
||
|
||
function filterByPosition(array, position) {
|
||
return helpers$1.where(array, function(v) {
|
||
return v.pos === position;
|
||
});
|
||
}
|
||
|
||
function sortByWeight(array, reverse) {
|
||
return array.sort(function(a, b) {
|
||
var v0 = reverse ? b : a;
|
||
var v1 = reverse ? a : b;
|
||
return v0.weight === v1.weight ?
|
||
v0.index - v1.index :
|
||
v0.weight - v1.weight;
|
||
});
|
||
}
|
||
|
||
function wrapBoxes(boxes) {
|
||
var layoutBoxes = [];
|
||
var i, ilen, box;
|
||
|
||
for (i = 0, ilen = (boxes || []).length; i < ilen; ++i) {
|
||
box = boxes[i];
|
||
layoutBoxes.push({
|
||
index: i,
|
||
box: box,
|
||
pos: box.position,
|
||
horizontal: box.isHorizontal(),
|
||
weight: box.weight
|
||
});
|
||
}
|
||
return layoutBoxes;
|
||
}
|
||
|
||
function setLayoutDims(layouts, params) {
|
||
var i, ilen, layout;
|
||
for (i = 0, ilen = layouts.length; i < ilen; ++i) {
|
||
layout = layouts[i];
|
||
// store width used instead of chartArea.w in fitBoxes
|
||
layout.width = layout.horizontal
|
||
? layout.box.fullWidth && params.availableWidth
|
||
: params.vBoxMaxWidth;
|
||
// store height used instead of chartArea.h in fitBoxes
|
||
layout.height = layout.horizontal && params.hBoxMaxHeight;
|
||
}
|
||
}
|
||
|
||
function buildLayoutBoxes(boxes) {
|
||
var layoutBoxes = wrapBoxes(boxes);
|
||
var left = sortByWeight(filterByPosition(layoutBoxes, 'left'), true);
|
||
var right = sortByWeight(filterByPosition(layoutBoxes, 'right'));
|
||
var top = sortByWeight(filterByPosition(layoutBoxes, 'top'), true);
|
||
var bottom = sortByWeight(filterByPosition(layoutBoxes, 'bottom'));
|
||
|
||
return {
|
||
leftAndTop: left.concat(top),
|
||
rightAndBottom: right.concat(bottom),
|
||
chartArea: filterByPosition(layoutBoxes, 'chartArea'),
|
||
vertical: left.concat(right),
|
||
horizontal: top.concat(bottom)
|
||
};
|
||
}
|
||
|
||
function getCombinedMax(maxPadding, chartArea, a, b) {
|
||
return Math.max(maxPadding[a], chartArea[a]) + Math.max(maxPadding[b], chartArea[b]);
|
||
}
|
||
|
||
function updateDims(chartArea, params, layout) {
|
||
var box = layout.box;
|
||
var maxPadding = chartArea.maxPadding;
|
||
var newWidth, newHeight;
|
||
|
||
if (layout.size) {
|
||
// this layout was already counted for, lets first reduce old size
|
||
chartArea[layout.pos] -= layout.size;
|
||
}
|
||
layout.size = layout.horizontal ? box.height : box.width;
|
||
chartArea[layout.pos] += layout.size;
|
||
|
||
if (box.getPadding) {
|
||
var boxPadding = box.getPadding();
|
||
maxPadding.top = Math.max(maxPadding.top, boxPadding.top);
|
||
maxPadding.left = Math.max(maxPadding.left, boxPadding.left);
|
||
maxPadding.bottom = Math.max(maxPadding.bottom, boxPadding.bottom);
|
||
maxPadding.right = Math.max(maxPadding.right, boxPadding.right);
|
||
}
|
||
|
||
newWidth = params.outerWidth - getCombinedMax(maxPadding, chartArea, 'left', 'right');
|
||
newHeight = params.outerHeight - getCombinedMax(maxPadding, chartArea, 'top', 'bottom');
|
||
|
||
if (newWidth !== chartArea.w || newHeight !== chartArea.h) {
|
||
chartArea.w = newWidth;
|
||
chartArea.h = newHeight;
|
||
|
||
// return true if chart area changed in layout's direction
|
||
var sizes = layout.horizontal ? [newWidth, chartArea.w] : [newHeight, chartArea.h];
|
||
return sizes[0] !== sizes[1] && (!isNaN(sizes[0]) || !isNaN(sizes[1]));
|
||
}
|
||
}
|
||
|
||
function handleMaxPadding(chartArea) {
|
||
var maxPadding = chartArea.maxPadding;
|
||
|
||
function updatePos(pos) {
|
||
var change = Math.max(maxPadding[pos] - chartArea[pos], 0);
|
||
chartArea[pos] += change;
|
||
return change;
|
||
}
|
||
chartArea.y += updatePos('top');
|
||
chartArea.x += updatePos('left');
|
||
updatePos('right');
|
||
updatePos('bottom');
|
||
}
|
||
|
||
function getMargins(horizontal, chartArea) {
|
||
var maxPadding = chartArea.maxPadding;
|
||
|
||
function marginForPositions(positions) {
|
||
var margin = {left: 0, top: 0, right: 0, bottom: 0};
|
||
positions.forEach(function(pos) {
|
||
margin[pos] = Math.max(chartArea[pos], maxPadding[pos]);
|
||
});
|
||
return margin;
|
||
}
|
||
|
||
return horizontal
|
||
? marginForPositions(['left', 'right'])
|
||
: marginForPositions(['top', 'bottom']);
|
||
}
|
||
|
||
function fitBoxes(boxes, chartArea, params) {
|
||
var refitBoxes = [];
|
||
var i, ilen, layout, box, refit, changed;
|
||
|
||
for (i = 0, ilen = boxes.length; i < ilen; ++i) {
|
||
layout = boxes[i];
|
||
box = layout.box;
|
||
|
||
box.update(
|
||
layout.width || chartArea.w,
|
||
layout.height || chartArea.h,
|
||
getMargins(layout.horizontal, chartArea)
|
||
);
|
||
if (updateDims(chartArea, params, layout)) {
|
||
changed = true;
|
||
if (refitBoxes.length) {
|
||
// Dimensions changed and there were non full width boxes before this
|
||
// -> we have to refit those
|
||
refit = true;
|
||
}
|
||
}
|
||
if (!box.fullWidth) { // fullWidth boxes don't need to be re-fitted in any case
|
||
refitBoxes.push(layout);
|
||
}
|
||
}
|
||
|
||
return refit ? fitBoxes(refitBoxes, chartArea, params) || changed : changed;
|
||
}
|
||
|
||
function placeBoxes(boxes, chartArea, params) {
|
||
var userPadding = params.padding;
|
||
var x = chartArea.x;
|
||
var y = chartArea.y;
|
||
var i, ilen, layout, box;
|
||
|
||
for (i = 0, ilen = boxes.length; i < ilen; ++i) {
|
||
layout = boxes[i];
|
||
box = layout.box;
|
||
if (layout.horizontal) {
|
||
box.left = box.fullWidth ? userPadding.left : chartArea.left;
|
||
box.right = box.fullWidth ? params.outerWidth - userPadding.right : chartArea.left + chartArea.w;
|
||
box.top = y;
|
||
box.bottom = y + box.height;
|
||
box.width = box.right - box.left;
|
||
y = box.bottom;
|
||
} else {
|
||
box.left = x;
|
||
box.right = x + box.width;
|
||
box.top = chartArea.top;
|
||
box.bottom = chartArea.top + chartArea.h;
|
||
box.height = box.bottom - box.top;
|
||
x = box.right;
|
||
}
|
||
}
|
||
|
||
chartArea.x = x;
|
||
chartArea.y = y;
|
||
}
|
||
|
||
core_defaults._set('global', {
|
||
layout: {
|
||
padding: {
|
||
top: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
left: 0
|
||
}
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @interface ILayoutItem
|
||
* @prop {string} position - The position of the item in the chart layout. Possible values are
|
||
* 'left', 'top', 'right', 'bottom', and 'chartArea'
|
||
* @prop {number} weight - The weight used to sort the item. Higher weights are further away from the chart area
|
||
* @prop {boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down
|
||
* @prop {function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)
|
||
* @prop {function} update - Takes two parameters: width and height. Returns size of item
|
||
* @prop {function} getPadding - Returns an object with padding on the edges
|
||
* @prop {number} width - Width of item. Must be valid after update()
|
||
* @prop {number} height - Height of item. Must be valid after update()
|
||
* @prop {number} left - Left edge of the item. Set by layout system and cannot be used in update
|
||
* @prop {number} top - Top edge of the item. Set by layout system and cannot be used in update
|
||
* @prop {number} right - Right edge of the item. Set by layout system and cannot be used in update
|
||
* @prop {number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update
|
||
*/
|
||
|
||
// The layout service is very self explanatory. It's responsible for the layout within a chart.
|
||
// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need
|
||
// It is this service's responsibility of carrying out that layout.
|
||
var core_layouts = {
|
||
defaults: {},
|
||
|
||
/**
|
||
* Register a box to a chart.
|
||
* A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.
|
||
* @param {Chart} chart - the chart to use
|
||
* @param {ILayoutItem} item - the item to add to be layed out
|
||
*/
|
||
addBox: function(chart, item) {
|
||
if (!chart.boxes) {
|
||
chart.boxes = [];
|
||
}
|
||
|
||
// initialize item with default values
|
||
item.fullWidth = item.fullWidth || false;
|
||
item.position = item.position || 'top';
|
||
item.weight = item.weight || 0;
|
||
item._layers = item._layers || function() {
|
||
return [{
|
||
z: 0,
|
||
draw: function() {
|
||
item.draw.apply(item, arguments);
|
||
}
|
||
}];
|
||
};
|
||
|
||
chart.boxes.push(item);
|
||
},
|
||
|
||
/**
|
||
* Remove a layoutItem from a chart
|
||
* @param {Chart} chart - the chart to remove the box from
|
||
* @param {ILayoutItem} layoutItem - the item to remove from the layout
|
||
*/
|
||
removeBox: function(chart, layoutItem) {
|
||
var index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;
|
||
if (index !== -1) {
|
||
chart.boxes.splice(index, 1);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Sets (or updates) options on the given `item`.
|
||
* @param {Chart} chart - the chart in which the item lives (or will be added to)
|
||
* @param {ILayoutItem} item - the item to configure with the given options
|
||
* @param {object} options - the new item options.
|
||
*/
|
||
configure: function(chart, item, options) {
|
||
var props = ['fullWidth', 'position', 'weight'];
|
||
var ilen = props.length;
|
||
var i = 0;
|
||
var prop;
|
||
|
||
for (; i < ilen; ++i) {
|
||
prop = props[i];
|
||
if (options.hasOwnProperty(prop)) {
|
||
item[prop] = options[prop];
|
||
}
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Fits boxes of the given chart into the given size by having each box measure itself
|
||
* then running a fitting algorithm
|
||
* @param {Chart} chart - the chart
|
||
* @param {number} width - the width to fit into
|
||
* @param {number} height - the height to fit into
|
||
*/
|
||
update: function(chart, width, height) {
|
||
if (!chart) {
|
||
return;
|
||
}
|
||
|
||
var layoutOptions = chart.options.layout || {};
|
||
var padding = helpers$1.options.toPadding(layoutOptions.padding);
|
||
|
||
var availableWidth = width - padding.width;
|
||
var availableHeight = height - padding.height;
|
||
var boxes = buildLayoutBoxes(chart.boxes);
|
||
var verticalBoxes = boxes.vertical;
|
||
var horizontalBoxes = boxes.horizontal;
|
||
|
||
// Essentially we now have any number of boxes on each of the 4 sides.
|
||
// Our canvas looks like the following.
|
||
// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
|
||
// B1 is the bottom axis
|
||
// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays
|
||
// These locations are single-box locations only, when trying to register a chartArea location that is already taken,
|
||
// an error will be thrown.
|
||
//
|
||
// |----------------------------------------------------|
|
||
// | T1 (Full Width) |
|
||
// |----------------------------------------------------|
|
||
// | | | T2 | |
|
||
// | |----|-------------------------------------|----|
|
||
// | | | C1 | | C2 | |
|
||
// | | |----| |----| |
|
||
// | | | | |
|
||
// | L1 | L2 | ChartArea (C0) | R1 |
|
||
// | | | | |
|
||
// | | |----| |----| |
|
||
// | | | C3 | | C4 | |
|
||
// | |----|-------------------------------------|----|
|
||
// | | | B1 | |
|
||
// |----------------------------------------------------|
|
||
// | B2 (Full Width) |
|
||
// |----------------------------------------------------|
|
||
//
|
||
|
||
var params = Object.freeze({
|
||
outerWidth: width,
|
||
outerHeight: height,
|
||
padding: padding,
|
||
availableWidth: availableWidth,
|
||
vBoxMaxWidth: availableWidth / 2 / verticalBoxes.length,
|
||
hBoxMaxHeight: availableHeight / 2
|
||
});
|
||
var chartArea = extend({
|
||
maxPadding: extend({}, padding),
|
||
w: availableWidth,
|
||
h: availableHeight,
|
||
x: padding.left,
|
||
y: padding.top
|
||
}, padding);
|
||
|
||
setLayoutDims(verticalBoxes.concat(horizontalBoxes), params);
|
||
|
||
// First fit vertical boxes
|
||
fitBoxes(verticalBoxes, chartArea, params);
|
||
|
||
// Then fit horizontal boxes
|
||
if (fitBoxes(horizontalBoxes, chartArea, params)) {
|
||
// if the area changed, re-fit vertical boxes
|
||
fitBoxes(verticalBoxes, chartArea, params);
|
||
}
|
||
|
||
handleMaxPadding(chartArea);
|
||
|
||
// Finally place the boxes to correct coordinates
|
||
placeBoxes(boxes.leftAndTop, chartArea, params);
|
||
|
||
// Move to opposite side of chart
|
||
chartArea.x += chartArea.w;
|
||
chartArea.y += chartArea.h;
|
||
|
||
placeBoxes(boxes.rightAndBottom, chartArea, params);
|
||
|
||
chart.chartArea = {
|
||
left: chartArea.left,
|
||
top: chartArea.top,
|
||
right: chartArea.left + chartArea.w,
|
||
bottom: chartArea.top + chartArea.h
|
||
};
|
||
|
||
// Finally update boxes in chartArea (radial scale for example)
|
||
helpers$1.each(boxes.chartArea, function(layout) {
|
||
var box = layout.box;
|
||
extend(box, chart.chartArea);
|
||
box.update(chartArea.w, chartArea.h);
|
||
});
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Platform fallback implementation (minimal).
|
||
* @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939
|
||
*/
|
||
|
||
var platform_basic = {
|
||
acquireContext: function(item) {
|
||
if (item && item.canvas) {
|
||
// Support for any object associated to a canvas (including a context2d)
|
||
item = item.canvas;
|
||
}
|
||
|
||
return item && item.getContext('2d') || null;
|
||
}
|
||
};
|
||
|
||
var platform_dom = "/*\r\n * DOM element rendering detection\r\n * https://davidwalsh.name/detect-node-insertion\r\n */\r\n@keyframes chartjs-render-animation {\r\n\tfrom { opacity: 0.99; }\r\n\tto { opacity: 1; }\r\n}\r\n\r\n.chartjs-render-monitor {\r\n\tanimation: chartjs-render-animation 0.001s;\r\n}\r\n\r\n/*\r\n * DOM element resizing detection\r\n * https://github.com/marcj/css-element-queries\r\n */\r\n.chartjs-size-monitor,\r\n.chartjs-size-monitor-expand,\r\n.chartjs-size-monitor-shrink {\r\n\tposition: absolute;\r\n\tdirection: ltr;\r\n\tleft: 0;\r\n\ttop: 0;\r\n\tright: 0;\r\n\tbottom: 0;\r\n\toverflow: hidden;\r\n\tpointer-events: none;\r\n\tvisibility: hidden;\r\n\tz-index: -1;\r\n}\r\n\r\n.chartjs-size-monitor-expand > div {\r\n\tposition: absolute;\r\n\twidth: 1000000px;\r\n\theight: 1000000px;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n\r\n.chartjs-size-monitor-shrink > div {\r\n\tposition: absolute;\r\n\twidth: 200%;\r\n\theight: 200%;\r\n\tleft: 0;\r\n\ttop: 0;\r\n}\r\n";
|
||
|
||
var platform_dom$1 = /*#__PURE__*/Object.freeze({
|
||
__proto__: null,
|
||
'default': platform_dom
|
||
});
|
||
|
||
var stylesheet = getCjsExportFromNamespace(platform_dom$1);
|
||
|
||
var EXPANDO_KEY = '$chartjs';
|
||
var CSS_PREFIX = 'chartjs-';
|
||
var CSS_SIZE_MONITOR = CSS_PREFIX + 'size-monitor';
|
||
var CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';
|
||
var CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';
|
||
var ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];
|
||
|
||
/**
|
||
* DOM event types -> Chart.js event types.
|
||
* Note: only events with different types are mapped.
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/Events
|
||
*/
|
||
var EVENT_TYPES = {
|
||
touchstart: 'mousedown',
|
||
touchmove: 'mousemove',
|
||
touchend: 'mouseup',
|
||
pointerenter: 'mouseenter',
|
||
pointerdown: 'mousedown',
|
||
pointermove: 'mousemove',
|
||
pointerup: 'mouseup',
|
||
pointerleave: 'mouseout',
|
||
pointerout: 'mouseout'
|
||
};
|
||
|
||
/**
|
||
* The "used" size is the final value of a dimension property after all calculations have
|
||
* been performed. This method uses the computed style of `element` but returns undefined
|
||
* if the computed style is not expressed in pixels. That can happen in some cases where
|
||
* `element` has a size relative to its parent and this last one is not yet displayed,
|
||
* for example because of `display: none` on a parent node.
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value
|
||
* @returns {number} Size in pixels or undefined if unknown.
|
||
*/
|
||
function readUsedSize(element, property) {
|
||
var value = helpers$1.getStyle(element, property);
|
||
var matches = value && value.match(/^(\d+)(\.\d+)?px$/);
|
||
return matches ? Number(matches[1]) : undefined;
|
||
}
|
||
|
||
/**
|
||
* Initializes the canvas style and render size without modifying the canvas display size,
|
||
* since responsiveness is handled by the controller.resize() method. The config is used
|
||
* to determine the aspect ratio to apply in case no explicit height has been specified.
|
||
*/
|
||
function initCanvas(canvas, config) {
|
||
var style = canvas.style;
|
||
|
||
// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it
|
||
// returns null or '' if no explicit value has been set to the canvas attribute.
|
||
var renderHeight = canvas.getAttribute('height');
|
||
var renderWidth = canvas.getAttribute('width');
|
||
|
||
// Chart.js modifies some canvas values that we want to restore on destroy
|
||
canvas[EXPANDO_KEY] = {
|
||
initial: {
|
||
height: renderHeight,
|
||
width: renderWidth,
|
||
style: {
|
||
display: style.display,
|
||
height: style.height,
|
||
width: style.width
|
||
}
|
||
}
|
||
};
|
||
|
||
// Force canvas to display as block to avoid extra space caused by inline
|
||
// elements, which would interfere with the responsive resize process.
|
||
// https://github.com/chartjs/Chart.js/issues/2538
|
||
style.display = style.display || 'block';
|
||
|
||
if (renderWidth === null || renderWidth === '') {
|
||
var displayWidth = readUsedSize(canvas, 'width');
|
||
if (displayWidth !== undefined) {
|
||
canvas.width = displayWidth;
|
||
}
|
||
}
|
||
|
||
if (renderHeight === null || renderHeight === '') {
|
||
if (canvas.style.height === '') {
|
||
// If no explicit render height and style height, let's apply the aspect ratio,
|
||
// which one can be specified by the user but also by charts as default option
|
||
// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.
|
||
canvas.height = canvas.width / (config.options.aspectRatio || 2);
|
||
} else {
|
||
var displayHeight = readUsedSize(canvas, 'height');
|
||
if (displayWidth !== undefined) {
|
||
canvas.height = displayHeight;
|
||
}
|
||
}
|
||
}
|
||
|
||
return canvas;
|
||
}
|
||
|
||
/**
|
||
* Detects support for options object argument in addEventListener.
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
|
||
* @private
|
||
*/
|
||
var supportsEventListenerOptions = (function() {
|
||
var supports = false;
|
||
try {
|
||
var options = Object.defineProperty({}, 'passive', {
|
||
// eslint-disable-next-line getter-return
|
||
get: function() {
|
||
supports = true;
|
||
}
|
||
});
|
||
window.addEventListener('e', null, options);
|
||
} catch (e) {
|
||
// continue regardless of error
|
||
}
|
||
return supports;
|
||
}());
|
||
|
||
// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.
|
||
// https://github.com/chartjs/Chart.js/issues/4287
|
||
var eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;
|
||
|
||
function addListener(node, type, listener) {
|
||
node.addEventListener(type, listener, eventListenerOptions);
|
||
}
|
||
|
||
function removeListener(node, type, listener) {
|
||
node.removeEventListener(type, listener, eventListenerOptions);
|
||
}
|
||
|
||
function createEvent(type, chart, x, y, nativeEvent) {
|
||
return {
|
||
type: type,
|
||
chart: chart,
|
||
native: nativeEvent || null,
|
||
x: x !== undefined ? x : null,
|
||
y: y !== undefined ? y : null,
|
||
};
|
||
}
|
||
|
||
function fromNativeEvent(event, chart) {
|
||
var type = EVENT_TYPES[event.type] || event.type;
|
||
var pos = helpers$1.getRelativePosition(event, chart);
|
||
return createEvent(type, chart, pos.x, pos.y, event);
|
||
}
|
||
|
||
function throttled(fn, thisArg) {
|
||
var ticking = false;
|
||
var args = [];
|
||
|
||
return function() {
|
||
args = Array.prototype.slice.call(arguments);
|
||
thisArg = thisArg || this;
|
||
|
||
if (!ticking) {
|
||
ticking = true;
|
||
helpers$1.requestAnimFrame.call(window, function() {
|
||
ticking = false;
|
||
fn.apply(thisArg, args);
|
||
});
|
||
}
|
||
};
|
||
}
|
||
|
||
function createDiv(cls) {
|
||
var el = document.createElement('div');
|
||
el.className = cls || '';
|
||
return el;
|
||
}
|
||
|
||
// Implementation based on https://github.com/marcj/css-element-queries
|
||
function createResizer(handler) {
|
||
var maxSize = 1000000;
|
||
|
||
// NOTE(SB) Don't use innerHTML because it could be considered unsafe.
|
||
// https://github.com/chartjs/Chart.js/issues/5902
|
||
var resizer = createDiv(CSS_SIZE_MONITOR);
|
||
var expand = createDiv(CSS_SIZE_MONITOR + '-expand');
|
||
var shrink = createDiv(CSS_SIZE_MONITOR + '-shrink');
|
||
|
||
expand.appendChild(createDiv());
|
||
shrink.appendChild(createDiv());
|
||
|
||
resizer.appendChild(expand);
|
||
resizer.appendChild(shrink);
|
||
resizer._reset = function() {
|
||
expand.scrollLeft = maxSize;
|
||
expand.scrollTop = maxSize;
|
||
shrink.scrollLeft = maxSize;
|
||
shrink.scrollTop = maxSize;
|
||
};
|
||
|
||
var onScroll = function() {
|
||
resizer._reset();
|
||
handler();
|
||
};
|
||
|
||
addListener(expand, 'scroll', onScroll.bind(expand, 'expand'));
|
||
addListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));
|
||
|
||
return resizer;
|
||
}
|
||
|
||
// https://davidwalsh.name/detect-node-insertion
|
||
function watchForRender(node, handler) {
|
||
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
|
||
var proxy = expando.renderProxy = function(e) {
|
||
if (e.animationName === CSS_RENDER_ANIMATION) {
|
||
handler();
|
||
}
|
||
};
|
||
|
||
helpers$1.each(ANIMATION_START_EVENTS, function(type) {
|
||
addListener(node, type, proxy);
|
||
});
|
||
|
||
// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class
|
||
// is removed then added back immediately (same animation frame?). Accessing the
|
||
// `offsetParent` property will force a reflow and re-evaluate the CSS animation.
|
||
// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics
|
||
// https://github.com/chartjs/Chart.js/issues/4737
|
||
expando.reflow = !!node.offsetParent;
|
||
|
||
node.classList.add(CSS_RENDER_MONITOR);
|
||
}
|
||
|
||
function unwatchForRender(node) {
|
||
var expando = node[EXPANDO_KEY] || {};
|
||
var proxy = expando.renderProxy;
|
||
|
||
if (proxy) {
|
||
helpers$1.each(ANIMATION_START_EVENTS, function(type) {
|
||
removeListener(node, type, proxy);
|
||
});
|
||
|
||
delete expando.renderProxy;
|
||
}
|
||
|
||
node.classList.remove(CSS_RENDER_MONITOR);
|
||
}
|
||
|
||
function addResizeListener(node, listener, chart) {
|
||
var expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});
|
||
|
||
// Let's keep track of this added resizer and thus avoid DOM query when removing it.
|
||
var resizer = expando.resizer = createResizer(throttled(function() {
|
||
if (expando.resizer) {
|
||
var container = chart.options.maintainAspectRatio && node.parentNode;
|
||
var w = container ? container.clientWidth : 0;
|
||
listener(createEvent('resize', chart));
|
||
if (container && container.clientWidth < w && chart.canvas) {
|
||
// If the container size shrank during chart resize, let's assume
|
||
// scrollbar appeared. So we resize again with the scrollbar visible -
|
||
// effectively making chart smaller and the scrollbar hidden again.
|
||
// Because we are inside `throttled`, and currently `ticking`, scroll
|
||
// events are ignored during this whole 2 resize process.
|
||
// If we assumed wrong and something else happened, we are resizing
|
||
// twice in a frame (potential performance issue)
|
||
listener(createEvent('resize', chart));
|
||
}
|
||
}
|
||
}));
|
||
|
||
// The resizer needs to be attached to the node parent, so we first need to be
|
||
// sure that `node` is attached to the DOM before injecting the resizer element.
|
||
watchForRender(node, function() {
|
||
if (expando.resizer) {
|
||
var container = node.parentNode;
|
||
if (container && container !== resizer.parentNode) {
|
||
container.insertBefore(resizer, container.firstChild);
|
||
}
|
||
|
||
// The container size might have changed, let's reset the resizer state.
|
||
resizer._reset();
|
||
}
|
||
});
|
||
}
|
||
|
||
function removeResizeListener(node) {
|
||
var expando = node[EXPANDO_KEY] || {};
|
||
var resizer = expando.resizer;
|
||
|
||
delete expando.resizer;
|
||
unwatchForRender(node);
|
||
|
||
if (resizer && resizer.parentNode) {
|
||
resizer.parentNode.removeChild(resizer);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Injects CSS styles inline if the styles are not already present.
|
||
* @param {HTMLDocument|ShadowRoot} rootNode - the node to contain the <style>.
|
||
* @param {string} css - the CSS to be injected.
|
||
*/
|
||
function injectCSS(rootNode, css) {
|
||
// https://stackoverflow.com/q/3922139
|
||
var expando = rootNode[EXPANDO_KEY] || (rootNode[EXPANDO_KEY] = {});
|
||
if (!expando.containsStyles) {
|
||
expando.containsStyles = true;
|
||
css = '/* Chart.js */\n' + css;
|
||
var style = document.createElement('style');
|
||
style.setAttribute('type', 'text/css');
|
||
style.appendChild(document.createTextNode(css));
|
||
rootNode.appendChild(style);
|
||
}
|
||
}
|
||
|
||
var platform_dom$2 = {
|
||
/**
|
||
* When `true`, prevents the automatic injection of the stylesheet required to
|
||
* correctly detect when the chart is added to the DOM and then resized. This
|
||
* switch has been added to allow external stylesheet (`dist/Chart(.min)?.js`)
|
||
* to be manually imported to make this library compatible with any CSP.
|
||
* See https://github.com/chartjs/Chart.js/issues/5208
|
||
*/
|
||
disableCSSInjection: false,
|
||
|
||
/**
|
||
* This property holds whether this platform is enabled for the current environment.
|
||
* Currently used by platform.js to select the proper implementation.
|
||
* @private
|
||
*/
|
||
_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',
|
||
|
||
/**
|
||
* Initializes resources that depend on platform options.
|
||
* @param {HTMLCanvasElement} canvas - The Canvas element.
|
||
* @private
|
||
*/
|
||
_ensureLoaded: function(canvas) {
|
||
if (!this.disableCSSInjection) {
|
||
// If the canvas is in a shadow DOM, then the styles must also be inserted
|
||
// into the same shadow DOM.
|
||
// https://github.com/chartjs/Chart.js/issues/5763
|
||
var root = canvas.getRootNode ? canvas.getRootNode() : document;
|
||
var targetNode = root.host ? root : document.head;
|
||
injectCSS(targetNode, stylesheet);
|
||
}
|
||
},
|
||
|
||
acquireContext: function(item, config) {
|
||
if (typeof item === 'string') {
|
||
item = document.getElementById(item);
|
||
} else if (item.length) {
|
||
// Support for array based queries (such as jQuery)
|
||
item = item[0];
|
||
}
|
||
|
||
if (item && item.canvas) {
|
||
// Support for any object associated to a canvas (including a context2d)
|
||
item = item.canvas;
|
||
}
|
||
|
||
// To prevent canvas fingerprinting, some add-ons undefine the getContext
|
||
// method, for example: https://github.com/kkapsner/CanvasBlocker
|
||
// https://github.com/chartjs/Chart.js/issues/2807
|
||
var context = item && item.getContext && item.getContext('2d');
|
||
|
||
// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is
|
||
// inside an iframe or when running in a protected environment. We could guess the
|
||
// types from their toString() value but let's keep things flexible and assume it's
|
||
// a sufficient condition if the item has a context2D which has item as `canvas`.
|
||
// https://github.com/chartjs/Chart.js/issues/3887
|
||
// https://github.com/chartjs/Chart.js/issues/4102
|
||
// https://github.com/chartjs/Chart.js/issues/4152
|
||
if (context && context.canvas === item) {
|
||
// Load platform resources on first chart creation, to make it possible to
|
||
// import the library before setting platform options.
|
||
this._ensureLoaded(item);
|
||
initCanvas(item, config);
|
||
return context;
|
||
}
|
||
|
||
return null;
|
||
},
|
||
|
||
releaseContext: function(context) {
|
||
var canvas = context.canvas;
|
||
if (!canvas[EXPANDO_KEY]) {
|
||
return;
|
||
}
|
||
|
||
var initial = canvas[EXPANDO_KEY].initial;
|
||
['height', 'width'].forEach(function(prop) {
|
||
var value = initial[prop];
|
||
if (helpers$1.isNullOrUndef(value)) {
|
||
canvas.removeAttribute(prop);
|
||
} else {
|
||
canvas.setAttribute(prop, value);
|
||
}
|
||
});
|
||
|
||
helpers$1.each(initial.style || {}, function(value, key) {
|
||
canvas.style[key] = value;
|
||
});
|
||
|
||
// The canvas render size might have been changed (and thus the state stack discarded),
|
||
// we can't use save() and restore() to restore the initial state. So make sure that at
|
||
// least the canvas context is reset to the default state by setting the canvas width.
|
||
// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html
|
||
// eslint-disable-next-line no-self-assign
|
||
canvas.width = canvas.width;
|
||
|
||
delete canvas[EXPANDO_KEY];
|
||
},
|
||
|
||
addEventListener: function(chart, type, listener) {
|
||
var canvas = chart.canvas;
|
||
if (type === 'resize') {
|
||
// Note: the resize event is not supported on all browsers.
|
||
addResizeListener(canvas, listener, chart);
|
||
return;
|
||
}
|
||
|
||
var expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});
|
||
var proxies = expando.proxies || (expando.proxies = {});
|
||
var proxy = proxies[chart.id + '_' + type] = function(event) {
|
||
listener(fromNativeEvent(event, chart));
|
||
};
|
||
|
||
addListener(canvas, type, proxy);
|
||
},
|
||
|
||
removeEventListener: function(chart, type, listener) {
|
||
var canvas = chart.canvas;
|
||
if (type === 'resize') {
|
||
// Note: the resize event is not supported on all browsers.
|
||
removeResizeListener(canvas);
|
||
return;
|
||
}
|
||
|
||
var expando = listener[EXPANDO_KEY] || {};
|
||
var proxies = expando.proxies || {};
|
||
var proxy = proxies[chart.id + '_' + type];
|
||
if (!proxy) {
|
||
return;
|
||
}
|
||
|
||
removeListener(canvas, type, proxy);
|
||
}
|
||
};
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use EventTarget.addEventListener instead.
|
||
* EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
|
||
* @function Chart.helpers.addEvent
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers$1.addEvent = addListener;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use EventTarget.removeEventListener instead.
|
||
* EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+
|
||
* @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
|
||
* @function Chart.helpers.removeEvent
|
||
* @deprecated since version 2.7.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers$1.removeEvent = removeListener;
|
||
|
||
// @TODO Make possible to select another platform at build time.
|
||
var implementation = platform_dom$2._enabled ? platform_dom$2 : platform_basic;
|
||
|
||
/**
|
||
* @namespace Chart.platform
|
||
* @see https://chartjs.gitbooks.io/proposals/content/Platform.html
|
||
* @since 2.4.0
|
||
*/
|
||
var platform = helpers$1.extend({
|
||
/**
|
||
* @since 2.7.0
|
||
*/
|
||
initialize: function() {},
|
||
|
||
/**
|
||
* Called at chart construction time, returns a context2d instance implementing
|
||
* the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.
|
||
* @param {*} item - The native item from which to acquire context (platform specific)
|
||
* @param {object} options - The chart options
|
||
* @returns {CanvasRenderingContext2D} context2d instance
|
||
*/
|
||
acquireContext: function() {},
|
||
|
||
/**
|
||
* Called at chart destruction time, releases any resources associated to the context
|
||
* previously returned by the acquireContext() method.
|
||
* @param {CanvasRenderingContext2D} context - The context2d instance
|
||
* @returns {boolean} true if the method succeeded, else false
|
||
*/
|
||
releaseContext: function() {},
|
||
|
||
/**
|
||
* Registers the specified listener on the given chart.
|
||
* @param {Chart} chart - Chart from which to listen for event
|
||
* @param {string} type - The ({@link IEvent}) type to listen for
|
||
* @param {function} listener - Receives a notification (an object that implements
|
||
* the {@link IEvent} interface) when an event of the specified type occurs.
|
||
*/
|
||
addEventListener: function() {},
|
||
|
||
/**
|
||
* Removes the specified listener previously registered with addEventListener.
|
||
* @param {Chart} chart - Chart from which to remove the listener
|
||
* @param {string} type - The ({@link IEvent}) type to remove
|
||
* @param {function} listener - The listener function to remove from the event target.
|
||
*/
|
||
removeEventListener: function() {}
|
||
|
||
}, implementation);
|
||
|
||
core_defaults._set('global', {
|
||
plugins: {}
|
||
});
|
||
|
||
/**
|
||
* The plugin service singleton
|
||
* @namespace Chart.plugins
|
||
* @since 2.1.0
|
||
*/
|
||
var core_plugins = {
|
||
/**
|
||
* Globally registered plugins.
|
||
* @private
|
||
*/
|
||
_plugins: [],
|
||
|
||
/**
|
||
* This identifier is used to invalidate the descriptors cache attached to each chart
|
||
* when a global plugin is registered or unregistered. In this case, the cache ID is
|
||
* incremented and descriptors are regenerated during following API calls.
|
||
* @private
|
||
*/
|
||
_cacheId: 0,
|
||
|
||
/**
|
||
* Registers the given plugin(s) if not already registered.
|
||
* @param {IPlugin[]|IPlugin} plugins plugin instance(s).
|
||
*/
|
||
register: function(plugins) {
|
||
var p = this._plugins;
|
||
([]).concat(plugins).forEach(function(plugin) {
|
||
if (p.indexOf(plugin) === -1) {
|
||
p.push(plugin);
|
||
}
|
||
});
|
||
|
||
this._cacheId++;
|
||
},
|
||
|
||
/**
|
||
* Unregisters the given plugin(s) only if registered.
|
||
* @param {IPlugin[]|IPlugin} plugins plugin instance(s).
|
||
*/
|
||
unregister: function(plugins) {
|
||
var p = this._plugins;
|
||
([]).concat(plugins).forEach(function(plugin) {
|
||
var idx = p.indexOf(plugin);
|
||
if (idx !== -1) {
|
||
p.splice(idx, 1);
|
||
}
|
||
});
|
||
|
||
this._cacheId++;
|
||
},
|
||
|
||
/**
|
||
* Remove all registered plugins.
|
||
* @since 2.1.5
|
||
*/
|
||
clear: function() {
|
||
this._plugins = [];
|
||
this._cacheId++;
|
||
},
|
||
|
||
/**
|
||
* Returns the number of registered plugins?
|
||
* @returns {number}
|
||
* @since 2.1.5
|
||
*/
|
||
count: function() {
|
||
return this._plugins.length;
|
||
},
|
||
|
||
/**
|
||
* Returns all registered plugin instances.
|
||
* @returns {IPlugin[]} array of plugin objects.
|
||
* @since 2.1.5
|
||
*/
|
||
getAll: function() {
|
||
return this._plugins;
|
||
},
|
||
|
||
/**
|
||
* Calls enabled plugins for `chart` on the specified hook and with the given args.
|
||
* This method immediately returns as soon as a plugin explicitly returns false. The
|
||
* returned value can be used, for instance, to interrupt the current action.
|
||
* @param {Chart} chart - The chart instance for which plugins should be called.
|
||
* @param {string} hook - The name of the plugin method to call (e.g. 'beforeUpdate').
|
||
* @param {Array} [args] - Extra arguments to apply to the hook call.
|
||
* @returns {boolean} false if any of the plugins return false, else returns true.
|
||
*/
|
||
notify: function(chart, hook, args) {
|
||
var descriptors = this.descriptors(chart);
|
||
var ilen = descriptors.length;
|
||
var i, descriptor, plugin, params, method;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
descriptor = descriptors[i];
|
||
plugin = descriptor.plugin;
|
||
method = plugin[hook];
|
||
if (typeof method === 'function') {
|
||
params = [chart].concat(args || []);
|
||
params.push(descriptor.options);
|
||
if (method.apply(plugin, params) === false) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return true;
|
||
},
|
||
|
||
/**
|
||
* Returns descriptors of enabled plugins for the given chart.
|
||
* @returns {object[]} [{ plugin, options }]
|
||
* @private
|
||
*/
|
||
descriptors: function(chart) {
|
||
var cache = chart.$plugins || (chart.$plugins = {});
|
||
if (cache.id === this._cacheId) {
|
||
return cache.descriptors;
|
||
}
|
||
|
||
var plugins = [];
|
||
var descriptors = [];
|
||
var config = (chart && chart.config) || {};
|
||
var options = (config.options && config.options.plugins) || {};
|
||
|
||
this._plugins.concat(config.plugins || []).forEach(function(plugin) {
|
||
var idx = plugins.indexOf(plugin);
|
||
if (idx !== -1) {
|
||
return;
|
||
}
|
||
|
||
var id = plugin.id;
|
||
var opts = options[id];
|
||
if (opts === false) {
|
||
return;
|
||
}
|
||
|
||
if (opts === true) {
|
||
opts = helpers$1.clone(core_defaults.global.plugins[id]);
|
||
}
|
||
|
||
plugins.push(plugin);
|
||
descriptors.push({
|
||
plugin: plugin,
|
||
options: opts || {}
|
||
});
|
||
});
|
||
|
||
cache.descriptors = descriptors;
|
||
cache.id = this._cacheId;
|
||
return descriptors;
|
||
},
|
||
|
||
/**
|
||
* Invalidates cache for the given chart: descriptors hold a reference on plugin option,
|
||
* but in some cases, this reference can be changed by the user when updating options.
|
||
* https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
||
* @private
|
||
*/
|
||
_invalidate: function(chart) {
|
||
delete chart.$plugins;
|
||
}
|
||
};
|
||
|
||
var core_scaleService = {
|
||
// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
|
||
// use the new chart options to grab the correct scale
|
||
constructors: {},
|
||
// Use a registration function so that we can move to an ES6 map when we no longer need to support
|
||
// old browsers
|
||
|
||
// Scale config defaults
|
||
defaults: {},
|
||
registerScaleType: function(type, scaleConstructor, scaleDefaults) {
|
||
this.constructors[type] = scaleConstructor;
|
||
this.defaults[type] = helpers$1.clone(scaleDefaults);
|
||
},
|
||
getScaleConstructor: function(type) {
|
||
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
|
||
},
|
||
getScaleDefaults: function(type) {
|
||
// Return the scale defaults merged with the global settings so that we always use the latest ones
|
||
return this.defaults.hasOwnProperty(type) ? helpers$1.merge(Object.create(null), [core_defaults.scale, this.defaults[type]]) : {};
|
||
},
|
||
updateScaleDefaults: function(type, additions) {
|
||
var me = this;
|
||
if (me.defaults.hasOwnProperty(type)) {
|
||
me.defaults[type] = helpers$1.extend(me.defaults[type], additions);
|
||
}
|
||
},
|
||
addScalesToLayout: function(chart) {
|
||
// Adds each scale to the chart.boxes array to be sized accordingly
|
||
helpers$1.each(chart.scales, function(scale) {
|
||
// Set ILayoutItem parameters for backwards compatibility
|
||
scale.fullWidth = scale.options.fullWidth;
|
||
scale.position = scale.options.position;
|
||
scale.weight = scale.options.weight;
|
||
core_layouts.addBox(chart, scale);
|
||
});
|
||
}
|
||
};
|
||
|
||
var valueOrDefault$8 = helpers$1.valueOrDefault;
|
||
var getRtlHelper = helpers$1.rtl.getRtlAdapter;
|
||
|
||
core_defaults._set('global', {
|
||
tooltips: {
|
||
enabled: true,
|
||
custom: null,
|
||
mode: 'nearest',
|
||
position: 'average',
|
||
intersect: true,
|
||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||
titleFontStyle: 'bold',
|
||
titleSpacing: 2,
|
||
titleMarginBottom: 6,
|
||
titleFontColor: '#fff',
|
||
titleAlign: 'left',
|
||
bodySpacing: 2,
|
||
bodyFontColor: '#fff',
|
||
bodyAlign: 'left',
|
||
footerFontStyle: 'bold',
|
||
footerSpacing: 2,
|
||
footerMarginTop: 6,
|
||
footerFontColor: '#fff',
|
||
footerAlign: 'left',
|
||
yPadding: 6,
|
||
xPadding: 6,
|
||
caretPadding: 2,
|
||
caretSize: 5,
|
||
cornerRadius: 6,
|
||
multiKeyBackground: '#fff',
|
||
displayColors: true,
|
||
borderColor: 'rgba(0,0,0,0)',
|
||
borderWidth: 0,
|
||
callbacks: {
|
||
// Args are: (tooltipItems, data)
|
||
beforeTitle: helpers$1.noop,
|
||
title: function(tooltipItems, data) {
|
||
var title = '';
|
||
var labels = data.labels;
|
||
var labelCount = labels ? labels.length : 0;
|
||
|
||
if (tooltipItems.length > 0) {
|
||
var item = tooltipItems[0];
|
||
if (item.label) {
|
||
title = item.label;
|
||
} else if (item.xLabel) {
|
||
title = item.xLabel;
|
||
} else if (labelCount > 0 && item.index < labelCount) {
|
||
title = labels[item.index];
|
||
}
|
||
}
|
||
|
||
return title;
|
||
},
|
||
afterTitle: helpers$1.noop,
|
||
|
||
// Args are: (tooltipItems, data)
|
||
beforeBody: helpers$1.noop,
|
||
|
||
// Args are: (tooltipItem, data)
|
||
beforeLabel: helpers$1.noop,
|
||
label: function(tooltipItem, data) {
|
||
var label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||
|
||
if (label) {
|
||
label += ': ';
|
||
}
|
||
if (!helpers$1.isNullOrUndef(tooltipItem.value)) {
|
||
label += tooltipItem.value;
|
||
} else {
|
||
label += tooltipItem.yLabel;
|
||
}
|
||
return label;
|
||
},
|
||
labelColor: function(tooltipItem, chart) {
|
||
var meta = chart.getDatasetMeta(tooltipItem.datasetIndex);
|
||
var activeElement = meta.data[tooltipItem.index];
|
||
var view = activeElement._view;
|
||
return {
|
||
borderColor: view.borderColor,
|
||
backgroundColor: view.backgroundColor
|
||
};
|
||
},
|
||
labelTextColor: function() {
|
||
return this._options.bodyFontColor;
|
||
},
|
||
afterLabel: helpers$1.noop,
|
||
|
||
// Args are: (tooltipItems, data)
|
||
afterBody: helpers$1.noop,
|
||
|
||
// Args are: (tooltipItems, data)
|
||
beforeFooter: helpers$1.noop,
|
||
footer: helpers$1.noop,
|
||
afterFooter: helpers$1.noop
|
||
}
|
||
}
|
||
});
|
||
|
||
var positioners = {
|
||
/**
|
||
* Average mode places the tooltip at the average position of the elements shown
|
||
* @function Chart.Tooltip.positioners.average
|
||
* @param elements {ChartElement[]} the elements being displayed in the tooltip
|
||
* @returns {object} tooltip position
|
||
*/
|
||
average: function(elements) {
|
||
if (!elements.length) {
|
||
return false;
|
||
}
|
||
|
||
var i, len;
|
||
var x = 0;
|
||
var y = 0;
|
||
var count = 0;
|
||
|
||
for (i = 0, len = elements.length; i < len; ++i) {
|
||
var el = elements[i];
|
||
if (el && el.hasValue()) {
|
||
var pos = el.tooltipPosition();
|
||
x += pos.x;
|
||
y += pos.y;
|
||
++count;
|
||
}
|
||
}
|
||
|
||
return {
|
||
x: x / count,
|
||
y: y / count
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Gets the tooltip position nearest of the item nearest to the event position
|
||
* @function Chart.Tooltip.positioners.nearest
|
||
* @param elements {Chart.Element[]} the tooltip elements
|
||
* @param eventPosition {object} the position of the event in canvas coordinates
|
||
* @returns {object} the tooltip position
|
||
*/
|
||
nearest: function(elements, eventPosition) {
|
||
var x = eventPosition.x;
|
||
var y = eventPosition.y;
|
||
var minDistance = Number.POSITIVE_INFINITY;
|
||
var i, len, nearestElement;
|
||
|
||
for (i = 0, len = elements.length; i < len; ++i) {
|
||
var el = elements[i];
|
||
if (el && el.hasValue()) {
|
||
var center = el.getCenterPoint();
|
||
var d = helpers$1.distanceBetweenPoints(eventPosition, center);
|
||
|
||
if (d < minDistance) {
|
||
minDistance = d;
|
||
nearestElement = el;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (nearestElement) {
|
||
var tp = nearestElement.tooltipPosition();
|
||
x = tp.x;
|
||
y = tp.y;
|
||
}
|
||
|
||
return {
|
||
x: x,
|
||
y: y
|
||
};
|
||
}
|
||
};
|
||
|
||
// Helper to push or concat based on if the 2nd parameter is an array or not
|
||
function pushOrConcat(base, toPush) {
|
||
if (toPush) {
|
||
if (helpers$1.isArray(toPush)) {
|
||
// base = base.concat(toPush);
|
||
Array.prototype.push.apply(base, toPush);
|
||
} else {
|
||
base.push(toPush);
|
||
}
|
||
}
|
||
|
||
return base;
|
||
}
|
||
|
||
/**
|
||
* Returns array of strings split by newline
|
||
* @param {string} value - The value to split by newline.
|
||
* @returns {string[]} value if newline present - Returned from String split() method
|
||
* @function
|
||
*/
|
||
function splitNewlines(str) {
|
||
if ((typeof str === 'string' || str instanceof String) && str.indexOf('\n') > -1) {
|
||
return str.split('\n');
|
||
}
|
||
return str;
|
||
}
|
||
|
||
|
||
/**
|
||
* Private helper to create a tooltip item model
|
||
* @param element - the chart element (point, arc, bar) to create the tooltip item for
|
||
* @return new tooltip item
|
||
*/
|
||
function createTooltipItem(element) {
|
||
var xScale = element._xScale;
|
||
var yScale = element._yScale || element._scale; // handle radar || polarArea charts
|
||
var index = element._index;
|
||
var datasetIndex = element._datasetIndex;
|
||
var controller = element._chart.getDatasetMeta(datasetIndex).controller;
|
||
var indexScale = controller._getIndexScale();
|
||
var valueScale = controller._getValueScale();
|
||
|
||
return {
|
||
xLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',
|
||
yLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',
|
||
label: indexScale ? '' + indexScale.getLabelForIndex(index, datasetIndex) : '',
|
||
value: valueScale ? '' + valueScale.getLabelForIndex(index, datasetIndex) : '',
|
||
index: index,
|
||
datasetIndex: datasetIndex,
|
||
x: element._model.x,
|
||
y: element._model.y
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Helper to get the reset model for the tooltip
|
||
* @param tooltipOpts {object} the tooltip options
|
||
*/
|
||
function getBaseModel(tooltipOpts) {
|
||
var globalDefaults = core_defaults.global;
|
||
|
||
return {
|
||
// Positioning
|
||
xPadding: tooltipOpts.xPadding,
|
||
yPadding: tooltipOpts.yPadding,
|
||
xAlign: tooltipOpts.xAlign,
|
||
yAlign: tooltipOpts.yAlign,
|
||
|
||
// Drawing direction and text direction
|
||
rtl: tooltipOpts.rtl,
|
||
textDirection: tooltipOpts.textDirection,
|
||
|
||
// Body
|
||
bodyFontColor: tooltipOpts.bodyFontColor,
|
||
_bodyFontFamily: valueOrDefault$8(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),
|
||
_bodyFontStyle: valueOrDefault$8(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),
|
||
_bodyAlign: tooltipOpts.bodyAlign,
|
||
bodyFontSize: valueOrDefault$8(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),
|
||
bodySpacing: tooltipOpts.bodySpacing,
|
||
|
||
// Title
|
||
titleFontColor: tooltipOpts.titleFontColor,
|
||
_titleFontFamily: valueOrDefault$8(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),
|
||
_titleFontStyle: valueOrDefault$8(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),
|
||
titleFontSize: valueOrDefault$8(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),
|
||
_titleAlign: tooltipOpts.titleAlign,
|
||
titleSpacing: tooltipOpts.titleSpacing,
|
||
titleMarginBottom: tooltipOpts.titleMarginBottom,
|
||
|
||
// Footer
|
||
footerFontColor: tooltipOpts.footerFontColor,
|
||
_footerFontFamily: valueOrDefault$8(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),
|
||
_footerFontStyle: valueOrDefault$8(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),
|
||
footerFontSize: valueOrDefault$8(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),
|
||
_footerAlign: tooltipOpts.footerAlign,
|
||
footerSpacing: tooltipOpts.footerSpacing,
|
||
footerMarginTop: tooltipOpts.footerMarginTop,
|
||
|
||
// Appearance
|
||
caretSize: tooltipOpts.caretSize,
|
||
cornerRadius: tooltipOpts.cornerRadius,
|
||
backgroundColor: tooltipOpts.backgroundColor,
|
||
opacity: 0,
|
||
legendColorBackground: tooltipOpts.multiKeyBackground,
|
||
displayColors: tooltipOpts.displayColors,
|
||
borderColor: tooltipOpts.borderColor,
|
||
borderWidth: tooltipOpts.borderWidth
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Get the size of the tooltip
|
||
*/
|
||
function getTooltipSize(tooltip, model) {
|
||
var ctx = tooltip._chart.ctx;
|
||
|
||
var height = model.yPadding * 2; // Tooltip Padding
|
||
var width = 0;
|
||
|
||
// Count of all lines in the body
|
||
var body = model.body;
|
||
var combinedBodyLength = body.reduce(function(count, bodyItem) {
|
||
return count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;
|
||
}, 0);
|
||
combinedBodyLength += model.beforeBody.length + model.afterBody.length;
|
||
|
||
var titleLineCount = model.title.length;
|
||
var footerLineCount = model.footer.length;
|
||
var titleFontSize = model.titleFontSize;
|
||
var bodyFontSize = model.bodyFontSize;
|
||
var footerFontSize = model.footerFontSize;
|
||
|
||
height += titleLineCount * titleFontSize; // Title Lines
|
||
height += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing
|
||
height += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin
|
||
height += combinedBodyLength * bodyFontSize; // Body Lines
|
||
height += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing
|
||
height += footerLineCount ? model.footerMarginTop : 0; // Footer Margin
|
||
height += footerLineCount * (footerFontSize); // Footer Lines
|
||
height += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing
|
||
|
||
// Title width
|
||
var widthPadding = 0;
|
||
var maxLineWidth = function(line) {
|
||
width = Math.max(width, ctx.measureText(line).width + widthPadding);
|
||
};
|
||
|
||
ctx.font = helpers$1.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);
|
||
helpers$1.each(model.title, maxLineWidth);
|
||
|
||
// Body width
|
||
ctx.font = helpers$1.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);
|
||
helpers$1.each(model.beforeBody.concat(model.afterBody), maxLineWidth);
|
||
|
||
// Body lines may include some extra width due to the color box
|
||
widthPadding = model.displayColors ? (bodyFontSize + 2) : 0;
|
||
helpers$1.each(body, function(bodyItem) {
|
||
helpers$1.each(bodyItem.before, maxLineWidth);
|
||
helpers$1.each(bodyItem.lines, maxLineWidth);
|
||
helpers$1.each(bodyItem.after, maxLineWidth);
|
||
});
|
||
|
||
// Reset back to 0
|
||
widthPadding = 0;
|
||
|
||
// Footer width
|
||
ctx.font = helpers$1.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);
|
||
helpers$1.each(model.footer, maxLineWidth);
|
||
|
||
// Add padding
|
||
width += 2 * model.xPadding;
|
||
|
||
return {
|
||
width: width,
|
||
height: height
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Helper to get the alignment of a tooltip given the size
|
||
*/
|
||
function determineAlignment(tooltip, size) {
|
||
var model = tooltip._model;
|
||
var chart = tooltip._chart;
|
||
var chartArea = tooltip._chart.chartArea;
|
||
var xAlign = 'center';
|
||
var yAlign = 'center';
|
||
|
||
if (model.y < size.height) {
|
||
yAlign = 'top';
|
||
} else if (model.y > (chart.height - size.height)) {
|
||
yAlign = 'bottom';
|
||
}
|
||
|
||
var lf, rf; // functions to determine left, right alignment
|
||
var olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart
|
||
var yf; // function to get the y alignment if the tooltip goes outside of the left or right edges
|
||
var midX = (chartArea.left + chartArea.right) / 2;
|
||
var midY = (chartArea.top + chartArea.bottom) / 2;
|
||
|
||
if (yAlign === 'center') {
|
||
lf = function(x) {
|
||
return x <= midX;
|
||
};
|
||
rf = function(x) {
|
||
return x > midX;
|
||
};
|
||
} else {
|
||
lf = function(x) {
|
||
return x <= (size.width / 2);
|
||
};
|
||
rf = function(x) {
|
||
return x >= (chart.width - (size.width / 2));
|
||
};
|
||
}
|
||
|
||
olf = function(x) {
|
||
return x + size.width + model.caretSize + model.caretPadding > chart.width;
|
||
};
|
||
orf = function(x) {
|
||
return x - size.width - model.caretSize - model.caretPadding < 0;
|
||
};
|
||
yf = function(y) {
|
||
return y <= midY ? 'top' : 'bottom';
|
||
};
|
||
|
||
if (lf(model.x)) {
|
||
xAlign = 'left';
|
||
|
||
// Is tooltip too wide and goes over the right side of the chart.?
|
||
if (olf(model.x)) {
|
||
xAlign = 'center';
|
||
yAlign = yf(model.y);
|
||
}
|
||
} else if (rf(model.x)) {
|
||
xAlign = 'right';
|
||
|
||
// Is tooltip too wide and goes outside left edge of canvas?
|
||
if (orf(model.x)) {
|
||
xAlign = 'center';
|
||
yAlign = yf(model.y);
|
||
}
|
||
}
|
||
|
||
var opts = tooltip._options;
|
||
return {
|
||
xAlign: opts.xAlign ? opts.xAlign : xAlign,
|
||
yAlign: opts.yAlign ? opts.yAlign : yAlign
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment
|
||
*/
|
||
function getBackgroundPoint(vm, size, alignment, chart) {
|
||
// Background Position
|
||
var x = vm.x;
|
||
var y = vm.y;
|
||
|
||
var caretSize = vm.caretSize;
|
||
var caretPadding = vm.caretPadding;
|
||
var cornerRadius = vm.cornerRadius;
|
||
var xAlign = alignment.xAlign;
|
||
var yAlign = alignment.yAlign;
|
||
var paddingAndSize = caretSize + caretPadding;
|
||
var radiusAndPadding = cornerRadius + caretPadding;
|
||
|
||
if (xAlign === 'right') {
|
||
x -= size.width;
|
||
} else if (xAlign === 'center') {
|
||
x -= (size.width / 2);
|
||
if (x + size.width > chart.width) {
|
||
x = chart.width - size.width;
|
||
}
|
||
if (x < 0) {
|
||
x = 0;
|
||
}
|
||
}
|
||
|
||
if (yAlign === 'top') {
|
||
y += paddingAndSize;
|
||
} else if (yAlign === 'bottom') {
|
||
y -= size.height + paddingAndSize;
|
||
} else {
|
||
y -= (size.height / 2);
|
||
}
|
||
|
||
if (yAlign === 'center') {
|
||
if (xAlign === 'left') {
|
||
x += paddingAndSize;
|
||
} else if (xAlign === 'right') {
|
||
x -= paddingAndSize;
|
||
}
|
||
} else if (xAlign === 'left') {
|
||
x -= radiusAndPadding;
|
||
} else if (xAlign === 'right') {
|
||
x += radiusAndPadding;
|
||
}
|
||
|
||
return {
|
||
x: x,
|
||
y: y
|
||
};
|
||
}
|
||
|
||
function getAlignedX(vm, align) {
|
||
return align === 'center'
|
||
? vm.x + vm.width / 2
|
||
: align === 'right'
|
||
? vm.x + vm.width - vm.xPadding
|
||
: vm.x + vm.xPadding;
|
||
}
|
||
|
||
/**
|
||
* Helper to build before and after body lines
|
||
*/
|
||
function getBeforeAfterBodyLines(callback) {
|
||
return pushOrConcat([], splitNewlines(callback));
|
||
}
|
||
|
||
var exports$4 = core_element.extend({
|
||
initialize: function() {
|
||
this._model = getBaseModel(this._options);
|
||
this._lastActive = [];
|
||
},
|
||
|
||
// Get the title
|
||
// Args are: (tooltipItem, data)
|
||
getTitle: function() {
|
||
var me = this;
|
||
var opts = me._options;
|
||
var callbacks = opts.callbacks;
|
||
|
||
var beforeTitle = callbacks.beforeTitle.apply(me, arguments);
|
||
var title = callbacks.title.apply(me, arguments);
|
||
var afterTitle = callbacks.afterTitle.apply(me, arguments);
|
||
|
||
var lines = [];
|
||
lines = pushOrConcat(lines, splitNewlines(beforeTitle));
|
||
lines = pushOrConcat(lines, splitNewlines(title));
|
||
lines = pushOrConcat(lines, splitNewlines(afterTitle));
|
||
|
||
return lines;
|
||
},
|
||
|
||
// Args are: (tooltipItem, data)
|
||
getBeforeBody: function() {
|
||
return getBeforeAfterBodyLines(this._options.callbacks.beforeBody.apply(this, arguments));
|
||
},
|
||
|
||
// Args are: (tooltipItem, data)
|
||
getBody: function(tooltipItems, data) {
|
||
var me = this;
|
||
var callbacks = me._options.callbacks;
|
||
var bodyItems = [];
|
||
|
||
helpers$1.each(tooltipItems, function(tooltipItem) {
|
||
var bodyItem = {
|
||
before: [],
|
||
lines: [],
|
||
after: []
|
||
};
|
||
pushOrConcat(bodyItem.before, splitNewlines(callbacks.beforeLabel.call(me, tooltipItem, data)));
|
||
pushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));
|
||
pushOrConcat(bodyItem.after, splitNewlines(callbacks.afterLabel.call(me, tooltipItem, data)));
|
||
|
||
bodyItems.push(bodyItem);
|
||
});
|
||
|
||
return bodyItems;
|
||
},
|
||
|
||
// Args are: (tooltipItem, data)
|
||
getAfterBody: function() {
|
||
return getBeforeAfterBodyLines(this._options.callbacks.afterBody.apply(this, arguments));
|
||
},
|
||
|
||
// Get the footer and beforeFooter and afterFooter lines
|
||
// Args are: (tooltipItem, data)
|
||
getFooter: function() {
|
||
var me = this;
|
||
var callbacks = me._options.callbacks;
|
||
|
||
var beforeFooter = callbacks.beforeFooter.apply(me, arguments);
|
||
var footer = callbacks.footer.apply(me, arguments);
|
||
var afterFooter = callbacks.afterFooter.apply(me, arguments);
|
||
|
||
var lines = [];
|
||
lines = pushOrConcat(lines, splitNewlines(beforeFooter));
|
||
lines = pushOrConcat(lines, splitNewlines(footer));
|
||
lines = pushOrConcat(lines, splitNewlines(afterFooter));
|
||
|
||
return lines;
|
||
},
|
||
|
||
update: function(changed) {
|
||
var me = this;
|
||
var opts = me._options;
|
||
|
||
// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition
|
||
// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time
|
||
// which breaks any animations.
|
||
var existingModel = me._model;
|
||
var model = me._model = getBaseModel(opts);
|
||
var active = me._active;
|
||
|
||
var data = me._data;
|
||
|
||
// In the case where active.length === 0 we need to keep these at existing values for good animations
|
||
var alignment = {
|
||
xAlign: existingModel.xAlign,
|
||
yAlign: existingModel.yAlign
|
||
};
|
||
var backgroundPoint = {
|
||
x: existingModel.x,
|
||
y: existingModel.y
|
||
};
|
||
var tooltipSize = {
|
||
width: existingModel.width,
|
||
height: existingModel.height
|
||
};
|
||
var tooltipPosition = {
|
||
x: existingModel.caretX,
|
||
y: existingModel.caretY
|
||
};
|
||
|
||
var i, len;
|
||
|
||
if (active.length) {
|
||
model.opacity = 1;
|
||
|
||
var labelColors = [];
|
||
var labelTextColors = [];
|
||
tooltipPosition = positioners[opts.position].call(me, active, me._eventPosition);
|
||
|
||
var tooltipItems = [];
|
||
for (i = 0, len = active.length; i < len; ++i) {
|
||
tooltipItems.push(createTooltipItem(active[i]));
|
||
}
|
||
|
||
// If the user provided a filter function, use it to modify the tooltip items
|
||
if (opts.filter) {
|
||
tooltipItems = tooltipItems.filter(function(a) {
|
||
return opts.filter(a, data);
|
||
});
|
||
}
|
||
|
||
// If the user provided a sorting function, use it to modify the tooltip items
|
||
if (opts.itemSort) {
|
||
tooltipItems = tooltipItems.sort(function(a, b) {
|
||
return opts.itemSort(a, b, data);
|
||
});
|
||
}
|
||
|
||
// Determine colors for boxes
|
||
helpers$1.each(tooltipItems, function(tooltipItem) {
|
||
labelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));
|
||
labelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));
|
||
});
|
||
|
||
|
||
// Build the Text Lines
|
||
model.title = me.getTitle(tooltipItems, data);
|
||
model.beforeBody = me.getBeforeBody(tooltipItems, data);
|
||
model.body = me.getBody(tooltipItems, data);
|
||
model.afterBody = me.getAfterBody(tooltipItems, data);
|
||
model.footer = me.getFooter(tooltipItems, data);
|
||
|
||
// Initial positioning and colors
|
||
model.x = tooltipPosition.x;
|
||
model.y = tooltipPosition.y;
|
||
model.caretPadding = opts.caretPadding;
|
||
model.labelColors = labelColors;
|
||
model.labelTextColors = labelTextColors;
|
||
|
||
// data points
|
||
model.dataPoints = tooltipItems;
|
||
|
||
// We need to determine alignment of the tooltip
|
||
tooltipSize = getTooltipSize(this, model);
|
||
alignment = determineAlignment(this, tooltipSize);
|
||
// Final Size and Position
|
||
backgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);
|
||
} else {
|
||
model.opacity = 0;
|
||
}
|
||
|
||
model.xAlign = alignment.xAlign;
|
||
model.yAlign = alignment.yAlign;
|
||
model.x = backgroundPoint.x;
|
||
model.y = backgroundPoint.y;
|
||
model.width = tooltipSize.width;
|
||
model.height = tooltipSize.height;
|
||
|
||
// Point where the caret on the tooltip points to
|
||
model.caretX = tooltipPosition.x;
|
||
model.caretY = tooltipPosition.y;
|
||
|
||
me._model = model;
|
||
|
||
if (changed && opts.custom) {
|
||
opts.custom.call(me, model);
|
||
}
|
||
|
||
return me;
|
||
},
|
||
|
||
drawCaret: function(tooltipPoint, size) {
|
||
var ctx = this._chart.ctx;
|
||
var vm = this._view;
|
||
var caretPosition = this.getCaretPosition(tooltipPoint, size, vm);
|
||
|
||
ctx.lineTo(caretPosition.x1, caretPosition.y1);
|
||
ctx.lineTo(caretPosition.x2, caretPosition.y2);
|
||
ctx.lineTo(caretPosition.x3, caretPosition.y3);
|
||
},
|
||
getCaretPosition: function(tooltipPoint, size, vm) {
|
||
var x1, x2, x3, y1, y2, y3;
|
||
var caretSize = vm.caretSize;
|
||
var cornerRadius = vm.cornerRadius;
|
||
var xAlign = vm.xAlign;
|
||
var yAlign = vm.yAlign;
|
||
var ptX = tooltipPoint.x;
|
||
var ptY = tooltipPoint.y;
|
||
var width = size.width;
|
||
var height = size.height;
|
||
|
||
if (yAlign === 'center') {
|
||
y2 = ptY + (height / 2);
|
||
|
||
if (xAlign === 'left') {
|
||
x1 = ptX;
|
||
x2 = x1 - caretSize;
|
||
x3 = x1;
|
||
|
||
y1 = y2 + caretSize;
|
||
y3 = y2 - caretSize;
|
||
} else {
|
||
x1 = ptX + width;
|
||
x2 = x1 + caretSize;
|
||
x3 = x1;
|
||
|
||
y1 = y2 - caretSize;
|
||
y3 = y2 + caretSize;
|
||
}
|
||
} else {
|
||
if (xAlign === 'left') {
|
||
x2 = ptX + cornerRadius + (caretSize);
|
||
x1 = x2 - caretSize;
|
||
x3 = x2 + caretSize;
|
||
} else if (xAlign === 'right') {
|
||
x2 = ptX + width - cornerRadius - caretSize;
|
||
x1 = x2 - caretSize;
|
||
x3 = x2 + caretSize;
|
||
} else {
|
||
x2 = vm.caretX;
|
||
x1 = x2 - caretSize;
|
||
x3 = x2 + caretSize;
|
||
}
|
||
if (yAlign === 'top') {
|
||
y1 = ptY;
|
||
y2 = y1 - caretSize;
|
||
y3 = y1;
|
||
} else {
|
||
y1 = ptY + height;
|
||
y2 = y1 + caretSize;
|
||
y3 = y1;
|
||
// invert drawing order
|
||
var tmp = x3;
|
||
x3 = x1;
|
||
x1 = tmp;
|
||
}
|
||
}
|
||
return {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};
|
||
},
|
||
|
||
drawTitle: function(pt, vm, ctx) {
|
||
var title = vm.title;
|
||
var length = title.length;
|
||
var titleFontSize, titleSpacing, i;
|
||
|
||
if (length) {
|
||
var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
|
||
|
||
pt.x = getAlignedX(vm, vm._titleAlign);
|
||
|
||
ctx.textAlign = rtlHelper.textAlign(vm._titleAlign);
|
||
ctx.textBaseline = 'middle';
|
||
|
||
titleFontSize = vm.titleFontSize;
|
||
titleSpacing = vm.titleSpacing;
|
||
|
||
ctx.fillStyle = vm.titleFontColor;
|
||
ctx.font = helpers$1.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);
|
||
|
||
for (i = 0; i < length; ++i) {
|
||
ctx.fillText(title[i], rtlHelper.x(pt.x), pt.y + titleFontSize / 2);
|
||
pt.y += titleFontSize + titleSpacing; // Line Height and spacing
|
||
|
||
if (i + 1 === length) {
|
||
pt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing
|
||
}
|
||
}
|
||
}
|
||
},
|
||
|
||
drawBody: function(pt, vm, ctx) {
|
||
var bodyFontSize = vm.bodyFontSize;
|
||
var bodySpacing = vm.bodySpacing;
|
||
var bodyAlign = vm._bodyAlign;
|
||
var body = vm.body;
|
||
var drawColorBoxes = vm.displayColors;
|
||
var xLinePadding = 0;
|
||
var colorX = drawColorBoxes ? getAlignedX(vm, 'left') : 0;
|
||
|
||
var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
|
||
|
||
var fillLineOfText = function(line) {
|
||
ctx.fillText(line, rtlHelper.x(pt.x + xLinePadding), pt.y + bodyFontSize / 2);
|
||
pt.y += bodyFontSize + bodySpacing;
|
||
};
|
||
|
||
var bodyItem, textColor, labelColors, lines, i, j, ilen, jlen;
|
||
var bodyAlignForCalculation = rtlHelper.textAlign(bodyAlign);
|
||
|
||
ctx.textAlign = bodyAlign;
|
||
ctx.textBaseline = 'middle';
|
||
ctx.font = helpers$1.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);
|
||
|
||
pt.x = getAlignedX(vm, bodyAlignForCalculation);
|
||
|
||
// Before body lines
|
||
ctx.fillStyle = vm.bodyFontColor;
|
||
helpers$1.each(vm.beforeBody, fillLineOfText);
|
||
|
||
xLinePadding = drawColorBoxes && bodyAlignForCalculation !== 'right'
|
||
? bodyAlign === 'center' ? (bodyFontSize / 2 + 1) : (bodyFontSize + 2)
|
||
: 0;
|
||
|
||
// Draw body lines now
|
||
for (i = 0, ilen = body.length; i < ilen; ++i) {
|
||
bodyItem = body[i];
|
||
textColor = vm.labelTextColors[i];
|
||
labelColors = vm.labelColors[i];
|
||
|
||
ctx.fillStyle = textColor;
|
||
helpers$1.each(bodyItem.before, fillLineOfText);
|
||
|
||
lines = bodyItem.lines;
|
||
for (j = 0, jlen = lines.length; j < jlen; ++j) {
|
||
// Draw Legend-like boxes if needed
|
||
if (drawColorBoxes) {
|
||
var rtlColorX = rtlHelper.x(colorX);
|
||
|
||
// Fill a white rect so that colours merge nicely if the opacity is < 1
|
||
ctx.fillStyle = vm.legendColorBackground;
|
||
ctx.fillRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
|
||
|
||
// Border
|
||
ctx.lineWidth = 1;
|
||
ctx.strokeStyle = labelColors.borderColor;
|
||
ctx.strokeRect(rtlHelper.leftForLtr(rtlColorX, bodyFontSize), pt.y, bodyFontSize, bodyFontSize);
|
||
|
||
// Inner square
|
||
ctx.fillStyle = labelColors.backgroundColor;
|
||
ctx.fillRect(rtlHelper.leftForLtr(rtlHelper.xPlus(rtlColorX, 1), bodyFontSize - 2), pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);
|
||
ctx.fillStyle = textColor;
|
||
}
|
||
|
||
fillLineOfText(lines[j]);
|
||
}
|
||
|
||
helpers$1.each(bodyItem.after, fillLineOfText);
|
||
}
|
||
|
||
// Reset back to 0 for after body
|
||
xLinePadding = 0;
|
||
|
||
// After body lines
|
||
helpers$1.each(vm.afterBody, fillLineOfText);
|
||
pt.y -= bodySpacing; // Remove last body spacing
|
||
},
|
||
|
||
drawFooter: function(pt, vm, ctx) {
|
||
var footer = vm.footer;
|
||
var length = footer.length;
|
||
var footerFontSize, i;
|
||
|
||
if (length) {
|
||
var rtlHelper = getRtlHelper(vm.rtl, vm.x, vm.width);
|
||
|
||
pt.x = getAlignedX(vm, vm._footerAlign);
|
||
pt.y += vm.footerMarginTop;
|
||
|
||
ctx.textAlign = rtlHelper.textAlign(vm._footerAlign);
|
||
ctx.textBaseline = 'middle';
|
||
|
||
footerFontSize = vm.footerFontSize;
|
||
|
||
ctx.fillStyle = vm.footerFontColor;
|
||
ctx.font = helpers$1.fontString(footerFontSize, vm._footerFontStyle, vm._footerFontFamily);
|
||
|
||
for (i = 0; i < length; ++i) {
|
||
ctx.fillText(footer[i], rtlHelper.x(pt.x), pt.y + footerFontSize / 2);
|
||
pt.y += footerFontSize + vm.footerSpacing;
|
||
}
|
||
}
|
||
},
|
||
|
||
drawBackground: function(pt, vm, ctx, tooltipSize) {
|
||
ctx.fillStyle = vm.backgroundColor;
|
||
ctx.strokeStyle = vm.borderColor;
|
||
ctx.lineWidth = vm.borderWidth;
|
||
var xAlign = vm.xAlign;
|
||
var yAlign = vm.yAlign;
|
||
var x = pt.x;
|
||
var y = pt.y;
|
||
var width = tooltipSize.width;
|
||
var height = tooltipSize.height;
|
||
var radius = vm.cornerRadius;
|
||
|
||
ctx.beginPath();
|
||
ctx.moveTo(x + radius, y);
|
||
if (yAlign === 'top') {
|
||
this.drawCaret(pt, tooltipSize);
|
||
}
|
||
ctx.lineTo(x + width - radius, y);
|
||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||
if (yAlign === 'center' && xAlign === 'right') {
|
||
this.drawCaret(pt, tooltipSize);
|
||
}
|
||
ctx.lineTo(x + width, y + height - radius);
|
||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
||
if (yAlign === 'bottom') {
|
||
this.drawCaret(pt, tooltipSize);
|
||
}
|
||
ctx.lineTo(x + radius, y + height);
|
||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||
if (yAlign === 'center' && xAlign === 'left') {
|
||
this.drawCaret(pt, tooltipSize);
|
||
}
|
||
ctx.lineTo(x, y + radius);
|
||
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||
ctx.closePath();
|
||
|
||
ctx.fill();
|
||
|
||
if (vm.borderWidth > 0) {
|
||
ctx.stroke();
|
||
}
|
||
},
|
||
|
||
draw: function() {
|
||
var ctx = this._chart.ctx;
|
||
var vm = this._view;
|
||
|
||
if (vm.opacity === 0) {
|
||
return;
|
||
}
|
||
|
||
var tooltipSize = {
|
||
width: vm.width,
|
||
height: vm.height
|
||
};
|
||
var pt = {
|
||
x: vm.x,
|
||
y: vm.y
|
||
};
|
||
|
||
// IE11/Edge does not like very small opacities, so snap to 0
|
||
var opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;
|
||
|
||
// Truthy/falsey value for empty tooltip
|
||
var hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;
|
||
|
||
if (this._options.enabled && hasTooltipContent) {
|
||
ctx.save();
|
||
ctx.globalAlpha = opacity;
|
||
|
||
// Draw Background
|
||
this.drawBackground(pt, vm, ctx, tooltipSize);
|
||
|
||
// Draw Title, Body, and Footer
|
||
pt.y += vm.yPadding;
|
||
|
||
helpers$1.rtl.overrideTextDirection(ctx, vm.textDirection);
|
||
|
||
// Titles
|
||
this.drawTitle(pt, vm, ctx);
|
||
|
||
// Body
|
||
this.drawBody(pt, vm, ctx);
|
||
|
||
// Footer
|
||
this.drawFooter(pt, vm, ctx);
|
||
|
||
helpers$1.rtl.restoreTextDirection(ctx, vm.textDirection);
|
||
|
||
ctx.restore();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Handle an event
|
||
* @private
|
||
* @param {IEvent} event - The event to handle
|
||
* @returns {boolean} true if the tooltip changed
|
||
*/
|
||
handleEvent: function(e) {
|
||
var me = this;
|
||
var options = me._options;
|
||
var changed = false;
|
||
|
||
me._lastActive = me._lastActive || [];
|
||
|
||
// Find Active Elements for tooltips
|
||
if (e.type === 'mouseout') {
|
||
me._active = [];
|
||
} else {
|
||
me._active = me._chart.getElementsAtEventForMode(e, options.mode, options);
|
||
if (options.reverse) {
|
||
me._active.reverse();
|
||
}
|
||
}
|
||
|
||
// Remember Last Actives
|
||
changed = !helpers$1.arrayEquals(me._active, me._lastActive);
|
||
|
||
// Only handle target event on tooltip change
|
||
if (changed) {
|
||
me._lastActive = me._active;
|
||
|
||
if (options.enabled || options.custom) {
|
||
me._eventPosition = {
|
||
x: e.x,
|
||
y: e.y
|
||
};
|
||
|
||
me.update(true);
|
||
me.pivot();
|
||
}
|
||
}
|
||
|
||
return changed;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* @namespace Chart.Tooltip.positioners
|
||
*/
|
||
var positioners_1 = positioners;
|
||
|
||
var core_tooltip = exports$4;
|
||
core_tooltip.positioners = positioners_1;
|
||
|
||
var valueOrDefault$9 = helpers$1.valueOrDefault;
|
||
|
||
core_defaults._set('global', {
|
||
elements: {},
|
||
events: [
|
||
'mousemove',
|
||
'mouseout',
|
||
'click',
|
||
'touchstart',
|
||
'touchmove'
|
||
],
|
||
hover: {
|
||
onHover: null,
|
||
mode: 'nearest',
|
||
intersect: true,
|
||
animationDuration: 400
|
||
},
|
||
onClick: null,
|
||
maintainAspectRatio: true,
|
||
responsive: true,
|
||
responsiveAnimationDuration: 0
|
||
});
|
||
|
||
/**
|
||
* Recursively merge the given config objects representing the `scales` option
|
||
* by incorporating scale defaults in `xAxes` and `yAxes` array items, then
|
||
* returns a deep copy of the result, thus doesn't alter inputs.
|
||
*/
|
||
function mergeScaleConfig(/* config objects ... */) {
|
||
return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
|
||
merger: function(key, target, source, options) {
|
||
if (key === 'xAxes' || key === 'yAxes') {
|
||
var slen = source[key].length;
|
||
var i, type, scale;
|
||
|
||
if (!target[key]) {
|
||
target[key] = [];
|
||
}
|
||
|
||
for (i = 0; i < slen; ++i) {
|
||
scale = source[key][i];
|
||
type = valueOrDefault$9(scale.type, key === 'xAxes' ? 'category' : 'linear');
|
||
|
||
if (i >= target[key].length) {
|
||
target[key].push({});
|
||
}
|
||
|
||
if (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {
|
||
// new/untyped scale or type changed: let's apply the new defaults
|
||
// then merge source scale to correctly overwrite the defaults.
|
||
helpers$1.merge(target[key][i], [core_scaleService.getScaleDefaults(type), scale]);
|
||
} else {
|
||
// scales type are the same
|
||
helpers$1.merge(target[key][i], scale);
|
||
}
|
||
}
|
||
} else {
|
||
helpers$1._merger(key, target, source, options);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Recursively merge the given config objects as the root options by handling
|
||
* default scale options for the `scales` and `scale` properties, then returns
|
||
* a deep copy of the result, thus doesn't alter inputs.
|
||
*/
|
||
function mergeConfig(/* config objects ... */) {
|
||
return helpers$1.merge(Object.create(null), [].slice.call(arguments), {
|
||
merger: function(key, target, source, options) {
|
||
var tval = target[key] || Object.create(null);
|
||
var sval = source[key];
|
||
|
||
if (key === 'scales') {
|
||
// scale config merging is complex. Add our own function here for that
|
||
target[key] = mergeScaleConfig(tval, sval);
|
||
} else if (key === 'scale') {
|
||
// used in polar area & radar charts since there is only one scale
|
||
target[key] = helpers$1.merge(tval, [core_scaleService.getScaleDefaults(sval.type), sval]);
|
||
} else {
|
||
helpers$1._merger(key, target, source, options);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
function initConfig(config) {
|
||
config = config || Object.create(null);
|
||
|
||
// Do NOT use mergeConfig for the data object because this method merges arrays
|
||
// and so would change references to labels and datasets, preventing data updates.
|
||
var data = config.data = config.data || {};
|
||
data.datasets = data.datasets || [];
|
||
data.labels = data.labels || [];
|
||
|
||
config.options = mergeConfig(
|
||
core_defaults.global,
|
||
core_defaults[config.type],
|
||
config.options || {});
|
||
|
||
return config;
|
||
}
|
||
|
||
function updateConfig(chart) {
|
||
var newOptions = chart.options;
|
||
|
||
helpers$1.each(chart.scales, function(scale) {
|
||
core_layouts.removeBox(chart, scale);
|
||
});
|
||
|
||
newOptions = mergeConfig(
|
||
core_defaults.global,
|
||
core_defaults[chart.config.type],
|
||
newOptions);
|
||
|
||
chart.options = chart.config.options = newOptions;
|
||
chart.ensureScalesHaveIDs();
|
||
chart.buildOrUpdateScales();
|
||
|
||
// Tooltip
|
||
chart.tooltip._options = newOptions.tooltips;
|
||
chart.tooltip.initialize();
|
||
}
|
||
|
||
function nextAvailableScaleId(axesOpts, prefix, index) {
|
||
var id;
|
||
var hasId = function(obj) {
|
||
return obj.id === id;
|
||
};
|
||
|
||
do {
|
||
id = prefix + index++;
|
||
} while (helpers$1.findIndex(axesOpts, hasId) >= 0);
|
||
|
||
return id;
|
||
}
|
||
|
||
function positionIsHorizontal(position) {
|
||
return position === 'top' || position === 'bottom';
|
||
}
|
||
|
||
function compare2Level(l1, l2) {
|
||
return function(a, b) {
|
||
return a[l1] === b[l1]
|
||
? a[l2] - b[l2]
|
||
: a[l1] - b[l1];
|
||
};
|
||
}
|
||
|
||
var Chart = function(item, config) {
|
||
this.construct(item, config);
|
||
return this;
|
||
};
|
||
|
||
helpers$1.extend(Chart.prototype, /** @lends Chart */ {
|
||
/**
|
||
* @private
|
||
*/
|
||
construct: function(item, config) {
|
||
var me = this;
|
||
|
||
config = initConfig(config);
|
||
|
||
var context = platform.acquireContext(item, config);
|
||
var canvas = context && context.canvas;
|
||
var height = canvas && canvas.height;
|
||
var width = canvas && canvas.width;
|
||
|
||
me.id = helpers$1.uid();
|
||
me.ctx = context;
|
||
me.canvas = canvas;
|
||
me.config = config;
|
||
me.width = width;
|
||
me.height = height;
|
||
me.aspectRatio = height ? width / height : null;
|
||
me.options = config.options;
|
||
me._bufferedRender = false;
|
||
me._layers = [];
|
||
|
||
/**
|
||
* Provided for backward compatibility, Chart and Chart.Controller have been merged,
|
||
* the "instance" still need to be defined since it might be called from plugins.
|
||
* @prop Chart#chart
|
||
* @deprecated since version 2.6.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
me.chart = me;
|
||
me.controller = me; // chart.chart.controller #inception
|
||
|
||
// Add the chart instance to the global namespace
|
||
Chart.instances[me.id] = me;
|
||
|
||
// Define alias to the config data: `chart.data === chart.config.data`
|
||
Object.defineProperty(me, 'data', {
|
||
get: function() {
|
||
return me.config.data;
|
||
},
|
||
set: function(value) {
|
||
me.config.data = value;
|
||
}
|
||
});
|
||
|
||
if (!context || !canvas) {
|
||
// The given item is not a compatible context2d element, let's return before finalizing
|
||
// the chart initialization but after setting basic chart / controller properties that
|
||
// can help to figure out that the chart is not valid (e.g chart.canvas !== null);
|
||
// https://github.com/chartjs/Chart.js/issues/2807
|
||
console.error("Failed to create chart: can't acquire context from the given item");
|
||
return;
|
||
}
|
||
|
||
me.initialize();
|
||
me.update();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
initialize: function() {
|
||
var me = this;
|
||
|
||
// Before init plugin notification
|
||
core_plugins.notify(me, 'beforeInit');
|
||
|
||
helpers$1.retinaScale(me, me.options.devicePixelRatio);
|
||
|
||
me.bindEvents();
|
||
|
||
if (me.options.responsive) {
|
||
// Initial resize before chart draws (must be silent to preserve initial animations).
|
||
me.resize(true);
|
||
}
|
||
|
||
me.initToolTip();
|
||
|
||
// After init plugin notification
|
||
core_plugins.notify(me, 'afterInit');
|
||
|
||
return me;
|
||
},
|
||
|
||
clear: function() {
|
||
helpers$1.canvas.clear(this);
|
||
return this;
|
||
},
|
||
|
||
stop: function() {
|
||
// Stops any current animation loop occurring
|
||
core_animations.cancelAnimation(this);
|
||
return this;
|
||
},
|
||
|
||
resize: function(silent) {
|
||
var me = this;
|
||
var options = me.options;
|
||
var canvas = me.canvas;
|
||
var aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;
|
||
|
||
// the canvas render width and height will be casted to integers so make sure that
|
||
// the canvas display style uses the same integer values to avoid blurring effect.
|
||
|
||
// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collapsed
|
||
var newWidth = Math.max(0, Math.floor(helpers$1.getMaximumWidth(canvas)));
|
||
var newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers$1.getMaximumHeight(canvas)));
|
||
|
||
if (me.width === newWidth && me.height === newHeight) {
|
||
return;
|
||
}
|
||
|
||
canvas.width = me.width = newWidth;
|
||
canvas.height = me.height = newHeight;
|
||
canvas.style.width = newWidth + 'px';
|
||
canvas.style.height = newHeight + 'px';
|
||
|
||
helpers$1.retinaScale(me, options.devicePixelRatio);
|
||
|
||
if (!silent) {
|
||
// Notify any plugins about the resize
|
||
var newSize = {width: newWidth, height: newHeight};
|
||
core_plugins.notify(me, 'resize', [newSize]);
|
||
|
||
// Notify of resize
|
||
if (options.onResize) {
|
||
options.onResize(me, newSize);
|
||
}
|
||
|
||
me.stop();
|
||
me.update({
|
||
duration: options.responsiveAnimationDuration
|
||
});
|
||
}
|
||
},
|
||
|
||
ensureScalesHaveIDs: function() {
|
||
var options = this.options;
|
||
var scalesOptions = options.scales || {};
|
||
var scaleOptions = options.scale;
|
||
|
||
helpers$1.each(scalesOptions.xAxes, function(xAxisOptions, index) {
|
||
if (!xAxisOptions.id) {
|
||
xAxisOptions.id = nextAvailableScaleId(scalesOptions.xAxes, 'x-axis-', index);
|
||
}
|
||
});
|
||
|
||
helpers$1.each(scalesOptions.yAxes, function(yAxisOptions, index) {
|
||
if (!yAxisOptions.id) {
|
||
yAxisOptions.id = nextAvailableScaleId(scalesOptions.yAxes, 'y-axis-', index);
|
||
}
|
||
});
|
||
|
||
if (scaleOptions) {
|
||
scaleOptions.id = scaleOptions.id || 'scale';
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Builds a map of scale ID to scale object for future lookup.
|
||
*/
|
||
buildOrUpdateScales: function() {
|
||
var me = this;
|
||
var options = me.options;
|
||
var scales = me.scales || {};
|
||
var items = [];
|
||
var updated = Object.keys(scales).reduce(function(obj, id) {
|
||
obj[id] = false;
|
||
return obj;
|
||
}, {});
|
||
|
||
if (options.scales) {
|
||
items = items.concat(
|
||
(options.scales.xAxes || []).map(function(xAxisOptions) {
|
||
return {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};
|
||
}),
|
||
(options.scales.yAxes || []).map(function(yAxisOptions) {
|
||
return {options: yAxisOptions, dtype: 'linear', dposition: 'left'};
|
||
})
|
||
);
|
||
}
|
||
|
||
if (options.scale) {
|
||
items.push({
|
||
options: options.scale,
|
||
dtype: 'radialLinear',
|
||
isDefault: true,
|
||
dposition: 'chartArea'
|
||
});
|
||
}
|
||
|
||
helpers$1.each(items, function(item) {
|
||
var scaleOptions = item.options;
|
||
var id = scaleOptions.id;
|
||
var scaleType = valueOrDefault$9(scaleOptions.type, item.dtype);
|
||
|
||
if (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {
|
||
scaleOptions.position = item.dposition;
|
||
}
|
||
|
||
updated[id] = true;
|
||
var scale = null;
|
||
if (id in scales && scales[id].type === scaleType) {
|
||
scale = scales[id];
|
||
scale.options = scaleOptions;
|
||
scale.ctx = me.ctx;
|
||
scale.chart = me;
|
||
} else {
|
||
var scaleClass = core_scaleService.getScaleConstructor(scaleType);
|
||
if (!scaleClass) {
|
||
return;
|
||
}
|
||
scale = new scaleClass({
|
||
id: id,
|
||
type: scaleType,
|
||
options: scaleOptions,
|
||
ctx: me.ctx,
|
||
chart: me
|
||
});
|
||
scales[scale.id] = scale;
|
||
}
|
||
|
||
scale.mergeTicksOptions();
|
||
|
||
// TODO(SB): I think we should be able to remove this custom case (options.scale)
|
||
// and consider it as a regular scale part of the "scales"" map only! This would
|
||
// make the logic easier and remove some useless? custom code.
|
||
if (item.isDefault) {
|
||
me.scale = scale;
|
||
}
|
||
});
|
||
// clear up discarded scales
|
||
helpers$1.each(updated, function(hasUpdated, id) {
|
||
if (!hasUpdated) {
|
||
delete scales[id];
|
||
}
|
||
});
|
||
|
||
me.scales = scales;
|
||
|
||
core_scaleService.addScalesToLayout(this);
|
||
},
|
||
|
||
buildOrUpdateControllers: function() {
|
||
var me = this;
|
||
var newControllers = [];
|
||
var datasets = me.data.datasets;
|
||
var i, ilen;
|
||
|
||
for (i = 0, ilen = datasets.length; i < ilen; i++) {
|
||
var dataset = datasets[i];
|
||
var meta = me.getDatasetMeta(i);
|
||
var type = dataset.type || me.config.type;
|
||
|
||
if (meta.type && meta.type !== type) {
|
||
me.destroyDatasetMeta(i);
|
||
meta = me.getDatasetMeta(i);
|
||
}
|
||
meta.type = type;
|
||
meta.order = dataset.order || 0;
|
||
meta.index = i;
|
||
|
||
if (meta.controller) {
|
||
meta.controller.updateIndex(i);
|
||
meta.controller.linkScales();
|
||
} else {
|
||
var ControllerClass = controllers[meta.type];
|
||
if (ControllerClass === undefined) {
|
||
throw new Error('"' + meta.type + '" is not a chart type.');
|
||
}
|
||
|
||
meta.controller = new ControllerClass(me, i);
|
||
newControllers.push(meta.controller);
|
||
}
|
||
}
|
||
|
||
return newControllers;
|
||
},
|
||
|
||
/**
|
||
* Reset the elements of all datasets
|
||
* @private
|
||
*/
|
||
resetElements: function() {
|
||
var me = this;
|
||
helpers$1.each(me.data.datasets, function(dataset, datasetIndex) {
|
||
me.getDatasetMeta(datasetIndex).controller.reset();
|
||
}, me);
|
||
},
|
||
|
||
/**
|
||
* Resets the chart back to it's state before the initial animation
|
||
*/
|
||
reset: function() {
|
||
this.resetElements();
|
||
this.tooltip.initialize();
|
||
},
|
||
|
||
update: function(config) {
|
||
var me = this;
|
||
var i, ilen;
|
||
|
||
if (!config || typeof config !== 'object') {
|
||
// backwards compatibility
|
||
config = {
|
||
duration: config,
|
||
lazy: arguments[1]
|
||
};
|
||
}
|
||
|
||
updateConfig(me);
|
||
|
||
// plugins options references might have change, let's invalidate the cache
|
||
// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167
|
||
core_plugins._invalidate(me);
|
||
|
||
if (core_plugins.notify(me, 'beforeUpdate') === false) {
|
||
return;
|
||
}
|
||
|
||
// In case the entire data object changed
|
||
me.tooltip._data = me.data;
|
||
|
||
// Make sure dataset controllers are updated and new controllers are reset
|
||
var newControllers = me.buildOrUpdateControllers();
|
||
|
||
// Make sure all dataset controllers have correct meta data counts
|
||
for (i = 0, ilen = me.data.datasets.length; i < ilen; i++) {
|
||
me.getDatasetMeta(i).controller.buildOrUpdateElements();
|
||
}
|
||
|
||
me.updateLayout();
|
||
|
||
// Can only reset the new controllers after the scales have been updated
|
||
if (me.options.animation && me.options.animation.duration) {
|
||
helpers$1.each(newControllers, function(controller) {
|
||
controller.reset();
|
||
});
|
||
}
|
||
|
||
me.updateDatasets();
|
||
|
||
// Need to reset tooltip in case it is displayed with elements that are removed
|
||
// after update.
|
||
me.tooltip.initialize();
|
||
|
||
// Last active contains items that were previously in the tooltip.
|
||
// When we reset the tooltip, we need to clear it
|
||
me.lastActive = [];
|
||
|
||
// Do this before render so that any plugins that need final scale updates can use it
|
||
core_plugins.notify(me, 'afterUpdate');
|
||
|
||
me._layers.sort(compare2Level('z', '_idx'));
|
||
|
||
if (me._bufferedRender) {
|
||
me._bufferedRequest = {
|
||
duration: config.duration,
|
||
easing: config.easing,
|
||
lazy: config.lazy
|
||
};
|
||
} else {
|
||
me.render(config);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Updates the chart layout unless a plugin returns `false` to the `beforeLayout`
|
||
* hook, in which case, plugins will not be called on `afterLayout`.
|
||
* @private
|
||
*/
|
||
updateLayout: function() {
|
||
var me = this;
|
||
|
||
if (core_plugins.notify(me, 'beforeLayout') === false) {
|
||
return;
|
||
}
|
||
|
||
core_layouts.update(this, this.width, this.height);
|
||
|
||
me._layers = [];
|
||
helpers$1.each(me.boxes, function(box) {
|
||
// _configure is called twice, once in core.scale.update and once here.
|
||
// Here the boxes are fully updated and at their final positions.
|
||
if (box._configure) {
|
||
box._configure();
|
||
}
|
||
me._layers.push.apply(me._layers, box._layers());
|
||
}, me);
|
||
|
||
me._layers.forEach(function(item, index) {
|
||
item._idx = index;
|
||
});
|
||
|
||
/**
|
||
* Provided for backward compatibility, use `afterLayout` instead.
|
||
* @method IPlugin#afterScaleUpdate
|
||
* @deprecated since version 2.5.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_plugins.notify(me, 'afterScaleUpdate');
|
||
core_plugins.notify(me, 'afterLayout');
|
||
},
|
||
|
||
/**
|
||
* Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`
|
||
* hook, in which case, plugins will not be called on `afterDatasetsUpdate`.
|
||
* @private
|
||
*/
|
||
updateDatasets: function() {
|
||
var me = this;
|
||
|
||
if (core_plugins.notify(me, 'beforeDatasetsUpdate') === false) {
|
||
return;
|
||
}
|
||
|
||
for (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||
me.updateDataset(i);
|
||
}
|
||
|
||
core_plugins.notify(me, 'afterDatasetsUpdate');
|
||
},
|
||
|
||
/**
|
||
* Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`
|
||
* hook, in which case, plugins will not be called on `afterDatasetUpdate`.
|
||
* @private
|
||
*/
|
||
updateDataset: function(index) {
|
||
var me = this;
|
||
var meta = me.getDatasetMeta(index);
|
||
var args = {
|
||
meta: meta,
|
||
index: index
|
||
};
|
||
|
||
if (core_plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {
|
||
return;
|
||
}
|
||
|
||
meta.controller._update();
|
||
|
||
core_plugins.notify(me, 'afterDatasetUpdate', [args]);
|
||
},
|
||
|
||
render: function(config) {
|
||
var me = this;
|
||
|
||
if (!config || typeof config !== 'object') {
|
||
// backwards compatibility
|
||
config = {
|
||
duration: config,
|
||
lazy: arguments[1]
|
||
};
|
||
}
|
||
|
||
var animationOptions = me.options.animation;
|
||
var duration = valueOrDefault$9(config.duration, animationOptions && animationOptions.duration);
|
||
var lazy = config.lazy;
|
||
|
||
if (core_plugins.notify(me, 'beforeRender') === false) {
|
||
return;
|
||
}
|
||
|
||
var onComplete = function(animation) {
|
||
core_plugins.notify(me, 'afterRender');
|
||
helpers$1.callback(animationOptions && animationOptions.onComplete, [animation], me);
|
||
};
|
||
|
||
if (animationOptions && duration) {
|
||
var animation = new core_animation({
|
||
numSteps: duration / 16.66, // 60 fps
|
||
easing: config.easing || animationOptions.easing,
|
||
|
||
render: function(chart, animationObject) {
|
||
var easingFunction = helpers$1.easing.effects[animationObject.easing];
|
||
var currentStep = animationObject.currentStep;
|
||
var stepDecimal = currentStep / animationObject.numSteps;
|
||
|
||
chart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);
|
||
},
|
||
|
||
onAnimationProgress: animationOptions.onProgress,
|
||
onAnimationComplete: onComplete
|
||
});
|
||
|
||
core_animations.addAnimation(me, animation, duration, lazy);
|
||
} else {
|
||
me.draw();
|
||
|
||
// See https://github.com/chartjs/Chart.js/issues/3781
|
||
onComplete(new core_animation({numSteps: 0, chart: me}));
|
||
}
|
||
|
||
return me;
|
||
},
|
||
|
||
draw: function(easingValue) {
|
||
var me = this;
|
||
var i, layers;
|
||
|
||
me.clear();
|
||
|
||
if (helpers$1.isNullOrUndef(easingValue)) {
|
||
easingValue = 1;
|
||
}
|
||
|
||
me.transition(easingValue);
|
||
|
||
if (me.width <= 0 || me.height <= 0) {
|
||
return;
|
||
}
|
||
|
||
if (core_plugins.notify(me, 'beforeDraw', [easingValue]) === false) {
|
||
return;
|
||
}
|
||
|
||
// Because of plugin hooks (before/afterDatasetsDraw), datasets can't
|
||
// currently be part of layers. Instead, we draw
|
||
// layers <= 0 before(default, backward compat), and the rest after
|
||
layers = me._layers;
|
||
for (i = 0; i < layers.length && layers[i].z <= 0; ++i) {
|
||
layers[i].draw(me.chartArea);
|
||
}
|
||
|
||
me.drawDatasets(easingValue);
|
||
|
||
// Rest of layers
|
||
for (; i < layers.length; ++i) {
|
||
layers[i].draw(me.chartArea);
|
||
}
|
||
|
||
me._drawTooltip(easingValue);
|
||
|
||
core_plugins.notify(me, 'afterDraw', [easingValue]);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
transition: function(easingValue) {
|
||
var me = this;
|
||
|
||
for (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {
|
||
if (me.isDatasetVisible(i)) {
|
||
me.getDatasetMeta(i).controller.transition(easingValue);
|
||
}
|
||
}
|
||
|
||
me.tooltip.transition(easingValue);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getSortedDatasetMetas: function(filterVisible) {
|
||
var me = this;
|
||
var datasets = me.data.datasets || [];
|
||
var result = [];
|
||
var i, ilen;
|
||
|
||
for (i = 0, ilen = datasets.length; i < ilen; ++i) {
|
||
if (!filterVisible || me.isDatasetVisible(i)) {
|
||
result.push(me.getDatasetMeta(i));
|
||
}
|
||
}
|
||
|
||
result.sort(compare2Level('order', 'index'));
|
||
|
||
return result;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getSortedVisibleDatasetMetas: function() {
|
||
return this._getSortedDatasetMetas(true);
|
||
},
|
||
|
||
/**
|
||
* Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`
|
||
* hook, in which case, plugins will not be called on `afterDatasetsDraw`.
|
||
* @private
|
||
*/
|
||
drawDatasets: function(easingValue) {
|
||
var me = this;
|
||
var metasets, i;
|
||
|
||
if (core_plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {
|
||
return;
|
||
}
|
||
|
||
metasets = me._getSortedVisibleDatasetMetas();
|
||
for (i = metasets.length - 1; i >= 0; --i) {
|
||
me.drawDataset(metasets[i], easingValue);
|
||
}
|
||
|
||
core_plugins.notify(me, 'afterDatasetsDraw', [easingValue]);
|
||
},
|
||
|
||
/**
|
||
* Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`
|
||
* hook, in which case, plugins will not be called on `afterDatasetDraw`.
|
||
* @private
|
||
*/
|
||
drawDataset: function(meta, easingValue) {
|
||
var me = this;
|
||
var args = {
|
||
meta: meta,
|
||
index: meta.index,
|
||
easingValue: easingValue
|
||
};
|
||
|
||
if (core_plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {
|
||
return;
|
||
}
|
||
|
||
meta.controller.draw(easingValue);
|
||
|
||
core_plugins.notify(me, 'afterDatasetDraw', [args]);
|
||
},
|
||
|
||
/**
|
||
* Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`
|
||
* hook, in which case, plugins will not be called on `afterTooltipDraw`.
|
||
* @private
|
||
*/
|
||
_drawTooltip: function(easingValue) {
|
||
var me = this;
|
||
var tooltip = me.tooltip;
|
||
var args = {
|
||
tooltip: tooltip,
|
||
easingValue: easingValue
|
||
};
|
||
|
||
if (core_plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {
|
||
return;
|
||
}
|
||
|
||
tooltip.draw();
|
||
|
||
core_plugins.notify(me, 'afterTooltipDraw', [args]);
|
||
},
|
||
|
||
/**
|
||
* Get the single element that was clicked on
|
||
* @return An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw
|
||
*/
|
||
getElementAtEvent: function(e) {
|
||
return core_interaction.modes.single(this, e);
|
||
},
|
||
|
||
getElementsAtEvent: function(e) {
|
||
return core_interaction.modes.label(this, e, {intersect: true});
|
||
},
|
||
|
||
getElementsAtXAxis: function(e) {
|
||
return core_interaction.modes['x-axis'](this, e, {intersect: true});
|
||
},
|
||
|
||
getElementsAtEventForMode: function(e, mode, options) {
|
||
var method = core_interaction.modes[mode];
|
||
if (typeof method === 'function') {
|
||
return method(this, e, options);
|
||
}
|
||
|
||
return [];
|
||
},
|
||
|
||
getDatasetAtEvent: function(e) {
|
||
return core_interaction.modes.dataset(this, e, {intersect: true});
|
||
},
|
||
|
||
getDatasetMeta: function(datasetIndex) {
|
||
var me = this;
|
||
var dataset = me.data.datasets[datasetIndex];
|
||
if (!dataset._meta) {
|
||
dataset._meta = {};
|
||
}
|
||
|
||
var meta = dataset._meta[me.id];
|
||
if (!meta) {
|
||
meta = dataset._meta[me.id] = {
|
||
type: null,
|
||
data: [],
|
||
dataset: null,
|
||
controller: null,
|
||
hidden: null, // See isDatasetVisible() comment
|
||
xAxisID: null,
|
||
yAxisID: null,
|
||
order: dataset.order || 0,
|
||
index: datasetIndex
|
||
};
|
||
}
|
||
|
||
return meta;
|
||
},
|
||
|
||
getVisibleDatasetCount: function() {
|
||
var count = 0;
|
||
for (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {
|
||
if (this.isDatasetVisible(i)) {
|
||
count++;
|
||
}
|
||
}
|
||
return count;
|
||
},
|
||
|
||
isDatasetVisible: function(datasetIndex) {
|
||
var meta = this.getDatasetMeta(datasetIndex);
|
||
|
||
// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,
|
||
// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.
|
||
return typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;
|
||
},
|
||
|
||
generateLegend: function() {
|
||
return this.options.legendCallback(this);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
destroyDatasetMeta: function(datasetIndex) {
|
||
var id = this.id;
|
||
var dataset = this.data.datasets[datasetIndex];
|
||
var meta = dataset._meta && dataset._meta[id];
|
||
|
||
if (meta) {
|
||
meta.controller.destroy();
|
||
delete dataset._meta[id];
|
||
}
|
||
},
|
||
|
||
destroy: function() {
|
||
var me = this;
|
||
var canvas = me.canvas;
|
||
var i, ilen;
|
||
|
||
me.stop();
|
||
|
||
// dataset controllers need to cleanup associated data
|
||
for (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {
|
||
me.destroyDatasetMeta(i);
|
||
}
|
||
|
||
if (canvas) {
|
||
me.unbindEvents();
|
||
helpers$1.canvas.clear(me);
|
||
platform.releaseContext(me.ctx);
|
||
me.canvas = null;
|
||
me.ctx = null;
|
||
}
|
||
|
||
core_plugins.notify(me, 'destroy');
|
||
|
||
delete Chart.instances[me.id];
|
||
},
|
||
|
||
toBase64Image: function() {
|
||
return this.canvas.toDataURL.apply(this.canvas, arguments);
|
||
},
|
||
|
||
initToolTip: function() {
|
||
var me = this;
|
||
me.tooltip = new core_tooltip({
|
||
_chart: me,
|
||
_chartInstance: me, // deprecated, backward compatibility
|
||
_data: me.data,
|
||
_options: me.options.tooltips
|
||
}, me);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
bindEvents: function() {
|
||
var me = this;
|
||
var listeners = me._listeners = {};
|
||
var listener = function() {
|
||
me.eventHandler.apply(me, arguments);
|
||
};
|
||
|
||
helpers$1.each(me.options.events, function(type) {
|
||
platform.addEventListener(me, type, listener);
|
||
listeners[type] = listener;
|
||
});
|
||
|
||
// Elements used to detect size change should not be injected for non responsive charts.
|
||
// See https://github.com/chartjs/Chart.js/issues/2210
|
||
if (me.options.responsive) {
|
||
listener = function() {
|
||
me.resize();
|
||
};
|
||
|
||
platform.addEventListener(me, 'resize', listener);
|
||
listeners.resize = listener;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
unbindEvents: function() {
|
||
var me = this;
|
||
var listeners = me._listeners;
|
||
if (!listeners) {
|
||
return;
|
||
}
|
||
|
||
delete me._listeners;
|
||
helpers$1.each(listeners, function(listener, type) {
|
||
platform.removeEventListener(me, type, listener);
|
||
});
|
||
},
|
||
|
||
updateHoverStyle: function(elements, mode, enabled) {
|
||
var prefix = enabled ? 'set' : 'remove';
|
||
var element, i, ilen;
|
||
|
||
for (i = 0, ilen = elements.length; i < ilen; ++i) {
|
||
element = elements[i];
|
||
if (element) {
|
||
this.getDatasetMeta(element._datasetIndex).controller[prefix + 'HoverStyle'](element);
|
||
}
|
||
}
|
||
|
||
if (mode === 'dataset') {
|
||
this.getDatasetMeta(elements[0]._datasetIndex).controller['_' + prefix + 'DatasetHoverStyle']();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
eventHandler: function(e) {
|
||
var me = this;
|
||
var tooltip = me.tooltip;
|
||
|
||
if (core_plugins.notify(me, 'beforeEvent', [e]) === false) {
|
||
return;
|
||
}
|
||
|
||
// Buffer any update calls so that renders do not occur
|
||
me._bufferedRender = true;
|
||
me._bufferedRequest = null;
|
||
|
||
var changed = me.handleEvent(e);
|
||
// for smooth tooltip animations issue #4989
|
||
// the tooltip should be the source of change
|
||
// Animation check workaround:
|
||
// tooltip._start will be null when tooltip isn't animating
|
||
if (tooltip) {
|
||
changed = tooltip._start
|
||
? tooltip.handleEvent(e)
|
||
: changed | tooltip.handleEvent(e);
|
||
}
|
||
|
||
core_plugins.notify(me, 'afterEvent', [e]);
|
||
|
||
var bufferedRequest = me._bufferedRequest;
|
||
if (bufferedRequest) {
|
||
// If we have an update that was triggered, we need to do a normal render
|
||
me.render(bufferedRequest);
|
||
} else if (changed && !me.animating) {
|
||
// If entering, leaving, or changing elements, animate the change via pivot
|
||
me.stop();
|
||
|
||
// We only need to render at this point. Updating will cause scales to be
|
||
// recomputed generating flicker & using more memory than necessary.
|
||
me.render({
|
||
duration: me.options.hover.animationDuration,
|
||
lazy: true
|
||
});
|
||
}
|
||
|
||
me._bufferedRender = false;
|
||
me._bufferedRequest = null;
|
||
|
||
return me;
|
||
},
|
||
|
||
/**
|
||
* Handle an event
|
||
* @private
|
||
* @param {IEvent} event the event to handle
|
||
* @return {boolean} true if the chart needs to re-render
|
||
*/
|
||
handleEvent: function(e) {
|
||
var me = this;
|
||
var options = me.options || {};
|
||
var hoverOptions = options.hover;
|
||
var changed = false;
|
||
|
||
me.lastActive = me.lastActive || [];
|
||
|
||
// Find Active Elements for hover and tooltips
|
||
if (e.type === 'mouseout') {
|
||
me.active = [];
|
||
} else {
|
||
me.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);
|
||
}
|
||
|
||
// Invoke onHover hook
|
||
// Need to call with native event here to not break backwards compatibility
|
||
helpers$1.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);
|
||
|
||
if (e.type === 'mouseup' || e.type === 'click') {
|
||
if (options.onClick) {
|
||
// Use e.native here for backwards compatibility
|
||
options.onClick.call(me, e.native, me.active);
|
||
}
|
||
}
|
||
|
||
// Remove styling for last active (even if it may still be active)
|
||
if (me.lastActive.length) {
|
||
me.updateHoverStyle(me.lastActive, hoverOptions.mode, false);
|
||
}
|
||
|
||
// Built in hover styling
|
||
if (me.active.length && hoverOptions.mode) {
|
||
me.updateHoverStyle(me.active, hoverOptions.mode, true);
|
||
}
|
||
|
||
changed = !helpers$1.arrayEquals(me.active, me.lastActive);
|
||
|
||
// Remember Last Actives
|
||
me.lastActive = me.active;
|
||
|
||
return changed;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* NOTE(SB) We actually don't use this container anymore but we need to keep it
|
||
* for backward compatibility. Though, it can still be useful for plugins that
|
||
* would need to work on multiple charts?!
|
||
*/
|
||
Chart.instances = {};
|
||
|
||
var core_controller = Chart;
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart instead.
|
||
* @class Chart.Controller
|
||
* @deprecated since version 2.6
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
Chart.Controller = Chart;
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore.
|
||
* @namespace Chart
|
||
* @deprecated since version 2.8
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
Chart.types = {};
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore.
|
||
* @namespace Chart.helpers.configMerge
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers$1.configMerge = mergeConfig;
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore.
|
||
* @namespace Chart.helpers.scaleMerge
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
helpers$1.scaleMerge = mergeScaleConfig;
|
||
|
||
var core_helpers = function() {
|
||
|
||
// -- Basic js utility methods
|
||
|
||
helpers$1.where = function(collection, filterCallback) {
|
||
if (helpers$1.isArray(collection) && Array.prototype.filter) {
|
||
return collection.filter(filterCallback);
|
||
}
|
||
var filtered = [];
|
||
|
||
helpers$1.each(collection, function(item) {
|
||
if (filterCallback(item)) {
|
||
filtered.push(item);
|
||
}
|
||
});
|
||
|
||
return filtered;
|
||
};
|
||
helpers$1.findIndex = Array.prototype.findIndex ?
|
||
function(array, callback, scope) {
|
||
return array.findIndex(callback, scope);
|
||
} :
|
||
function(array, callback, scope) {
|
||
scope = scope === undefined ? array : scope;
|
||
for (var i = 0, ilen = array.length; i < ilen; ++i) {
|
||
if (callback.call(scope, array[i], i, array)) {
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
};
|
||
helpers$1.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||
// Default to start of the array
|
||
if (helpers$1.isNullOrUndef(startIndex)) {
|
||
startIndex = -1;
|
||
}
|
||
for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
|
||
var currentItem = arrayToSearch[i];
|
||
if (filterCallback(currentItem)) {
|
||
return currentItem;
|
||
}
|
||
}
|
||
};
|
||
helpers$1.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {
|
||
// Default to end of the array
|
||
if (helpers$1.isNullOrUndef(startIndex)) {
|
||
startIndex = arrayToSearch.length;
|
||
}
|
||
for (var i = startIndex - 1; i >= 0; i--) {
|
||
var currentItem = arrayToSearch[i];
|
||
if (filterCallback(currentItem)) {
|
||
return currentItem;
|
||
}
|
||
}
|
||
};
|
||
|
||
// -- Math methods
|
||
helpers$1.isNumber = function(n) {
|
||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||
};
|
||
helpers$1.almostEquals = function(x, y, epsilon) {
|
||
return Math.abs(x - y) < epsilon;
|
||
};
|
||
helpers$1.almostWhole = function(x, epsilon) {
|
||
var rounded = Math.round(x);
|
||
return ((rounded - epsilon) <= x) && ((rounded + epsilon) >= x);
|
||
};
|
||
helpers$1.max = function(array) {
|
||
return array.reduce(function(max, value) {
|
||
if (!isNaN(value)) {
|
||
return Math.max(max, value);
|
||
}
|
||
return max;
|
||
}, Number.NEGATIVE_INFINITY);
|
||
};
|
||
helpers$1.min = function(array) {
|
||
return array.reduce(function(min, value) {
|
||
if (!isNaN(value)) {
|
||
return Math.min(min, value);
|
||
}
|
||
return min;
|
||
}, Number.POSITIVE_INFINITY);
|
||
};
|
||
helpers$1.sign = Math.sign ?
|
||
function(x) {
|
||
return Math.sign(x);
|
||
} :
|
||
function(x) {
|
||
x = +x; // convert to a number
|
||
if (x === 0 || isNaN(x)) {
|
||
return x;
|
||
}
|
||
return x > 0 ? 1 : -1;
|
||
};
|
||
helpers$1.toRadians = function(degrees) {
|
||
return degrees * (Math.PI / 180);
|
||
};
|
||
helpers$1.toDegrees = function(radians) {
|
||
return radians * (180 / Math.PI);
|
||
};
|
||
|
||
/**
|
||
* Returns the number of decimal places
|
||
* i.e. the number of digits after the decimal point, of the value of this Number.
|
||
* @param {number} x - A number.
|
||
* @returns {number} The number of decimal places.
|
||
* @private
|
||
*/
|
||
helpers$1._decimalPlaces = function(x) {
|
||
if (!helpers$1.isFinite(x)) {
|
||
return;
|
||
}
|
||
var e = 1;
|
||
var p = 0;
|
||
while (Math.round(x * e) / e !== x) {
|
||
e *= 10;
|
||
p++;
|
||
}
|
||
return p;
|
||
};
|
||
|
||
// Gets the angle from vertical upright to the point about a centre.
|
||
helpers$1.getAngleFromPoint = function(centrePoint, anglePoint) {
|
||
var distanceFromXCenter = anglePoint.x - centrePoint.x;
|
||
var distanceFromYCenter = anglePoint.y - centrePoint.y;
|
||
var radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
|
||
|
||
var angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);
|
||
|
||
if (angle < (-0.5 * Math.PI)) {
|
||
angle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]
|
||
}
|
||
|
||
return {
|
||
angle: angle,
|
||
distance: radialDistanceFromCenter
|
||
};
|
||
};
|
||
helpers$1.distanceBetweenPoints = function(pt1, pt2) {
|
||
return Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));
|
||
};
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore
|
||
* @function Chart.helpers.aliasPixel
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
*/
|
||
helpers$1.aliasPixel = function(pixelWidth) {
|
||
return (pixelWidth % 2 === 0) ? 0 : 0.5;
|
||
};
|
||
|
||
/**
|
||
* Returns the aligned pixel value to avoid anti-aliasing blur
|
||
* @param {Chart} chart - The chart instance.
|
||
* @param {number} pixel - A pixel value.
|
||
* @param {number} width - The width of the element.
|
||
* @returns {number} The aligned pixel value.
|
||
* @private
|
||
*/
|
||
helpers$1._alignPixel = function(chart, pixel, width) {
|
||
var devicePixelRatio = chart.currentDevicePixelRatio;
|
||
var halfWidth = width / 2;
|
||
return Math.round((pixel - halfWidth) * devicePixelRatio) / devicePixelRatio + halfWidth;
|
||
};
|
||
|
||
helpers$1.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {
|
||
// Props to Rob Spencer at scaled innovation for his post on splining between points
|
||
// http://scaledinnovation.com/analytics/splines/aboutSplines.html
|
||
|
||
// This function must also respect "skipped" points
|
||
|
||
var previous = firstPoint.skip ? middlePoint : firstPoint;
|
||
var current = middlePoint;
|
||
var next = afterPoint.skip ? middlePoint : afterPoint;
|
||
|
||
var d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));
|
||
var d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));
|
||
|
||
var s01 = d01 / (d01 + d12);
|
||
var s12 = d12 / (d01 + d12);
|
||
|
||
// If all points are the same, s01 & s02 will be inf
|
||
s01 = isNaN(s01) ? 0 : s01;
|
||
s12 = isNaN(s12) ? 0 : s12;
|
||
|
||
var fa = t * s01; // scaling factor for triangle Ta
|
||
var fb = t * s12;
|
||
|
||
return {
|
||
previous: {
|
||
x: current.x - fa * (next.x - previous.x),
|
||
y: current.y - fa * (next.y - previous.y)
|
||
},
|
||
next: {
|
||
x: current.x + fb * (next.x - previous.x),
|
||
y: current.y + fb * (next.y - previous.y)
|
||
}
|
||
};
|
||
};
|
||
helpers$1.EPSILON = Number.EPSILON || 1e-14;
|
||
helpers$1.splineCurveMonotone = function(points) {
|
||
// This function calculates Bézier control points in a similar way than |splineCurve|,
|
||
// but preserves monotonicity of the provided data and ensures no local extremums are added
|
||
// between the dataset discrete points due to the interpolation.
|
||
// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation
|
||
|
||
var pointsWithTangents = (points || []).map(function(point) {
|
||
return {
|
||
model: point._model,
|
||
deltaK: 0,
|
||
mK: 0
|
||
};
|
||
});
|
||
|
||
// Calculate slopes (deltaK) and initialize tangents (mK)
|
||
var pointsLen = pointsWithTangents.length;
|
||
var i, pointBefore, pointCurrent, pointAfter;
|
||
for (i = 0; i < pointsLen; ++i) {
|
||
pointCurrent = pointsWithTangents[i];
|
||
if (pointCurrent.model.skip) {
|
||
continue;
|
||
}
|
||
|
||
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
|
||
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
|
||
if (pointAfter && !pointAfter.model.skip) {
|
||
var slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);
|
||
|
||
// In the case of two points that appear at the same x pixel, slopeDeltaX is 0
|
||
pointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;
|
||
}
|
||
|
||
if (!pointBefore || pointBefore.model.skip) {
|
||
pointCurrent.mK = pointCurrent.deltaK;
|
||
} else if (!pointAfter || pointAfter.model.skip) {
|
||
pointCurrent.mK = pointBefore.deltaK;
|
||
} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {
|
||
pointCurrent.mK = 0;
|
||
} else {
|
||
pointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;
|
||
}
|
||
}
|
||
|
||
// Adjust tangents to ensure monotonic properties
|
||
var alphaK, betaK, tauK, squaredMagnitude;
|
||
for (i = 0; i < pointsLen - 1; ++i) {
|
||
pointCurrent = pointsWithTangents[i];
|
||
pointAfter = pointsWithTangents[i + 1];
|
||
if (pointCurrent.model.skip || pointAfter.model.skip) {
|
||
continue;
|
||
}
|
||
|
||
if (helpers$1.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {
|
||
pointCurrent.mK = pointAfter.mK = 0;
|
||
continue;
|
||
}
|
||
|
||
alphaK = pointCurrent.mK / pointCurrent.deltaK;
|
||
betaK = pointAfter.mK / pointCurrent.deltaK;
|
||
squaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);
|
||
if (squaredMagnitude <= 9) {
|
||
continue;
|
||
}
|
||
|
||
tauK = 3 / Math.sqrt(squaredMagnitude);
|
||
pointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;
|
||
pointAfter.mK = betaK * tauK * pointCurrent.deltaK;
|
||
}
|
||
|
||
// Compute control points
|
||
var deltaX;
|
||
for (i = 0; i < pointsLen; ++i) {
|
||
pointCurrent = pointsWithTangents[i];
|
||
if (pointCurrent.model.skip) {
|
||
continue;
|
||
}
|
||
|
||
pointBefore = i > 0 ? pointsWithTangents[i - 1] : null;
|
||
pointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;
|
||
if (pointBefore && !pointBefore.model.skip) {
|
||
deltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;
|
||
pointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;
|
||
pointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;
|
||
}
|
||
if (pointAfter && !pointAfter.model.skip) {
|
||
deltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;
|
||
pointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;
|
||
pointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;
|
||
}
|
||
}
|
||
};
|
||
helpers$1.nextItem = function(collection, index, loop) {
|
||
if (loop) {
|
||
return index >= collection.length - 1 ? collection[0] : collection[index + 1];
|
||
}
|
||
return index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];
|
||
};
|
||
helpers$1.previousItem = function(collection, index, loop) {
|
||
if (loop) {
|
||
return index <= 0 ? collection[collection.length - 1] : collection[index - 1];
|
||
}
|
||
return index <= 0 ? collection[0] : collection[index - 1];
|
||
};
|
||
// Implementation of the nice number algorithm used in determining where axis labels will go
|
||
helpers$1.niceNum = function(range, round) {
|
||
var exponent = Math.floor(helpers$1.log10(range));
|
||
var fraction = range / Math.pow(10, exponent);
|
||
var niceFraction;
|
||
|
||
if (round) {
|
||
if (fraction < 1.5) {
|
||
niceFraction = 1;
|
||
} else if (fraction < 3) {
|
||
niceFraction = 2;
|
||
} else if (fraction < 7) {
|
||
niceFraction = 5;
|
||
} else {
|
||
niceFraction = 10;
|
||
}
|
||
} else if (fraction <= 1.0) {
|
||
niceFraction = 1;
|
||
} else if (fraction <= 2) {
|
||
niceFraction = 2;
|
||
} else if (fraction <= 5) {
|
||
niceFraction = 5;
|
||
} else {
|
||
niceFraction = 10;
|
||
}
|
||
|
||
return niceFraction * Math.pow(10, exponent);
|
||
};
|
||
// Request animation polyfill - https://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
|
||
helpers$1.requestAnimFrame = (function() {
|
||
if (typeof window === 'undefined') {
|
||
return function(callback) {
|
||
callback();
|
||
};
|
||
}
|
||
return window.requestAnimationFrame ||
|
||
window.webkitRequestAnimationFrame ||
|
||
window.mozRequestAnimationFrame ||
|
||
window.oRequestAnimationFrame ||
|
||
window.msRequestAnimationFrame ||
|
||
function(callback) {
|
||
return window.setTimeout(callback, 1000 / 60);
|
||
};
|
||
}());
|
||
// -- DOM methods
|
||
helpers$1.getRelativePosition = function(evt, chart) {
|
||
var mouseX, mouseY;
|
||
var e = evt.originalEvent || evt;
|
||
var canvas = evt.target || evt.srcElement;
|
||
var boundingRect = canvas.getBoundingClientRect();
|
||
|
||
var touches = e.touches;
|
||
if (touches && touches.length > 0) {
|
||
mouseX = touches[0].clientX;
|
||
mouseY = touches[0].clientY;
|
||
|
||
} else {
|
||
mouseX = e.clientX;
|
||
mouseY = e.clientY;
|
||
}
|
||
|
||
// Scale mouse coordinates into canvas coordinates
|
||
// by following the pattern laid out by 'jerryj' in the comments of
|
||
// https://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/
|
||
var paddingLeft = parseFloat(helpers$1.getStyle(canvas, 'padding-left'));
|
||
var paddingTop = parseFloat(helpers$1.getStyle(canvas, 'padding-top'));
|
||
var paddingRight = parseFloat(helpers$1.getStyle(canvas, 'padding-right'));
|
||
var paddingBottom = parseFloat(helpers$1.getStyle(canvas, 'padding-bottom'));
|
||
var width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;
|
||
var height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;
|
||
|
||
// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However
|
||
// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here
|
||
mouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);
|
||
mouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);
|
||
|
||
return {
|
||
x: mouseX,
|
||
y: mouseY
|
||
};
|
||
|
||
};
|
||
|
||
// Private helper function to convert max-width/max-height values that may be percentages into a number
|
||
function parseMaxStyle(styleValue, node, parentProperty) {
|
||
var valueInPixels;
|
||
if (typeof styleValue === 'string') {
|
||
valueInPixels = parseInt(styleValue, 10);
|
||
|
||
if (styleValue.indexOf('%') !== -1) {
|
||
// percentage * size in dimension
|
||
valueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];
|
||
}
|
||
} else {
|
||
valueInPixels = styleValue;
|
||
}
|
||
|
||
return valueInPixels;
|
||
}
|
||
|
||
/**
|
||
* Returns if the given value contains an effective constraint.
|
||
* @private
|
||
*/
|
||
function isConstrainedValue(value) {
|
||
return value !== undefined && value !== null && value !== 'none';
|
||
}
|
||
|
||
/**
|
||
* Returns the max width or height of the given DOM node in a cross-browser compatible fashion
|
||
* @param {HTMLElement} domNode - the node to check the constraint on
|
||
* @param {string} maxStyle - the style that defines the maximum for the direction we are using ('max-width' / 'max-height')
|
||
* @param {string} percentageProperty - property of parent to use when calculating width as a percentage
|
||
* @see {@link https://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser}
|
||
*/
|
||
function getConstraintDimension(domNode, maxStyle, percentageProperty) {
|
||
var view = document.defaultView;
|
||
var parentNode = helpers$1._getParentNode(domNode);
|
||
var constrainedNode = view.getComputedStyle(domNode)[maxStyle];
|
||
var constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];
|
||
var hasCNode = isConstrainedValue(constrainedNode);
|
||
var hasCContainer = isConstrainedValue(constrainedContainer);
|
||
var infinity = Number.POSITIVE_INFINITY;
|
||
|
||
if (hasCNode || hasCContainer) {
|
||
return Math.min(
|
||
hasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,
|
||
hasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);
|
||
}
|
||
|
||
return 'none';
|
||
}
|
||
// returns Number or undefined if no constraint
|
||
helpers$1.getConstraintWidth = function(domNode) {
|
||
return getConstraintDimension(domNode, 'max-width', 'clientWidth');
|
||
};
|
||
// returns Number or undefined if no constraint
|
||
helpers$1.getConstraintHeight = function(domNode) {
|
||
return getConstraintDimension(domNode, 'max-height', 'clientHeight');
|
||
};
|
||
/**
|
||
* @private
|
||
*/
|
||
helpers$1._calculatePadding = function(container, padding, parentDimension) {
|
||
padding = helpers$1.getStyle(container, padding);
|
||
|
||
return padding.indexOf('%') > -1 ? parentDimension * parseInt(padding, 10) / 100 : parseInt(padding, 10);
|
||
};
|
||
/**
|
||
* @private
|
||
*/
|
||
helpers$1._getParentNode = function(domNode) {
|
||
var parent = domNode.parentNode;
|
||
if (parent && parent.toString() === '[object ShadowRoot]') {
|
||
parent = parent.host;
|
||
}
|
||
return parent;
|
||
};
|
||
helpers$1.getMaximumWidth = function(domNode) {
|
||
var container = helpers$1._getParentNode(domNode);
|
||
if (!container) {
|
||
return domNode.clientWidth;
|
||
}
|
||
|
||
var clientWidth = container.clientWidth;
|
||
var paddingLeft = helpers$1._calculatePadding(container, 'padding-left', clientWidth);
|
||
var paddingRight = helpers$1._calculatePadding(container, 'padding-right', clientWidth);
|
||
|
||
var w = clientWidth - paddingLeft - paddingRight;
|
||
var cw = helpers$1.getConstraintWidth(domNode);
|
||
return isNaN(cw) ? w : Math.min(w, cw);
|
||
};
|
||
helpers$1.getMaximumHeight = function(domNode) {
|
||
var container = helpers$1._getParentNode(domNode);
|
||
if (!container) {
|
||
return domNode.clientHeight;
|
||
}
|
||
|
||
var clientHeight = container.clientHeight;
|
||
var paddingTop = helpers$1._calculatePadding(container, 'padding-top', clientHeight);
|
||
var paddingBottom = helpers$1._calculatePadding(container, 'padding-bottom', clientHeight);
|
||
|
||
var h = clientHeight - paddingTop - paddingBottom;
|
||
var ch = helpers$1.getConstraintHeight(domNode);
|
||
return isNaN(ch) ? h : Math.min(h, ch);
|
||
};
|
||
helpers$1.getStyle = function(el, property) {
|
||
return el.currentStyle ?
|
||
el.currentStyle[property] :
|
||
document.defaultView.getComputedStyle(el, null).getPropertyValue(property);
|
||
};
|
||
helpers$1.retinaScale = function(chart, forceRatio) {
|
||
var pixelRatio = chart.currentDevicePixelRatio = forceRatio || (typeof window !== 'undefined' && window.devicePixelRatio) || 1;
|
||
if (pixelRatio === 1) {
|
||
return;
|
||
}
|
||
|
||
var canvas = chart.canvas;
|
||
var height = chart.height;
|
||
var width = chart.width;
|
||
|
||
canvas.height = height * pixelRatio;
|
||
canvas.width = width * pixelRatio;
|
||
chart.ctx.scale(pixelRatio, pixelRatio);
|
||
|
||
// If no style has been set on the canvas, the render size is used as display size,
|
||
// making the chart visually bigger, so let's enforce it to the "correct" values.
|
||
// See https://github.com/chartjs/Chart.js/issues/3575
|
||
if (!canvas.style.height && !canvas.style.width) {
|
||
canvas.style.height = height + 'px';
|
||
canvas.style.width = width + 'px';
|
||
}
|
||
};
|
||
// -- Canvas methods
|
||
helpers$1.fontString = function(pixelSize, fontStyle, fontFamily) {
|
||
return fontStyle + ' ' + pixelSize + 'px ' + fontFamily;
|
||
};
|
||
helpers$1.longestText = function(ctx, font, arrayOfThings, cache) {
|
||
cache = cache || {};
|
||
var data = cache.data = cache.data || {};
|
||
var gc = cache.garbageCollect = cache.garbageCollect || [];
|
||
|
||
if (cache.font !== font) {
|
||
data = cache.data = {};
|
||
gc = cache.garbageCollect = [];
|
||
cache.font = font;
|
||
}
|
||
|
||
ctx.font = font;
|
||
var longest = 0;
|
||
var ilen = arrayOfThings.length;
|
||
var i, j, jlen, thing, nestedThing;
|
||
for (i = 0; i < ilen; i++) {
|
||
thing = arrayOfThings[i];
|
||
|
||
// Undefined strings and arrays should not be measured
|
||
if (thing !== undefined && thing !== null && helpers$1.isArray(thing) !== true) {
|
||
longest = helpers$1.measureText(ctx, data, gc, longest, thing);
|
||
} else if (helpers$1.isArray(thing)) {
|
||
// if it is an array lets measure each element
|
||
// to do maybe simplify this function a bit so we can do this more recursively?
|
||
for (j = 0, jlen = thing.length; j < jlen; j++) {
|
||
nestedThing = thing[j];
|
||
// Undefined strings and arrays should not be measured
|
||
if (nestedThing !== undefined && nestedThing !== null && !helpers$1.isArray(nestedThing)) {
|
||
longest = helpers$1.measureText(ctx, data, gc, longest, nestedThing);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var gcLen = gc.length / 2;
|
||
if (gcLen > arrayOfThings.length) {
|
||
for (i = 0; i < gcLen; i++) {
|
||
delete data[gc[i]];
|
||
}
|
||
gc.splice(0, gcLen);
|
||
}
|
||
return longest;
|
||
};
|
||
helpers$1.measureText = function(ctx, data, gc, longest, string) {
|
||
var textWidth = data[string];
|
||
if (!textWidth) {
|
||
textWidth = data[string] = ctx.measureText(string).width;
|
||
gc.push(string);
|
||
}
|
||
if (textWidth > longest) {
|
||
longest = textWidth;
|
||
}
|
||
return longest;
|
||
};
|
||
|
||
/**
|
||
* @deprecated
|
||
*/
|
||
helpers$1.numberOfLabelLines = function(arrayOfThings) {
|
||
var numberOfLines = 1;
|
||
helpers$1.each(arrayOfThings, function(thing) {
|
||
if (helpers$1.isArray(thing)) {
|
||
if (thing.length > numberOfLines) {
|
||
numberOfLines = thing.length;
|
||
}
|
||
}
|
||
});
|
||
return numberOfLines;
|
||
};
|
||
|
||
helpers$1.color = !chartjsColor ?
|
||
function(value) {
|
||
console.error('Color.js not found!');
|
||
return value;
|
||
} :
|
||
function(value) {
|
||
/* global CanvasGradient */
|
||
if (value instanceof CanvasGradient) {
|
||
value = core_defaults.global.defaultColor;
|
||
}
|
||
|
||
return chartjsColor(value);
|
||
};
|
||
|
||
helpers$1.getHoverColor = function(colorValue) {
|
||
/* global CanvasPattern */
|
||
return (colorValue instanceof CanvasPattern || colorValue instanceof CanvasGradient) ?
|
||
colorValue :
|
||
helpers$1.color(colorValue).saturate(0.5).darken(0.1).rgbString();
|
||
};
|
||
};
|
||
|
||
function abstract() {
|
||
throw new Error(
|
||
'This method is not implemented: either no adapter can ' +
|
||
'be found or an incomplete integration was provided.'
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Date adapter (current used by the time scale)
|
||
* @namespace Chart._adapters._date
|
||
* @memberof Chart._adapters
|
||
* @private
|
||
*/
|
||
|
||
/**
|
||
* Currently supported unit string values.
|
||
* @typedef {('millisecond'|'second'|'minute'|'hour'|'day'|'week'|'month'|'quarter'|'year')}
|
||
* @memberof Chart._adapters._date
|
||
* @name Unit
|
||
*/
|
||
|
||
/**
|
||
* @class
|
||
*/
|
||
function DateAdapter(options) {
|
||
this.options = options || {};
|
||
}
|
||
|
||
helpers$1.extend(DateAdapter.prototype, /** @lends DateAdapter */ {
|
||
/**
|
||
* Returns a map of time formats for the supported formatting units defined
|
||
* in Unit as well as 'datetime' representing a detailed date/time string.
|
||
* @returns {{string: string}}
|
||
*/
|
||
formats: abstract,
|
||
|
||
/**
|
||
* Parses the given `value` and return the associated timestamp.
|
||
* @param {any} value - the value to parse (usually comes from the data)
|
||
* @param {string} [format] - the expected data format
|
||
* @returns {(number|null)}
|
||
* @function
|
||
*/
|
||
parse: abstract,
|
||
|
||
/**
|
||
* Returns the formatted date in the specified `format` for a given `timestamp`.
|
||
* @param {number} timestamp - the timestamp to format
|
||
* @param {string} format - the date/time token
|
||
* @return {string}
|
||
* @function
|
||
*/
|
||
format: abstract,
|
||
|
||
/**
|
||
* Adds the specified `amount` of `unit` to the given `timestamp`.
|
||
* @param {number} timestamp - the input timestamp
|
||
* @param {number} amount - the amount to add
|
||
* @param {Unit} unit - the unit as string
|
||
* @return {number}
|
||
* @function
|
||
*/
|
||
add: abstract,
|
||
|
||
/**
|
||
* Returns the number of `unit` between the given timestamps.
|
||
* @param {number} max - the input timestamp (reference)
|
||
* @param {number} min - the timestamp to substract
|
||
* @param {Unit} unit - the unit as string
|
||
* @return {number}
|
||
* @function
|
||
*/
|
||
diff: abstract,
|
||
|
||
/**
|
||
* Returns start of `unit` for the given `timestamp`.
|
||
* @param {number} timestamp - the input timestamp
|
||
* @param {Unit} unit - the unit as string
|
||
* @param {number} [weekday] - the ISO day of the week with 1 being Monday
|
||
* and 7 being Sunday (only needed if param *unit* is `isoWeek`).
|
||
* @function
|
||
*/
|
||
startOf: abstract,
|
||
|
||
/**
|
||
* Returns end of `unit` for the given `timestamp`.
|
||
* @param {number} timestamp - the input timestamp
|
||
* @param {Unit} unit - the unit as string
|
||
* @function
|
||
*/
|
||
endOf: abstract,
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility for scale.getValueForPixel(),
|
||
* this method should be overridden only by the moment adapter.
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
_create: function(value) {
|
||
return value;
|
||
}
|
||
});
|
||
|
||
DateAdapter.override = function(members) {
|
||
helpers$1.extend(DateAdapter.prototype, members);
|
||
};
|
||
|
||
var _date = DateAdapter;
|
||
|
||
var core_adapters = {
|
||
_date: _date
|
||
};
|
||
|
||
/**
|
||
* Namespace to hold static tick generation functions
|
||
* @namespace Chart.Ticks
|
||
*/
|
||
var core_ticks = {
|
||
/**
|
||
* Namespace to hold formatters for different types of ticks
|
||
* @namespace Chart.Ticks.formatters
|
||
*/
|
||
formatters: {
|
||
/**
|
||
* Formatter for value labels
|
||
* @method Chart.Ticks.formatters.values
|
||
* @param value the value to display
|
||
* @return {string|string[]} the label to display
|
||
*/
|
||
values: function(value) {
|
||
return helpers$1.isArray(value) ? value : '' + value;
|
||
},
|
||
|
||
/**
|
||
* Formatter for linear numeric ticks
|
||
* @method Chart.Ticks.formatters.linear
|
||
* @param tickValue {number} the value to be formatted
|
||
* @param index {number} the position of the tickValue parameter in the ticks array
|
||
* @param ticks {number[]} the list of ticks being converted
|
||
* @return {string} string representation of the tickValue parameter
|
||
*/
|
||
linear: function(tickValue, index, ticks) {
|
||
// If we have lots of ticks, don't use the ones
|
||
var delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];
|
||
|
||
// If we have a number like 2.5 as the delta, figure out how many decimal places we need
|
||
if (Math.abs(delta) > 1) {
|
||
if (tickValue !== Math.floor(tickValue)) {
|
||
// not an integer
|
||
delta = tickValue - Math.floor(tickValue);
|
||
}
|
||
}
|
||
|
||
var logDelta = helpers$1.log10(Math.abs(delta));
|
||
var tickString = '';
|
||
|
||
if (tickValue !== 0) {
|
||
var maxTick = Math.max(Math.abs(ticks[0]), Math.abs(ticks[ticks.length - 1]));
|
||
if (maxTick < 1e-4) { // all ticks are small numbers; use scientific notation
|
||
var logTick = helpers$1.log10(Math.abs(tickValue));
|
||
var numExponential = Math.floor(logTick) - Math.floor(logDelta);
|
||
numExponential = Math.max(Math.min(numExponential, 20), 0);
|
||
tickString = tickValue.toExponential(numExponential);
|
||
} else {
|
||
var numDecimal = -1 * Math.floor(logDelta);
|
||
numDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places
|
||
tickString = tickValue.toFixed(numDecimal);
|
||
}
|
||
} else {
|
||
tickString = '0'; // never show decimal places for 0
|
||
}
|
||
|
||
return tickString;
|
||
},
|
||
|
||
logarithmic: function(tickValue, index, ticks) {
|
||
var remain = tickValue / (Math.pow(10, Math.floor(helpers$1.log10(tickValue))));
|
||
|
||
if (tickValue === 0) {
|
||
return '0';
|
||
} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {
|
||
return tickValue.toExponential();
|
||
}
|
||
return '';
|
||
}
|
||
}
|
||
};
|
||
|
||
var isArray = helpers$1.isArray;
|
||
var isNullOrUndef = helpers$1.isNullOrUndef;
|
||
var valueOrDefault$a = helpers$1.valueOrDefault;
|
||
var valueAtIndexOrDefault = helpers$1.valueAtIndexOrDefault;
|
||
|
||
core_defaults._set('scale', {
|
||
display: true,
|
||
position: 'left',
|
||
offset: false,
|
||
|
||
// grid line settings
|
||
gridLines: {
|
||
display: true,
|
||
color: 'rgba(0,0,0,0.1)',
|
||
lineWidth: 1,
|
||
drawBorder: true,
|
||
drawOnChartArea: true,
|
||
drawTicks: true,
|
||
tickMarkLength: 10,
|
||
zeroLineWidth: 1,
|
||
zeroLineColor: 'rgba(0,0,0,0.25)',
|
||
zeroLineBorderDash: [],
|
||
zeroLineBorderDashOffset: 0.0,
|
||
offsetGridLines: false,
|
||
borderDash: [],
|
||
borderDashOffset: 0.0
|
||
},
|
||
|
||
// scale label
|
||
scaleLabel: {
|
||
// display property
|
||
display: false,
|
||
|
||
// actual label
|
||
labelString: '',
|
||
|
||
// top/bottom padding
|
||
padding: {
|
||
top: 4,
|
||
bottom: 4
|
||
}
|
||
},
|
||
|
||
// label settings
|
||
ticks: {
|
||
beginAtZero: false,
|
||
minRotation: 0,
|
||
maxRotation: 50,
|
||
mirror: false,
|
||
padding: 0,
|
||
reverse: false,
|
||
display: true,
|
||
autoSkip: true,
|
||
autoSkipPadding: 0,
|
||
labelOffset: 0,
|
||
// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.
|
||
callback: core_ticks.formatters.values,
|
||
minor: {},
|
||
major: {}
|
||
}
|
||
});
|
||
|
||
/** Returns a new array containing numItems from arr */
|
||
function sample(arr, numItems) {
|
||
var result = [];
|
||
var increment = arr.length / numItems;
|
||
var i = 0;
|
||
var len = arr.length;
|
||
|
||
for (; i < len; i += increment) {
|
||
result.push(arr[Math.floor(i)]);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function getPixelForGridLine(scale, index, offsetGridLines) {
|
||
var length = scale.getTicks().length;
|
||
var validIndex = Math.min(index, length - 1);
|
||
var lineValue = scale.getPixelForTick(validIndex);
|
||
var start = scale._startPixel;
|
||
var end = scale._endPixel;
|
||
var epsilon = 1e-6; // 1e-6 is margin in pixels for accumulated error.
|
||
var offset;
|
||
|
||
if (offsetGridLines) {
|
||
if (length === 1) {
|
||
offset = Math.max(lineValue - start, end - lineValue);
|
||
} else if (index === 0) {
|
||
offset = (scale.getPixelForTick(1) - lineValue) / 2;
|
||
} else {
|
||
offset = (lineValue - scale.getPixelForTick(validIndex - 1)) / 2;
|
||
}
|
||
lineValue += validIndex < index ? offset : -offset;
|
||
|
||
// Return undefined if the pixel is out of the range
|
||
if (lineValue < start - epsilon || lineValue > end + epsilon) {
|
||
return;
|
||
}
|
||
}
|
||
return lineValue;
|
||
}
|
||
|
||
function garbageCollect(caches, length) {
|
||
helpers$1.each(caches, function(cache) {
|
||
var gc = cache.gc;
|
||
var gcLen = gc.length / 2;
|
||
var i;
|
||
if (gcLen > length) {
|
||
for (i = 0; i < gcLen; ++i) {
|
||
delete cache.data[gc[i]];
|
||
}
|
||
gc.splice(0, gcLen);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Returns {width, height, offset} objects for the first, last, widest, highest tick
|
||
* labels where offset indicates the anchor point offset from the top in pixels.
|
||
*/
|
||
function computeLabelSizes(ctx, tickFonts, ticks, caches) {
|
||
var length = ticks.length;
|
||
var widths = [];
|
||
var heights = [];
|
||
var offsets = [];
|
||
var widestLabelSize = 0;
|
||
var highestLabelSize = 0;
|
||
var i, j, jlen, label, tickFont, fontString, cache, lineHeight, width, height, nestedLabel, widest, highest;
|
||
|
||
for (i = 0; i < length; ++i) {
|
||
label = ticks[i].label;
|
||
tickFont = ticks[i].major ? tickFonts.major : tickFonts.minor;
|
||
ctx.font = fontString = tickFont.string;
|
||
cache = caches[fontString] = caches[fontString] || {data: {}, gc: []};
|
||
lineHeight = tickFont.lineHeight;
|
||
width = height = 0;
|
||
// Undefined labels and arrays should not be measured
|
||
if (!isNullOrUndef(label) && !isArray(label)) {
|
||
width = helpers$1.measureText(ctx, cache.data, cache.gc, width, label);
|
||
height = lineHeight;
|
||
} else if (isArray(label)) {
|
||
// if it is an array let's measure each element
|
||
for (j = 0, jlen = label.length; j < jlen; ++j) {
|
||
nestedLabel = label[j];
|
||
// Undefined labels and arrays should not be measured
|
||
if (!isNullOrUndef(nestedLabel) && !isArray(nestedLabel)) {
|
||
width = helpers$1.measureText(ctx, cache.data, cache.gc, width, nestedLabel);
|
||
height += lineHeight;
|
||
}
|
||
}
|
||
}
|
||
widths.push(width);
|
||
heights.push(height);
|
||
offsets.push(lineHeight / 2);
|
||
widestLabelSize = Math.max(width, widestLabelSize);
|
||
highestLabelSize = Math.max(height, highestLabelSize);
|
||
}
|
||
garbageCollect(caches, length);
|
||
|
||
widest = widths.indexOf(widestLabelSize);
|
||
highest = heights.indexOf(highestLabelSize);
|
||
|
||
function valueAt(idx) {
|
||
return {
|
||
width: widths[idx] || 0,
|
||
height: heights[idx] || 0,
|
||
offset: offsets[idx] || 0
|
||
};
|
||
}
|
||
|
||
return {
|
||
first: valueAt(0),
|
||
last: valueAt(length - 1),
|
||
widest: valueAt(widest),
|
||
highest: valueAt(highest)
|
||
};
|
||
}
|
||
|
||
function getTickMarkLength(options) {
|
||
return options.drawTicks ? options.tickMarkLength : 0;
|
||
}
|
||
|
||
function getScaleLabelHeight(options) {
|
||
var font, padding;
|
||
|
||
if (!options.display) {
|
||
return 0;
|
||
}
|
||
|
||
font = helpers$1.options._parseFont(options);
|
||
padding = helpers$1.options.toPadding(options.padding);
|
||
|
||
return font.lineHeight + padding.height;
|
||
}
|
||
|
||
function parseFontOptions(options, nestedOpts) {
|
||
return helpers$1.extend(helpers$1.options._parseFont({
|
||
fontFamily: valueOrDefault$a(nestedOpts.fontFamily, options.fontFamily),
|
||
fontSize: valueOrDefault$a(nestedOpts.fontSize, options.fontSize),
|
||
fontStyle: valueOrDefault$a(nestedOpts.fontStyle, options.fontStyle),
|
||
lineHeight: valueOrDefault$a(nestedOpts.lineHeight, options.lineHeight)
|
||
}), {
|
||
color: helpers$1.options.resolve([nestedOpts.fontColor, options.fontColor, core_defaults.global.defaultFontColor])
|
||
});
|
||
}
|
||
|
||
function parseTickFontOptions(options) {
|
||
var minor = parseFontOptions(options, options.minor);
|
||
var major = options.major.enabled ? parseFontOptions(options, options.major) : minor;
|
||
|
||
return {minor: minor, major: major};
|
||
}
|
||
|
||
function nonSkipped(ticksToFilter) {
|
||
var filtered = [];
|
||
var item, index, len;
|
||
for (index = 0, len = ticksToFilter.length; index < len; ++index) {
|
||
item = ticksToFilter[index];
|
||
if (typeof item._index !== 'undefined') {
|
||
filtered.push(item);
|
||
}
|
||
}
|
||
return filtered;
|
||
}
|
||
|
||
function getEvenSpacing(arr) {
|
||
var len = arr.length;
|
||
var i, diff;
|
||
|
||
if (len < 2) {
|
||
return false;
|
||
}
|
||
|
||
for (diff = arr[0], i = 1; i < len; ++i) {
|
||
if (arr[i] - arr[i - 1] !== diff) {
|
||
return false;
|
||
}
|
||
}
|
||
return diff;
|
||
}
|
||
|
||
function calculateSpacing(majorIndices, ticks, axisLength, ticksLimit) {
|
||
var evenMajorSpacing = getEvenSpacing(majorIndices);
|
||
var spacing = (ticks.length - 1) / ticksLimit;
|
||
var factors, factor, i, ilen;
|
||
|
||
// If the major ticks are evenly spaced apart, place the minor ticks
|
||
// so that they divide the major ticks into even chunks
|
||
if (!evenMajorSpacing) {
|
||
return Math.max(spacing, 1);
|
||
}
|
||
|
||
factors = helpers$1.math._factorize(evenMajorSpacing);
|
||
for (i = 0, ilen = factors.length - 1; i < ilen; i++) {
|
||
factor = factors[i];
|
||
if (factor > spacing) {
|
||
return factor;
|
||
}
|
||
}
|
||
return Math.max(spacing, 1);
|
||
}
|
||
|
||
function getMajorIndices(ticks) {
|
||
var result = [];
|
||
var i, ilen;
|
||
for (i = 0, ilen = ticks.length; i < ilen; i++) {
|
||
if (ticks[i].major) {
|
||
result.push(i);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function skipMajors(ticks, majorIndices, spacing) {
|
||
var count = 0;
|
||
var next = majorIndices[0];
|
||
var i, tick;
|
||
|
||
spacing = Math.ceil(spacing);
|
||
for (i = 0; i < ticks.length; i++) {
|
||
tick = ticks[i];
|
||
if (i === next) {
|
||
tick._index = i;
|
||
count++;
|
||
next = majorIndices[count * spacing];
|
||
} else {
|
||
delete tick.label;
|
||
}
|
||
}
|
||
}
|
||
|
||
function skip(ticks, spacing, majorStart, majorEnd) {
|
||
var start = valueOrDefault$a(majorStart, 0);
|
||
var end = Math.min(valueOrDefault$a(majorEnd, ticks.length), ticks.length);
|
||
var count = 0;
|
||
var length, i, tick, next;
|
||
|
||
spacing = Math.ceil(spacing);
|
||
if (majorEnd) {
|
||
length = majorEnd - majorStart;
|
||
spacing = length / Math.floor(length / spacing);
|
||
}
|
||
|
||
next = start;
|
||
|
||
while (next < 0) {
|
||
count++;
|
||
next = Math.round(start + count * spacing);
|
||
}
|
||
|
||
for (i = Math.max(start, 0); i < end; i++) {
|
||
tick = ticks[i];
|
||
if (i === next) {
|
||
tick._index = i;
|
||
count++;
|
||
next = Math.round(start + count * spacing);
|
||
} else {
|
||
delete tick.label;
|
||
}
|
||
}
|
||
}
|
||
|
||
var Scale = core_element.extend({
|
||
|
||
zeroLineIndex: 0,
|
||
|
||
/**
|
||
* Get the padding needed for the scale
|
||
* @method getPadding
|
||
* @private
|
||
* @returns {Padding} the necessary padding
|
||
*/
|
||
getPadding: function() {
|
||
var me = this;
|
||
return {
|
||
left: me.paddingLeft || 0,
|
||
top: me.paddingTop || 0,
|
||
right: me.paddingRight || 0,
|
||
bottom: me.paddingBottom || 0
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Returns the scale tick objects ({label, major})
|
||
* @since 2.7
|
||
*/
|
||
getTicks: function() {
|
||
return this._ticks;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getLabels: function() {
|
||
var data = this.chart.data;
|
||
return this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels || [];
|
||
},
|
||
|
||
// These methods are ordered by lifecyle. Utilities then follow.
|
||
// Any function defined here is inherited by all scale types.
|
||
// Any function can be extended by the scale type
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore
|
||
* @function Chart.Scale.mergeTicksOptions
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
*/
|
||
mergeTicksOptions: function() {
|
||
// noop
|
||
},
|
||
|
||
beforeUpdate: function() {
|
||
helpers$1.callback(this.options.beforeUpdate, [this]);
|
||
},
|
||
|
||
/**
|
||
* @param {number} maxWidth - the max width in pixels
|
||
* @param {number} maxHeight - the max height in pixels
|
||
* @param {object} margins - the space between the edge of the other scales and edge of the chart
|
||
* This space comes from two sources:
|
||
* - padding - space that's required to show the labels at the edges of the scale
|
||
* - thickness of scales or legends in another orientation
|
||
*/
|
||
update: function(maxWidth, maxHeight, margins) {
|
||
var me = this;
|
||
var tickOpts = me.options.ticks;
|
||
var sampleSize = tickOpts.sampleSize;
|
||
var i, ilen, labels, ticks, samplingEnabled;
|
||
|
||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||
me.beforeUpdate();
|
||
|
||
// Absorb the master measurements
|
||
me.maxWidth = maxWidth;
|
||
me.maxHeight = maxHeight;
|
||
me.margins = helpers$1.extend({
|
||
left: 0,
|
||
right: 0,
|
||
top: 0,
|
||
bottom: 0
|
||
}, margins);
|
||
|
||
me._ticks = null;
|
||
me.ticks = null;
|
||
me._labelSizes = null;
|
||
me._maxLabelLines = 0;
|
||
me.longestLabelWidth = 0;
|
||
me.longestTextCache = me.longestTextCache || {};
|
||
me._gridLineItems = null;
|
||
me._labelItems = null;
|
||
|
||
// Dimensions
|
||
me.beforeSetDimensions();
|
||
me.setDimensions();
|
||
me.afterSetDimensions();
|
||
|
||
// Data min/max
|
||
me.beforeDataLimits();
|
||
me.determineDataLimits();
|
||
me.afterDataLimits();
|
||
|
||
// Ticks - `this.ticks` is now DEPRECATED!
|
||
// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member
|
||
// and must not be accessed directly from outside this class. `this.ticks` being
|
||
// around for long time and not marked as private, we can't change its structure
|
||
// without unexpected breaking changes. If you need to access the scale ticks,
|
||
// use scale.getTicks() instead.
|
||
|
||
me.beforeBuildTicks();
|
||
|
||
// New implementations should return an array of objects but for BACKWARD COMPAT,
|
||
// we still support no return (`this.ticks` internally set by calling this method).
|
||
ticks = me.buildTicks() || [];
|
||
|
||
// Allow modification of ticks in callback.
|
||
ticks = me.afterBuildTicks(ticks) || ticks;
|
||
|
||
// Ensure ticks contains ticks in new tick format
|
||
if ((!ticks || !ticks.length) && me.ticks) {
|
||
ticks = [];
|
||
for (i = 0, ilen = me.ticks.length; i < ilen; ++i) {
|
||
ticks.push({
|
||
value: me.ticks[i],
|
||
major: false
|
||
});
|
||
}
|
||
}
|
||
|
||
me._ticks = ticks;
|
||
|
||
// Compute tick rotation and fit using a sampled subset of labels
|
||
// We generally don't need to compute the size of every single label for determining scale size
|
||
samplingEnabled = sampleSize < ticks.length;
|
||
labels = me._convertTicksToLabels(samplingEnabled ? sample(ticks, sampleSize) : ticks);
|
||
|
||
// _configure is called twice, once here, once from core.controller.updateLayout.
|
||
// Here we haven't been positioned yet, but dimensions are correct.
|
||
// Variables set in _configure are needed for calculateTickRotation, and
|
||
// it's ok that coordinates are not correct there, only dimensions matter.
|
||
me._configure();
|
||
|
||
// Tick Rotation
|
||
me.beforeCalculateTickRotation();
|
||
me.calculateTickRotation();
|
||
me.afterCalculateTickRotation();
|
||
|
||
me.beforeFit();
|
||
me.fit();
|
||
me.afterFit();
|
||
|
||
// Auto-skip
|
||
me._ticksToDraw = tickOpts.display && (tickOpts.autoSkip || tickOpts.source === 'auto') ? me._autoSkip(ticks) : ticks;
|
||
|
||
if (samplingEnabled) {
|
||
// Generate labels using all non-skipped ticks
|
||
labels = me._convertTicksToLabels(me._ticksToDraw);
|
||
}
|
||
|
||
me.ticks = labels; // BACKWARD COMPATIBILITY
|
||
|
||
// IMPORTANT: after this point, we consider that `this.ticks` will NEVER change!
|
||
|
||
me.afterUpdate();
|
||
|
||
// TODO(v3): remove minSize as a public property and return value from all layout boxes. It is unused
|
||
// make maxWidth and maxHeight private
|
||
return me.minSize;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_configure: function() {
|
||
var me = this;
|
||
var reversePixels = me.options.ticks.reverse;
|
||
var startPixel, endPixel;
|
||
|
||
if (me.isHorizontal()) {
|
||
startPixel = me.left;
|
||
endPixel = me.right;
|
||
} else {
|
||
startPixel = me.top;
|
||
endPixel = me.bottom;
|
||
// by default vertical scales are from bottom to top, so pixels are reversed
|
||
reversePixels = !reversePixels;
|
||
}
|
||
me._startPixel = startPixel;
|
||
me._endPixel = endPixel;
|
||
me._reversePixels = reversePixels;
|
||
me._length = endPixel - startPixel;
|
||
},
|
||
|
||
afterUpdate: function() {
|
||
helpers$1.callback(this.options.afterUpdate, [this]);
|
||
},
|
||
|
||
//
|
||
|
||
beforeSetDimensions: function() {
|
||
helpers$1.callback(this.options.beforeSetDimensions, [this]);
|
||
},
|
||
setDimensions: function() {
|
||
var me = this;
|
||
// Set the unconstrained dimension before label rotation
|
||
if (me.isHorizontal()) {
|
||
// Reset position before calculating rotation
|
||
me.width = me.maxWidth;
|
||
me.left = 0;
|
||
me.right = me.width;
|
||
} else {
|
||
me.height = me.maxHeight;
|
||
|
||
// Reset position before calculating rotation
|
||
me.top = 0;
|
||
me.bottom = me.height;
|
||
}
|
||
|
||
// Reset padding
|
||
me.paddingLeft = 0;
|
||
me.paddingTop = 0;
|
||
me.paddingRight = 0;
|
||
me.paddingBottom = 0;
|
||
},
|
||
afterSetDimensions: function() {
|
||
helpers$1.callback(this.options.afterSetDimensions, [this]);
|
||
},
|
||
|
||
// Data limits
|
||
beforeDataLimits: function() {
|
||
helpers$1.callback(this.options.beforeDataLimits, [this]);
|
||
},
|
||
determineDataLimits: helpers$1.noop,
|
||
afterDataLimits: function() {
|
||
helpers$1.callback(this.options.afterDataLimits, [this]);
|
||
},
|
||
|
||
//
|
||
beforeBuildTicks: function() {
|
||
helpers$1.callback(this.options.beforeBuildTicks, [this]);
|
||
},
|
||
buildTicks: helpers$1.noop,
|
||
afterBuildTicks: function(ticks) {
|
||
var me = this;
|
||
// ticks is empty for old axis implementations here
|
||
if (isArray(ticks) && ticks.length) {
|
||
return helpers$1.callback(me.options.afterBuildTicks, [me, ticks]);
|
||
}
|
||
// Support old implementations (that modified `this.ticks` directly in buildTicks)
|
||
me.ticks = helpers$1.callback(me.options.afterBuildTicks, [me, me.ticks]) || me.ticks;
|
||
return ticks;
|
||
},
|
||
|
||
beforeTickToLabelConversion: function() {
|
||
helpers$1.callback(this.options.beforeTickToLabelConversion, [this]);
|
||
},
|
||
convertTicksToLabels: function() {
|
||
var me = this;
|
||
// Convert ticks to strings
|
||
var tickOpts = me.options.ticks;
|
||
me.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);
|
||
},
|
||
afterTickToLabelConversion: function() {
|
||
helpers$1.callback(this.options.afterTickToLabelConversion, [this]);
|
||
},
|
||
|
||
//
|
||
|
||
beforeCalculateTickRotation: function() {
|
||
helpers$1.callback(this.options.beforeCalculateTickRotation, [this]);
|
||
},
|
||
calculateTickRotation: function() {
|
||
var me = this;
|
||
var options = me.options;
|
||
var tickOpts = options.ticks;
|
||
var numTicks = me.getTicks().length;
|
||
var minRotation = tickOpts.minRotation || 0;
|
||
var maxRotation = tickOpts.maxRotation;
|
||
var labelRotation = minRotation;
|
||
var labelSizes, maxLabelWidth, maxLabelHeight, maxWidth, tickWidth, maxHeight, maxLabelDiagonal;
|
||
|
||
if (!me._isVisible() || !tickOpts.display || minRotation >= maxRotation || numTicks <= 1 || !me.isHorizontal()) {
|
||
me.labelRotation = minRotation;
|
||
return;
|
||
}
|
||
|
||
labelSizes = me._getLabelSizes();
|
||
maxLabelWidth = labelSizes.widest.width;
|
||
maxLabelHeight = labelSizes.highest.height - labelSizes.highest.offset;
|
||
|
||
// Estimate the width of each grid based on the canvas width, the maximum
|
||
// label width and the number of tick intervals
|
||
maxWidth = Math.min(me.maxWidth, me.chart.width - maxLabelWidth);
|
||
tickWidth = options.offset ? me.maxWidth / numTicks : maxWidth / (numTicks - 1);
|
||
|
||
// Allow 3 pixels x2 padding either side for label readability
|
||
if (maxLabelWidth + 6 > tickWidth) {
|
||
tickWidth = maxWidth / (numTicks - (options.offset ? 0.5 : 1));
|
||
maxHeight = me.maxHeight - getTickMarkLength(options.gridLines)
|
||
- tickOpts.padding - getScaleLabelHeight(options.scaleLabel);
|
||
maxLabelDiagonal = Math.sqrt(maxLabelWidth * maxLabelWidth + maxLabelHeight * maxLabelHeight);
|
||
labelRotation = helpers$1.toDegrees(Math.min(
|
||
Math.asin(Math.min((labelSizes.highest.height + 6) / tickWidth, 1)),
|
||
Math.asin(Math.min(maxHeight / maxLabelDiagonal, 1)) - Math.asin(maxLabelHeight / maxLabelDiagonal)
|
||
));
|
||
labelRotation = Math.max(minRotation, Math.min(maxRotation, labelRotation));
|
||
}
|
||
|
||
me.labelRotation = labelRotation;
|
||
},
|
||
afterCalculateTickRotation: function() {
|
||
helpers$1.callback(this.options.afterCalculateTickRotation, [this]);
|
||
},
|
||
|
||
//
|
||
|
||
beforeFit: function() {
|
||
helpers$1.callback(this.options.beforeFit, [this]);
|
||
},
|
||
fit: function() {
|
||
var me = this;
|
||
// Reset
|
||
var minSize = me.minSize = {
|
||
width: 0,
|
||
height: 0
|
||
};
|
||
|
||
var chart = me.chart;
|
||
var opts = me.options;
|
||
var tickOpts = opts.ticks;
|
||
var scaleLabelOpts = opts.scaleLabel;
|
||
var gridLineOpts = opts.gridLines;
|
||
var display = me._isVisible();
|
||
var isBottom = opts.position === 'bottom';
|
||
var isHorizontal = me.isHorizontal();
|
||
|
||
// Width
|
||
if (isHorizontal) {
|
||
minSize.width = me.maxWidth;
|
||
} else if (display) {
|
||
minSize.width = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts);
|
||
}
|
||
|
||
// height
|
||
if (!isHorizontal) {
|
||
minSize.height = me.maxHeight; // fill all the height
|
||
} else if (display) {
|
||
minSize.height = getTickMarkLength(gridLineOpts) + getScaleLabelHeight(scaleLabelOpts);
|
||
}
|
||
|
||
// Don't bother fitting the ticks if we are not showing the labels
|
||
if (tickOpts.display && display) {
|
||
var tickFonts = parseTickFontOptions(tickOpts);
|
||
var labelSizes = me._getLabelSizes();
|
||
var firstLabelSize = labelSizes.first;
|
||
var lastLabelSize = labelSizes.last;
|
||
var widestLabelSize = labelSizes.widest;
|
||
var highestLabelSize = labelSizes.highest;
|
||
var lineSpace = tickFonts.minor.lineHeight * 0.4;
|
||
var tickPadding = tickOpts.padding;
|
||
|
||
if (isHorizontal) {
|
||
// A horizontal axis is more constrained by the height.
|
||
var isRotated = me.labelRotation !== 0;
|
||
var angleRadians = helpers$1.toRadians(me.labelRotation);
|
||
var cosRotation = Math.cos(angleRadians);
|
||
var sinRotation = Math.sin(angleRadians);
|
||
|
||
var labelHeight = sinRotation * widestLabelSize.width
|
||
+ cosRotation * (highestLabelSize.height - (isRotated ? highestLabelSize.offset : 0))
|
||
+ (isRotated ? 0 : lineSpace); // padding
|
||
|
||
minSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);
|
||
|
||
var offsetLeft = me.getPixelForTick(0) - me.left;
|
||
var offsetRight = me.right - me.getPixelForTick(me.getTicks().length - 1);
|
||
var paddingLeft, paddingRight;
|
||
|
||
// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned
|
||
// which means that the right padding is dominated by the font height
|
||
if (isRotated) {
|
||
paddingLeft = isBottom ?
|
||
cosRotation * firstLabelSize.width + sinRotation * firstLabelSize.offset :
|
||
sinRotation * (firstLabelSize.height - firstLabelSize.offset);
|
||
paddingRight = isBottom ?
|
||
sinRotation * (lastLabelSize.height - lastLabelSize.offset) :
|
||
cosRotation * lastLabelSize.width + sinRotation * lastLabelSize.offset;
|
||
} else {
|
||
paddingLeft = firstLabelSize.width / 2;
|
||
paddingRight = lastLabelSize.width / 2;
|
||
}
|
||
|
||
// Adjust padding taking into account changes in offsets
|
||
// and add 3 px to move away from canvas edges
|
||
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3;
|
||
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3;
|
||
} else {
|
||
// A vertical axis is more constrained by the width. Labels are the
|
||
// dominant factor here, so get that length first and account for padding
|
||
var labelWidth = tickOpts.mirror ? 0 :
|
||
// use lineSpace for consistency with horizontal axis
|
||
// tickPadding is not implemented for horizontal
|
||
widestLabelSize.width + tickPadding + lineSpace;
|
||
|
||
minSize.width = Math.min(me.maxWidth, minSize.width + labelWidth);
|
||
|
||
me.paddingTop = firstLabelSize.height / 2;
|
||
me.paddingBottom = lastLabelSize.height / 2;
|
||
}
|
||
}
|
||
|
||
me.handleMargins();
|
||
|
||
if (isHorizontal) {
|
||
me.width = me._length = chart.width - me.margins.left - me.margins.right;
|
||
me.height = minSize.height;
|
||
} else {
|
||
me.width = minSize.width;
|
||
me.height = me._length = chart.height - me.margins.top - me.margins.bottom;
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Handle margins and padding interactions
|
||
* @private
|
||
*/
|
||
handleMargins: function() {
|
||
var me = this;
|
||
if (me.margins) {
|
||
me.margins.left = Math.max(me.paddingLeft, me.margins.left);
|
||
me.margins.top = Math.max(me.paddingTop, me.margins.top);
|
||
me.margins.right = Math.max(me.paddingRight, me.margins.right);
|
||
me.margins.bottom = Math.max(me.paddingBottom, me.margins.bottom);
|
||
}
|
||
},
|
||
|
||
afterFit: function() {
|
||
helpers$1.callback(this.options.afterFit, [this]);
|
||
},
|
||
|
||
// Shared Methods
|
||
isHorizontal: function() {
|
||
var pos = this.options.position;
|
||
return pos === 'top' || pos === 'bottom';
|
||
},
|
||
isFullWidth: function() {
|
||
return this.options.fullWidth;
|
||
},
|
||
|
||
// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not
|
||
getRightValue: function(rawValue) {
|
||
// Null and undefined values first
|
||
if (isNullOrUndef(rawValue)) {
|
||
return NaN;
|
||
}
|
||
// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values
|
||
if ((typeof rawValue === 'number' || rawValue instanceof Number) && !isFinite(rawValue)) {
|
||
return NaN;
|
||
}
|
||
|
||
// If it is in fact an object, dive in one more level
|
||
if (rawValue) {
|
||
if (this.isHorizontal()) {
|
||
if (rawValue.x !== undefined) {
|
||
return this.getRightValue(rawValue.x);
|
||
}
|
||
} else if (rawValue.y !== undefined) {
|
||
return this.getRightValue(rawValue.y);
|
||
}
|
||
}
|
||
|
||
// Value is good, return it
|
||
return rawValue;
|
||
},
|
||
|
||
_convertTicksToLabels: function(ticks) {
|
||
var me = this;
|
||
var labels, i, ilen;
|
||
|
||
me.ticks = ticks.map(function(tick) {
|
||
return tick.value;
|
||
});
|
||
|
||
me.beforeTickToLabelConversion();
|
||
|
||
// New implementations should return the formatted tick labels but for BACKWARD
|
||
// COMPAT, we still support no return (`this.ticks` internally changed by calling
|
||
// this method and supposed to contain only string values).
|
||
labels = me.convertTicksToLabels(ticks) || me.ticks;
|
||
|
||
me.afterTickToLabelConversion();
|
||
|
||
// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)
|
||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||
ticks[i].label = labels[i];
|
||
}
|
||
|
||
return labels;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getLabelSizes: function() {
|
||
var me = this;
|
||
var labelSizes = me._labelSizes;
|
||
|
||
if (!labelSizes) {
|
||
me._labelSizes = labelSizes = computeLabelSizes(me.ctx, parseTickFontOptions(me.options.ticks), me.getTicks(), me.longestTextCache);
|
||
me.longestLabelWidth = labelSizes.widest.width;
|
||
}
|
||
|
||
return labelSizes;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_parseValue: function(value) {
|
||
var start, end, min, max;
|
||
|
||
if (isArray(value)) {
|
||
start = +this.getRightValue(value[0]);
|
||
end = +this.getRightValue(value[1]);
|
||
min = Math.min(start, end);
|
||
max = Math.max(start, end);
|
||
} else {
|
||
value = +this.getRightValue(value);
|
||
start = undefined;
|
||
end = value;
|
||
min = value;
|
||
max = value;
|
||
}
|
||
|
||
return {
|
||
min: min,
|
||
max: max,
|
||
start: start,
|
||
end: end
|
||
};
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getScaleLabel: function(rawValue) {
|
||
var v = this._parseValue(rawValue);
|
||
if (v.start !== undefined) {
|
||
return '[' + v.start + ', ' + v.end + ']';
|
||
}
|
||
|
||
return +this.getRightValue(rawValue);
|
||
},
|
||
|
||
/**
|
||
* Used to get the value to display in the tooltip for the data at the given index
|
||
* @param index
|
||
* @param datasetIndex
|
||
*/
|
||
getLabelForIndex: helpers$1.noop,
|
||
|
||
/**
|
||
* Returns the location of the given data point. Value can either be an index or a numerical value
|
||
* The coordinate (0, 0) is at the upper-left corner of the canvas
|
||
* @param value
|
||
* @param index
|
||
* @param datasetIndex
|
||
*/
|
||
getPixelForValue: helpers$1.noop,
|
||
|
||
/**
|
||
* Used to get the data value from a given pixel. This is the inverse of getPixelForValue
|
||
* The coordinate (0, 0) is at the upper-left corner of the canvas
|
||
* @param pixel
|
||
*/
|
||
getValueForPixel: helpers$1.noop,
|
||
|
||
/**
|
||
* Returns the location of the tick at the given index
|
||
* The coordinate (0, 0) is at the upper-left corner of the canvas
|
||
*/
|
||
getPixelForTick: function(index) {
|
||
var me = this;
|
||
var offset = me.options.offset;
|
||
var numTicks = me._ticks.length;
|
||
var tickWidth = 1 / Math.max(numTicks - (offset ? 0 : 1), 1);
|
||
|
||
return index < 0 || index > numTicks - 1
|
||
? null
|
||
: me.getPixelForDecimal(index * tickWidth + (offset ? tickWidth / 2 : 0));
|
||
},
|
||
|
||
/**
|
||
* Utility for getting the pixel location of a percentage of scale
|
||
* The coordinate (0, 0) is at the upper-left corner of the canvas
|
||
*/
|
||
getPixelForDecimal: function(decimal) {
|
||
var me = this;
|
||
|
||
if (me._reversePixels) {
|
||
decimal = 1 - decimal;
|
||
}
|
||
|
||
return me._startPixel + decimal * me._length;
|
||
},
|
||
|
||
getDecimalForPixel: function(pixel) {
|
||
var decimal = (pixel - this._startPixel) / this._length;
|
||
return this._reversePixels ? 1 - decimal : decimal;
|
||
},
|
||
|
||
/**
|
||
* Returns the pixel for the minimum chart value
|
||
* The coordinate (0, 0) is at the upper-left corner of the canvas
|
||
*/
|
||
getBasePixel: function() {
|
||
return this.getPixelForValue(this.getBaseValue());
|
||
},
|
||
|
||
getBaseValue: function() {
|
||
var me = this;
|
||
var min = me.min;
|
||
var max = me.max;
|
||
|
||
return me.beginAtZero ? 0 :
|
||
min < 0 && max < 0 ? max :
|
||
min > 0 && max > 0 ? min :
|
||
0;
|
||
},
|
||
|
||
/**
|
||
* Returns a subset of ticks to be plotted to avoid overlapping labels.
|
||
* @private
|
||
*/
|
||
_autoSkip: function(ticks) {
|
||
var me = this;
|
||
var tickOpts = me.options.ticks;
|
||
var axisLength = me._length;
|
||
var ticksLimit = tickOpts.maxTicksLimit || axisLength / me._tickSize() + 1;
|
||
var majorIndices = tickOpts.major.enabled ? getMajorIndices(ticks) : [];
|
||
var numMajorIndices = majorIndices.length;
|
||
var first = majorIndices[0];
|
||
var last = majorIndices[numMajorIndices - 1];
|
||
var i, ilen, spacing, avgMajorSpacing;
|
||
|
||
// If there are too many major ticks to display them all
|
||
if (numMajorIndices > ticksLimit) {
|
||
skipMajors(ticks, majorIndices, numMajorIndices / ticksLimit);
|
||
return nonSkipped(ticks);
|
||
}
|
||
|
||
spacing = calculateSpacing(majorIndices, ticks, axisLength, ticksLimit);
|
||
|
||
if (numMajorIndices > 0) {
|
||
for (i = 0, ilen = numMajorIndices - 1; i < ilen; i++) {
|
||
skip(ticks, spacing, majorIndices[i], majorIndices[i + 1]);
|
||
}
|
||
avgMajorSpacing = numMajorIndices > 1 ? (last - first) / (numMajorIndices - 1) : null;
|
||
skip(ticks, spacing, helpers$1.isNullOrUndef(avgMajorSpacing) ? 0 : first - avgMajorSpacing, first);
|
||
skip(ticks, spacing, last, helpers$1.isNullOrUndef(avgMajorSpacing) ? ticks.length : last + avgMajorSpacing);
|
||
return nonSkipped(ticks);
|
||
}
|
||
skip(ticks, spacing);
|
||
return nonSkipped(ticks);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_tickSize: function() {
|
||
var me = this;
|
||
var optionTicks = me.options.ticks;
|
||
|
||
// Calculate space needed by label in axis direction.
|
||
var rot = helpers$1.toRadians(me.labelRotation);
|
||
var cos = Math.abs(Math.cos(rot));
|
||
var sin = Math.abs(Math.sin(rot));
|
||
|
||
var labelSizes = me._getLabelSizes();
|
||
var padding = optionTicks.autoSkipPadding || 0;
|
||
var w = labelSizes ? labelSizes.widest.width + padding : 0;
|
||
var h = labelSizes ? labelSizes.highest.height + padding : 0;
|
||
|
||
// Calculate space needed for 1 tick in axis direction.
|
||
return me.isHorizontal()
|
||
? h * cos > w * sin ? w / cos : h / sin
|
||
: h * sin < w * cos ? h / cos : w / sin;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_isVisible: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var display = me.options.display;
|
||
var i, ilen, meta;
|
||
|
||
if (display !== 'auto') {
|
||
return !!display;
|
||
}
|
||
|
||
// When 'auto', the scale is visible if at least one associated dataset is visible.
|
||
for (i = 0, ilen = chart.data.datasets.length; i < ilen; ++i) {
|
||
if (chart.isDatasetVisible(i)) {
|
||
meta = chart.getDatasetMeta(i);
|
||
if (meta.xAxisID === me.id || meta.yAxisID === me.id) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_computeGridLineItems: function(chartArea) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var options = me.options;
|
||
var gridLines = options.gridLines;
|
||
var position = options.position;
|
||
var offsetGridLines = gridLines.offsetGridLines;
|
||
var isHorizontal = me.isHorizontal();
|
||
var ticks = me._ticksToDraw;
|
||
var ticksLength = ticks.length + (offsetGridLines ? 1 : 0);
|
||
|
||
var tl = getTickMarkLength(gridLines);
|
||
var items = [];
|
||
var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
|
||
var axisHalfWidth = axisWidth / 2;
|
||
var alignPixel = helpers$1._alignPixel;
|
||
var alignBorderValue = function(pixel) {
|
||
return alignPixel(chart, pixel, axisWidth);
|
||
};
|
||
var borderValue, i, tick, lineValue, alignedLineValue;
|
||
var tx1, ty1, tx2, ty2, x1, y1, x2, y2, lineWidth, lineColor, borderDash, borderDashOffset;
|
||
|
||
if (position === 'top') {
|
||
borderValue = alignBorderValue(me.bottom);
|
||
ty1 = me.bottom - tl;
|
||
ty2 = borderValue - axisHalfWidth;
|
||
y1 = alignBorderValue(chartArea.top) + axisHalfWidth;
|
||
y2 = chartArea.bottom;
|
||
} else if (position === 'bottom') {
|
||
borderValue = alignBorderValue(me.top);
|
||
y1 = chartArea.top;
|
||
y2 = alignBorderValue(chartArea.bottom) - axisHalfWidth;
|
||
ty1 = borderValue + axisHalfWidth;
|
||
ty2 = me.top + tl;
|
||
} else if (position === 'left') {
|
||
borderValue = alignBorderValue(me.right);
|
||
tx1 = me.right - tl;
|
||
tx2 = borderValue - axisHalfWidth;
|
||
x1 = alignBorderValue(chartArea.left) + axisHalfWidth;
|
||
x2 = chartArea.right;
|
||
} else {
|
||
borderValue = alignBorderValue(me.left);
|
||
x1 = chartArea.left;
|
||
x2 = alignBorderValue(chartArea.right) - axisHalfWidth;
|
||
tx1 = borderValue + axisHalfWidth;
|
||
tx2 = me.left + tl;
|
||
}
|
||
|
||
for (i = 0; i < ticksLength; ++i) {
|
||
tick = ticks[i] || {};
|
||
|
||
// autoskipper skipped this tick (#4635)
|
||
if (isNullOrUndef(tick.label) && i < ticks.length) {
|
||
continue;
|
||
}
|
||
|
||
if (i === me.zeroLineIndex && options.offset === offsetGridLines) {
|
||
// Draw the first index specially
|
||
lineWidth = gridLines.zeroLineWidth;
|
||
lineColor = gridLines.zeroLineColor;
|
||
borderDash = gridLines.zeroLineBorderDash || [];
|
||
borderDashOffset = gridLines.zeroLineBorderDashOffset || 0.0;
|
||
} else {
|
||
lineWidth = valueAtIndexOrDefault(gridLines.lineWidth, i, 1);
|
||
lineColor = valueAtIndexOrDefault(gridLines.color, i, 'rgba(0,0,0,0.1)');
|
||
borderDash = gridLines.borderDash || [];
|
||
borderDashOffset = gridLines.borderDashOffset || 0.0;
|
||
}
|
||
|
||
lineValue = getPixelForGridLine(me, tick._index || i, offsetGridLines);
|
||
|
||
// Skip if the pixel is out of the range
|
||
if (lineValue === undefined) {
|
||
continue;
|
||
}
|
||
|
||
alignedLineValue = alignPixel(chart, lineValue, lineWidth);
|
||
|
||
if (isHorizontal) {
|
||
tx1 = tx2 = x1 = x2 = alignedLineValue;
|
||
} else {
|
||
ty1 = ty2 = y1 = y2 = alignedLineValue;
|
||
}
|
||
|
||
items.push({
|
||
tx1: tx1,
|
||
ty1: ty1,
|
||
tx2: tx2,
|
||
ty2: ty2,
|
||
x1: x1,
|
||
y1: y1,
|
||
x2: x2,
|
||
y2: y2,
|
||
width: lineWidth,
|
||
color: lineColor,
|
||
borderDash: borderDash,
|
||
borderDashOffset: borderDashOffset,
|
||
});
|
||
}
|
||
|
||
items.ticksLength = ticksLength;
|
||
items.borderValue = borderValue;
|
||
|
||
return items;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_computeLabelItems: function() {
|
||
var me = this;
|
||
var options = me.options;
|
||
var optionTicks = options.ticks;
|
||
var position = options.position;
|
||
var isMirrored = optionTicks.mirror;
|
||
var isHorizontal = me.isHorizontal();
|
||
var ticks = me._ticksToDraw;
|
||
var fonts = parseTickFontOptions(optionTicks);
|
||
var tickPadding = optionTicks.padding;
|
||
var tl = getTickMarkLength(options.gridLines);
|
||
var rotation = -helpers$1.toRadians(me.labelRotation);
|
||
var items = [];
|
||
var i, ilen, tick, label, x, y, textAlign, pixel, font, lineHeight, lineCount, textOffset;
|
||
|
||
if (position === 'top') {
|
||
y = me.bottom - tl - tickPadding;
|
||
textAlign = !rotation ? 'center' : 'left';
|
||
} else if (position === 'bottom') {
|
||
y = me.top + tl + tickPadding;
|
||
textAlign = !rotation ? 'center' : 'right';
|
||
} else if (position === 'left') {
|
||
x = me.right - (isMirrored ? 0 : tl) - tickPadding;
|
||
textAlign = isMirrored ? 'left' : 'right';
|
||
} else {
|
||
x = me.left + (isMirrored ? 0 : tl) + tickPadding;
|
||
textAlign = isMirrored ? 'right' : 'left';
|
||
}
|
||
|
||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||
tick = ticks[i];
|
||
label = tick.label;
|
||
|
||
// autoskipper skipped this tick (#4635)
|
||
if (isNullOrUndef(label)) {
|
||
continue;
|
||
}
|
||
|
||
pixel = me.getPixelForTick(tick._index || i) + optionTicks.labelOffset;
|
||
font = tick.major ? fonts.major : fonts.minor;
|
||
lineHeight = font.lineHeight;
|
||
lineCount = isArray(label) ? label.length : 1;
|
||
|
||
if (isHorizontal) {
|
||
x = pixel;
|
||
textOffset = position === 'top'
|
||
? ((!rotation ? 0.5 : 1) - lineCount) * lineHeight
|
||
: (!rotation ? 0.5 : 0) * lineHeight;
|
||
} else {
|
||
y = pixel;
|
||
textOffset = (1 - lineCount) * lineHeight / 2;
|
||
}
|
||
|
||
items.push({
|
||
x: x,
|
||
y: y,
|
||
rotation: rotation,
|
||
label: label,
|
||
font: font,
|
||
textOffset: textOffset,
|
||
textAlign: textAlign
|
||
});
|
||
}
|
||
|
||
return items;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawGrid: function(chartArea) {
|
||
var me = this;
|
||
var gridLines = me.options.gridLines;
|
||
|
||
if (!gridLines.display) {
|
||
return;
|
||
}
|
||
|
||
var ctx = me.ctx;
|
||
var chart = me.chart;
|
||
var alignPixel = helpers$1._alignPixel;
|
||
var axisWidth = gridLines.drawBorder ? valueAtIndexOrDefault(gridLines.lineWidth, 0, 0) : 0;
|
||
var items = me._gridLineItems || (me._gridLineItems = me._computeGridLineItems(chartArea));
|
||
var width, color, i, ilen, item;
|
||
|
||
for (i = 0, ilen = items.length; i < ilen; ++i) {
|
||
item = items[i];
|
||
width = item.width;
|
||
color = item.color;
|
||
|
||
if (width && color) {
|
||
ctx.save();
|
||
ctx.lineWidth = width;
|
||
ctx.strokeStyle = color;
|
||
if (ctx.setLineDash) {
|
||
ctx.setLineDash(item.borderDash);
|
||
ctx.lineDashOffset = item.borderDashOffset;
|
||
}
|
||
|
||
ctx.beginPath();
|
||
|
||
if (gridLines.drawTicks) {
|
||
ctx.moveTo(item.tx1, item.ty1);
|
||
ctx.lineTo(item.tx2, item.ty2);
|
||
}
|
||
|
||
if (gridLines.drawOnChartArea) {
|
||
ctx.moveTo(item.x1, item.y1);
|
||
ctx.lineTo(item.x2, item.y2);
|
||
}
|
||
|
||
ctx.stroke();
|
||
ctx.restore();
|
||
}
|
||
}
|
||
|
||
if (axisWidth) {
|
||
// Draw the line at the edge of the axis
|
||
var firstLineWidth = axisWidth;
|
||
var lastLineWidth = valueAtIndexOrDefault(gridLines.lineWidth, items.ticksLength - 1, 1);
|
||
var borderValue = items.borderValue;
|
||
var x1, x2, y1, y2;
|
||
|
||
if (me.isHorizontal()) {
|
||
x1 = alignPixel(chart, me.left, firstLineWidth) - firstLineWidth / 2;
|
||
x2 = alignPixel(chart, me.right, lastLineWidth) + lastLineWidth / 2;
|
||
y1 = y2 = borderValue;
|
||
} else {
|
||
y1 = alignPixel(chart, me.top, firstLineWidth) - firstLineWidth / 2;
|
||
y2 = alignPixel(chart, me.bottom, lastLineWidth) + lastLineWidth / 2;
|
||
x1 = x2 = borderValue;
|
||
}
|
||
|
||
ctx.lineWidth = axisWidth;
|
||
ctx.strokeStyle = valueAtIndexOrDefault(gridLines.color, 0);
|
||
ctx.beginPath();
|
||
ctx.moveTo(x1, y1);
|
||
ctx.lineTo(x2, y2);
|
||
ctx.stroke();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawLabels: function() {
|
||
var me = this;
|
||
var optionTicks = me.options.ticks;
|
||
|
||
if (!optionTicks.display) {
|
||
return;
|
||
}
|
||
|
||
var ctx = me.ctx;
|
||
var items = me._labelItems || (me._labelItems = me._computeLabelItems());
|
||
var i, j, ilen, jlen, item, tickFont, label, y;
|
||
|
||
for (i = 0, ilen = items.length; i < ilen; ++i) {
|
||
item = items[i];
|
||
tickFont = item.font;
|
||
|
||
// Make sure we draw text in the correct color and font
|
||
ctx.save();
|
||
ctx.translate(item.x, item.y);
|
||
ctx.rotate(item.rotation);
|
||
ctx.font = tickFont.string;
|
||
ctx.fillStyle = tickFont.color;
|
||
ctx.textBaseline = 'middle';
|
||
ctx.textAlign = item.textAlign;
|
||
|
||
label = item.label;
|
||
y = item.textOffset;
|
||
if (isArray(label)) {
|
||
for (j = 0, jlen = label.length; j < jlen; ++j) {
|
||
// We just make sure the multiline element is a string here..
|
||
ctx.fillText('' + label[j], 0, y);
|
||
y += tickFont.lineHeight;
|
||
}
|
||
} else {
|
||
ctx.fillText(label, 0, y);
|
||
}
|
||
ctx.restore();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawTitle: function() {
|
||
var me = this;
|
||
var ctx = me.ctx;
|
||
var options = me.options;
|
||
var scaleLabel = options.scaleLabel;
|
||
|
||
if (!scaleLabel.display) {
|
||
return;
|
||
}
|
||
|
||
var scaleLabelFontColor = valueOrDefault$a(scaleLabel.fontColor, core_defaults.global.defaultFontColor);
|
||
var scaleLabelFont = helpers$1.options._parseFont(scaleLabel);
|
||
var scaleLabelPadding = helpers$1.options.toPadding(scaleLabel.padding);
|
||
var halfLineHeight = scaleLabelFont.lineHeight / 2;
|
||
var position = options.position;
|
||
var rotation = 0;
|
||
var scaleLabelX, scaleLabelY;
|
||
|
||
if (me.isHorizontal()) {
|
||
scaleLabelX = me.left + me.width / 2; // midpoint of the width
|
||
scaleLabelY = position === 'bottom'
|
||
? me.bottom - halfLineHeight - scaleLabelPadding.bottom
|
||
: me.top + halfLineHeight + scaleLabelPadding.top;
|
||
} else {
|
||
var isLeft = position === 'left';
|
||
scaleLabelX = isLeft
|
||
? me.left + halfLineHeight + scaleLabelPadding.top
|
||
: me.right - halfLineHeight - scaleLabelPadding.top;
|
||
scaleLabelY = me.top + me.height / 2;
|
||
rotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;
|
||
}
|
||
|
||
ctx.save();
|
||
ctx.translate(scaleLabelX, scaleLabelY);
|
||
ctx.rotate(rotation);
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
ctx.fillStyle = scaleLabelFontColor; // render in correct colour
|
||
ctx.font = scaleLabelFont.string;
|
||
ctx.fillText(scaleLabel.labelString, 0, 0);
|
||
ctx.restore();
|
||
},
|
||
|
||
draw: function(chartArea) {
|
||
var me = this;
|
||
|
||
if (!me._isVisible()) {
|
||
return;
|
||
}
|
||
|
||
me._drawGrid(chartArea);
|
||
me._drawTitle();
|
||
me._drawLabels();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_layers: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var tz = opts.ticks && opts.ticks.z || 0;
|
||
var gz = opts.gridLines && opts.gridLines.z || 0;
|
||
|
||
if (!me._isVisible() || tz === gz || me.draw !== me._draw) {
|
||
// backward compatibility: draw has been overridden by custom scale
|
||
return [{
|
||
z: tz,
|
||
draw: function() {
|
||
me.draw.apply(me, arguments);
|
||
}
|
||
}];
|
||
}
|
||
|
||
return [{
|
||
z: gz,
|
||
draw: function() {
|
||
me._drawGrid.apply(me, arguments);
|
||
me._drawTitle.apply(me, arguments);
|
||
}
|
||
}, {
|
||
z: tz,
|
||
draw: function() {
|
||
me._drawLabels.apply(me, arguments);
|
||
}
|
||
}];
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getMatchingVisibleMetas: function(type) {
|
||
var me = this;
|
||
var isHorizontal = me.isHorizontal();
|
||
return me.chart._getSortedVisibleDatasetMetas()
|
||
.filter(function(meta) {
|
||
return (!type || meta.type === type)
|
||
&& (isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id);
|
||
});
|
||
}
|
||
});
|
||
|
||
Scale.prototype._draw = Scale.prototype.draw;
|
||
|
||
var core_scale = Scale;
|
||
|
||
var isNullOrUndef$1 = helpers$1.isNullOrUndef;
|
||
|
||
var defaultConfig = {
|
||
position: 'bottom'
|
||
};
|
||
|
||
var scale_category = core_scale.extend({
|
||
determineDataLimits: function() {
|
||
var me = this;
|
||
var labels = me._getLabels();
|
||
var ticksOpts = me.options.ticks;
|
||
var min = ticksOpts.min;
|
||
var max = ticksOpts.max;
|
||
var minIndex = 0;
|
||
var maxIndex = labels.length - 1;
|
||
var findIndex;
|
||
|
||
if (min !== undefined) {
|
||
// user specified min value
|
||
findIndex = labels.indexOf(min);
|
||
if (findIndex >= 0) {
|
||
minIndex = findIndex;
|
||
}
|
||
}
|
||
|
||
if (max !== undefined) {
|
||
// user specified max value
|
||
findIndex = labels.indexOf(max);
|
||
if (findIndex >= 0) {
|
||
maxIndex = findIndex;
|
||
}
|
||
}
|
||
|
||
me.minIndex = minIndex;
|
||
me.maxIndex = maxIndex;
|
||
me.min = labels[minIndex];
|
||
me.max = labels[maxIndex];
|
||
},
|
||
|
||
buildTicks: function() {
|
||
var me = this;
|
||
var labels = me._getLabels();
|
||
var minIndex = me.minIndex;
|
||
var maxIndex = me.maxIndex;
|
||
|
||
// If we are viewing some subset of labels, slice the original array
|
||
me.ticks = (minIndex === 0 && maxIndex === labels.length - 1) ? labels : labels.slice(minIndex, maxIndex + 1);
|
||
},
|
||
|
||
getLabelForIndex: function(index, datasetIndex) {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
|
||
if (chart.getDatasetMeta(datasetIndex).controller._getValueScaleId() === me.id) {
|
||
return me.getRightValue(chart.data.datasets[datasetIndex].data[index]);
|
||
}
|
||
|
||
return me._getLabels()[index];
|
||
},
|
||
|
||
_configure: function() {
|
||
var me = this;
|
||
var offset = me.options.offset;
|
||
var ticks = me.ticks;
|
||
|
||
core_scale.prototype._configure.call(me);
|
||
|
||
if (!me.isHorizontal()) {
|
||
// For backward compatibility, vertical category scale reverse is inverted.
|
||
me._reversePixels = !me._reversePixels;
|
||
}
|
||
|
||
if (!ticks) {
|
||
return;
|
||
}
|
||
|
||
me._startValue = me.minIndex - (offset ? 0.5 : 0);
|
||
me._valueRange = Math.max(ticks.length - (offset ? 0 : 1), 1);
|
||
},
|
||
|
||
// Used to get data value locations. Value can either be an index or a numerical value
|
||
getPixelForValue: function(value, index, datasetIndex) {
|
||
var me = this;
|
||
var valueCategory, labels, idx;
|
||
|
||
if (!isNullOrUndef$1(index) && !isNullOrUndef$1(datasetIndex)) {
|
||
value = me.chart.data.datasets[datasetIndex].data[index];
|
||
}
|
||
|
||
// If value is a data object, then index is the index in the data array,
|
||
// not the index of the scale. We need to change that.
|
||
if (!isNullOrUndef$1(value)) {
|
||
valueCategory = me.isHorizontal() ? value.x : value.y;
|
||
}
|
||
if (valueCategory !== undefined || (value !== undefined && isNaN(index))) {
|
||
labels = me._getLabels();
|
||
value = helpers$1.valueOrDefault(valueCategory, value);
|
||
idx = labels.indexOf(value);
|
||
index = idx !== -1 ? idx : index;
|
||
if (isNaN(index)) {
|
||
index = value;
|
||
}
|
||
}
|
||
return me.getPixelForDecimal((index - me._startValue) / me._valueRange);
|
||
},
|
||
|
||
getPixelForTick: function(index) {
|
||
var ticks = this.ticks;
|
||
return index < 0 || index > ticks.length - 1
|
||
? null
|
||
: this.getPixelForValue(ticks[index], index + this.minIndex);
|
||
},
|
||
|
||
getValueForPixel: function(pixel) {
|
||
var me = this;
|
||
var value = Math.round(me._startValue + me.getDecimalForPixel(pixel) * me._valueRange);
|
||
return Math.min(Math.max(value, 0), me.ticks.length - 1);
|
||
},
|
||
|
||
getBasePixel: function() {
|
||
return this.bottom;
|
||
}
|
||
});
|
||
|
||
// INTERNAL: static default options, registered in src/index.js
|
||
var _defaults = defaultConfig;
|
||
scale_category._defaults = _defaults;
|
||
|
||
var noop = helpers$1.noop;
|
||
var isNullOrUndef$2 = helpers$1.isNullOrUndef;
|
||
|
||
/**
|
||
* Generate a set of linear ticks
|
||
* @param generationOptions the options used to generate the ticks
|
||
* @param dataRange the range of the data
|
||
* @returns {number[]} array of tick values
|
||
*/
|
||
function generateTicks(generationOptions, dataRange) {
|
||
var ticks = [];
|
||
// To get a "nice" value for the tick spacing, we will use the appropriately named
|
||
// "nice number" algorithm. See https://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
|
||
// for details.
|
||
|
||
var MIN_SPACING = 1e-14;
|
||
var stepSize = generationOptions.stepSize;
|
||
var unit = stepSize || 1;
|
||
var maxNumSpaces = generationOptions.maxTicks - 1;
|
||
var min = generationOptions.min;
|
||
var max = generationOptions.max;
|
||
var precision = generationOptions.precision;
|
||
var rmin = dataRange.min;
|
||
var rmax = dataRange.max;
|
||
var spacing = helpers$1.niceNum((rmax - rmin) / maxNumSpaces / unit) * unit;
|
||
var factor, niceMin, niceMax, numSpaces;
|
||
|
||
// Beyond MIN_SPACING floating point numbers being to lose precision
|
||
// such that we can't do the math necessary to generate ticks
|
||
if (spacing < MIN_SPACING && isNullOrUndef$2(min) && isNullOrUndef$2(max)) {
|
||
return [rmin, rmax];
|
||
}
|
||
|
||
numSpaces = Math.ceil(rmax / spacing) - Math.floor(rmin / spacing);
|
||
if (numSpaces > maxNumSpaces) {
|
||
// If the calculated num of spaces exceeds maxNumSpaces, recalculate it
|
||
spacing = helpers$1.niceNum(numSpaces * spacing / maxNumSpaces / unit) * unit;
|
||
}
|
||
|
||
if (stepSize || isNullOrUndef$2(precision)) {
|
||
// If a precision is not specified, calculate factor based on spacing
|
||
factor = Math.pow(10, helpers$1._decimalPlaces(spacing));
|
||
} else {
|
||
// If the user specified a precision, round to that number of decimal places
|
||
factor = Math.pow(10, precision);
|
||
spacing = Math.ceil(spacing * factor) / factor;
|
||
}
|
||
|
||
niceMin = Math.floor(rmin / spacing) * spacing;
|
||
niceMax = Math.ceil(rmax / spacing) * spacing;
|
||
|
||
// If min, max and stepSize is set and they make an evenly spaced scale use it.
|
||
if (stepSize) {
|
||
// If very close to our whole number, use it.
|
||
if (!isNullOrUndef$2(min) && helpers$1.almostWhole(min / spacing, spacing / 1000)) {
|
||
niceMin = min;
|
||
}
|
||
if (!isNullOrUndef$2(max) && helpers$1.almostWhole(max / spacing, spacing / 1000)) {
|
||
niceMax = max;
|
||
}
|
||
}
|
||
|
||
numSpaces = (niceMax - niceMin) / spacing;
|
||
// If very close to our rounded value, use it.
|
||
if (helpers$1.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {
|
||
numSpaces = Math.round(numSpaces);
|
||
} else {
|
||
numSpaces = Math.ceil(numSpaces);
|
||
}
|
||
|
||
niceMin = Math.round(niceMin * factor) / factor;
|
||
niceMax = Math.round(niceMax * factor) / factor;
|
||
ticks.push(isNullOrUndef$2(min) ? niceMin : min);
|
||
for (var j = 1; j < numSpaces; ++j) {
|
||
ticks.push(Math.round((niceMin + j * spacing) * factor) / factor);
|
||
}
|
||
ticks.push(isNullOrUndef$2(max) ? niceMax : max);
|
||
|
||
return ticks;
|
||
}
|
||
|
||
var scale_linearbase = core_scale.extend({
|
||
getRightValue: function(value) {
|
||
if (typeof value === 'string') {
|
||
return +value;
|
||
}
|
||
return core_scale.prototype.getRightValue.call(this, value);
|
||
},
|
||
|
||
handleTickRangeOptions: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var tickOpts = opts.ticks;
|
||
|
||
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
|
||
// do nothing since that would make the chart weird. If the user really wants a weird chart
|
||
// axis, they can manually override it
|
||
if (tickOpts.beginAtZero) {
|
||
var minSign = helpers$1.sign(me.min);
|
||
var maxSign = helpers$1.sign(me.max);
|
||
|
||
if (minSign < 0 && maxSign < 0) {
|
||
// move the top up to 0
|
||
me.max = 0;
|
||
} else if (minSign > 0 && maxSign > 0) {
|
||
// move the bottom down to 0
|
||
me.min = 0;
|
||
}
|
||
}
|
||
|
||
var setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;
|
||
var setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;
|
||
|
||
if (tickOpts.min !== undefined) {
|
||
me.min = tickOpts.min;
|
||
} else if (tickOpts.suggestedMin !== undefined) {
|
||
if (me.min === null) {
|
||
me.min = tickOpts.suggestedMin;
|
||
} else {
|
||
me.min = Math.min(me.min, tickOpts.suggestedMin);
|
||
}
|
||
}
|
||
|
||
if (tickOpts.max !== undefined) {
|
||
me.max = tickOpts.max;
|
||
} else if (tickOpts.suggestedMax !== undefined) {
|
||
if (me.max === null) {
|
||
me.max = tickOpts.suggestedMax;
|
||
} else {
|
||
me.max = Math.max(me.max, tickOpts.suggestedMax);
|
||
}
|
||
}
|
||
|
||
if (setMin !== setMax) {
|
||
// We set the min or the max but not both.
|
||
// So ensure that our range is good
|
||
// Inverted or 0 length range can happen when
|
||
// ticks.min is set, and no datasets are visible
|
||
if (me.min >= me.max) {
|
||
if (setMin) {
|
||
me.max = me.min + 1;
|
||
} else {
|
||
me.min = me.max - 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (me.min === me.max) {
|
||
me.max++;
|
||
|
||
if (!tickOpts.beginAtZero) {
|
||
me.min--;
|
||
}
|
||
}
|
||
},
|
||
|
||
getTickLimit: function() {
|
||
var me = this;
|
||
var tickOpts = me.options.ticks;
|
||
var stepSize = tickOpts.stepSize;
|
||
var maxTicksLimit = tickOpts.maxTicksLimit;
|
||
var maxTicks;
|
||
|
||
if (stepSize) {
|
||
maxTicks = Math.ceil(me.max / stepSize) - Math.floor(me.min / stepSize) + 1;
|
||
} else {
|
||
maxTicks = me._computeTickLimit();
|
||
maxTicksLimit = maxTicksLimit || 11;
|
||
}
|
||
|
||
if (maxTicksLimit) {
|
||
maxTicks = Math.min(maxTicksLimit, maxTicks);
|
||
}
|
||
|
||
return maxTicks;
|
||
},
|
||
|
||
_computeTickLimit: function() {
|
||
return Number.POSITIVE_INFINITY;
|
||
},
|
||
|
||
handleDirectionalChanges: noop,
|
||
|
||
buildTicks: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var tickOpts = opts.ticks;
|
||
|
||
// Figure out what the max number of ticks we can support it is based on the size of
|
||
// the axis area. For now, we say that the minimum tick spacing in pixels must be 40
|
||
// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
|
||
// the graph. Make sure we always have at least 2 ticks
|
||
var maxTicks = me.getTickLimit();
|
||
maxTicks = Math.max(2, maxTicks);
|
||
|
||
var numericGeneratorOptions = {
|
||
maxTicks: maxTicks,
|
||
min: tickOpts.min,
|
||
max: tickOpts.max,
|
||
precision: tickOpts.precision,
|
||
stepSize: helpers$1.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)
|
||
};
|
||
var ticks = me.ticks = generateTicks(numericGeneratorOptions, me);
|
||
|
||
me.handleDirectionalChanges();
|
||
|
||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||
// range of the scale
|
||
me.max = helpers$1.max(ticks);
|
||
me.min = helpers$1.min(ticks);
|
||
|
||
if (tickOpts.reverse) {
|
||
ticks.reverse();
|
||
|
||
me.start = me.max;
|
||
me.end = me.min;
|
||
} else {
|
||
me.start = me.min;
|
||
me.end = me.max;
|
||
}
|
||
},
|
||
|
||
convertTicksToLabels: function() {
|
||
var me = this;
|
||
me.ticksAsNumbers = me.ticks.slice();
|
||
me.zeroLineIndex = me.ticks.indexOf(0);
|
||
|
||
core_scale.prototype.convertTicksToLabels.call(me);
|
||
},
|
||
|
||
_configure: function() {
|
||
var me = this;
|
||
var ticks = me.getTicks();
|
||
var start = me.min;
|
||
var end = me.max;
|
||
var offset;
|
||
|
||
core_scale.prototype._configure.call(me);
|
||
|
||
if (me.options.offset && ticks.length) {
|
||
offset = (end - start) / Math.max(ticks.length - 1, 1) / 2;
|
||
start -= offset;
|
||
end += offset;
|
||
}
|
||
me._startValue = start;
|
||
me._endValue = end;
|
||
me._valueRange = end - start;
|
||
}
|
||
});
|
||
|
||
var defaultConfig$1 = {
|
||
position: 'left',
|
||
ticks: {
|
||
callback: core_ticks.formatters.linear
|
||
}
|
||
};
|
||
|
||
var DEFAULT_MIN = 0;
|
||
var DEFAULT_MAX = 1;
|
||
|
||
function getOrCreateStack(stacks, stacked, meta) {
|
||
var key = [
|
||
meta.type,
|
||
// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
|
||
stacked === undefined && meta.stack === undefined ? meta.index : '',
|
||
meta.stack
|
||
].join('.');
|
||
|
||
if (stacks[key] === undefined) {
|
||
stacks[key] = {
|
||
pos: [],
|
||
neg: []
|
||
};
|
||
}
|
||
|
||
return stacks[key];
|
||
}
|
||
|
||
function stackData(scale, stacks, meta, data) {
|
||
var opts = scale.options;
|
||
var stacked = opts.stacked;
|
||
var stack = getOrCreateStack(stacks, stacked, meta);
|
||
var pos = stack.pos;
|
||
var neg = stack.neg;
|
||
var ilen = data.length;
|
||
var i, value;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
value = scale._parseValue(data[i]);
|
||
if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) {
|
||
continue;
|
||
}
|
||
|
||
pos[i] = pos[i] || 0;
|
||
neg[i] = neg[i] || 0;
|
||
|
||
if (opts.relativePoints) {
|
||
pos[i] = 100;
|
||
} else if (value.min < 0 || value.max < 0) {
|
||
neg[i] += value.min;
|
||
} else {
|
||
pos[i] += value.max;
|
||
}
|
||
}
|
||
}
|
||
|
||
function updateMinMax(scale, meta, data) {
|
||
var ilen = data.length;
|
||
var i, value;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
value = scale._parseValue(data[i]);
|
||
if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden) {
|
||
continue;
|
||
}
|
||
|
||
scale.min = Math.min(scale.min, value.min);
|
||
scale.max = Math.max(scale.max, value.max);
|
||
}
|
||
}
|
||
|
||
var scale_linear = scale_linearbase.extend({
|
||
determineDataLimits: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var chart = me.chart;
|
||
var datasets = chart.data.datasets;
|
||
var metasets = me._getMatchingVisibleMetas();
|
||
var hasStacks = opts.stacked;
|
||
var stacks = {};
|
||
var ilen = metasets.length;
|
||
var i, meta, data, values;
|
||
|
||
me.min = Number.POSITIVE_INFINITY;
|
||
me.max = Number.NEGATIVE_INFINITY;
|
||
|
||
if (hasStacks === undefined) {
|
||
for (i = 0; !hasStacks && i < ilen; ++i) {
|
||
meta = metasets[i];
|
||
hasStacks = meta.stack !== undefined;
|
||
}
|
||
}
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
meta = metasets[i];
|
||
data = datasets[meta.index].data;
|
||
if (hasStacks) {
|
||
stackData(me, stacks, meta, data);
|
||
} else {
|
||
updateMinMax(me, meta, data);
|
||
}
|
||
}
|
||
|
||
helpers$1.each(stacks, function(stackValues) {
|
||
values = stackValues.pos.concat(stackValues.neg);
|
||
me.min = Math.min(me.min, helpers$1.min(values));
|
||
me.max = Math.max(me.max, helpers$1.max(values));
|
||
});
|
||
|
||
me.min = helpers$1.isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;
|
||
me.max = helpers$1.isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;
|
||
|
||
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
|
||
me.handleTickRangeOptions();
|
||
},
|
||
|
||
// Returns the maximum number of ticks based on the scale dimension
|
||
_computeTickLimit: function() {
|
||
var me = this;
|
||
var tickFont;
|
||
|
||
if (me.isHorizontal()) {
|
||
return Math.ceil(me.width / 40);
|
||
}
|
||
tickFont = helpers$1.options._parseFont(me.options.ticks);
|
||
return Math.ceil(me.height / tickFont.lineHeight);
|
||
},
|
||
|
||
// Called after the ticks are built. We need
|
||
handleDirectionalChanges: function() {
|
||
if (!this.isHorizontal()) {
|
||
// We are in a vertical orientation. The top value is the highest. So reverse the array
|
||
this.ticks.reverse();
|
||
}
|
||
},
|
||
|
||
getLabelForIndex: function(index, datasetIndex) {
|
||
return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
|
||
},
|
||
|
||
// Utils
|
||
getPixelForValue: function(value) {
|
||
var me = this;
|
||
return me.getPixelForDecimal((+me.getRightValue(value) - me._startValue) / me._valueRange);
|
||
},
|
||
|
||
getValueForPixel: function(pixel) {
|
||
return this._startValue + this.getDecimalForPixel(pixel) * this._valueRange;
|
||
},
|
||
|
||
getPixelForTick: function(index) {
|
||
var ticks = this.ticksAsNumbers;
|
||
if (index < 0 || index > ticks.length - 1) {
|
||
return null;
|
||
}
|
||
return this.getPixelForValue(ticks[index]);
|
||
}
|
||
});
|
||
|
||
// INTERNAL: static default options, registered in src/index.js
|
||
var _defaults$1 = defaultConfig$1;
|
||
scale_linear._defaults = _defaults$1;
|
||
|
||
var valueOrDefault$b = helpers$1.valueOrDefault;
|
||
var log10 = helpers$1.math.log10;
|
||
|
||
/**
|
||
* Generate a set of logarithmic ticks
|
||
* @param generationOptions the options used to generate the ticks
|
||
* @param dataRange the range of the data
|
||
* @returns {number[]} array of tick values
|
||
*/
|
||
function generateTicks$1(generationOptions, dataRange) {
|
||
var ticks = [];
|
||
|
||
var tickVal = valueOrDefault$b(generationOptions.min, Math.pow(10, Math.floor(log10(dataRange.min))));
|
||
|
||
var endExp = Math.floor(log10(dataRange.max));
|
||
var endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));
|
||
var exp, significand;
|
||
|
||
if (tickVal === 0) {
|
||
exp = Math.floor(log10(dataRange.minNotZero));
|
||
significand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));
|
||
|
||
ticks.push(tickVal);
|
||
tickVal = significand * Math.pow(10, exp);
|
||
} else {
|
||
exp = Math.floor(log10(tickVal));
|
||
significand = Math.floor(tickVal / Math.pow(10, exp));
|
||
}
|
||
var precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;
|
||
|
||
do {
|
||
ticks.push(tickVal);
|
||
|
||
++significand;
|
||
if (significand === 10) {
|
||
significand = 1;
|
||
++exp;
|
||
precision = exp >= 0 ? 1 : precision;
|
||
}
|
||
|
||
tickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;
|
||
} while (exp < endExp || (exp === endExp && significand < endSignificand));
|
||
|
||
var lastTick = valueOrDefault$b(generationOptions.max, tickVal);
|
||
ticks.push(lastTick);
|
||
|
||
return ticks;
|
||
}
|
||
|
||
var defaultConfig$2 = {
|
||
position: 'left',
|
||
|
||
// label settings
|
||
ticks: {
|
||
callback: core_ticks.formatters.logarithmic
|
||
}
|
||
};
|
||
|
||
// TODO(v3): change this to positiveOrDefault
|
||
function nonNegativeOrDefault(value, defaultValue) {
|
||
return helpers$1.isFinite(value) && value >= 0 ? value : defaultValue;
|
||
}
|
||
|
||
var scale_logarithmic = core_scale.extend({
|
||
determineDataLimits: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var chart = me.chart;
|
||
var datasets = chart.data.datasets;
|
||
var isHorizontal = me.isHorizontal();
|
||
function IDMatches(meta) {
|
||
return isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;
|
||
}
|
||
var datasetIndex, meta, value, data, i, ilen;
|
||
|
||
// Calculate Range
|
||
me.min = Number.POSITIVE_INFINITY;
|
||
me.max = Number.NEGATIVE_INFINITY;
|
||
me.minNotZero = Number.POSITIVE_INFINITY;
|
||
|
||
var hasStacks = opts.stacked;
|
||
if (hasStacks === undefined) {
|
||
for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
||
meta = chart.getDatasetMeta(datasetIndex);
|
||
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&
|
||
meta.stack !== undefined) {
|
||
hasStacks = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (opts.stacked || hasStacks) {
|
||
var valuesPerStack = {};
|
||
|
||
for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
||
meta = chart.getDatasetMeta(datasetIndex);
|
||
var key = [
|
||
meta.type,
|
||
// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined
|
||
((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),
|
||
meta.stack
|
||
].join('.');
|
||
|
||
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
|
||
if (valuesPerStack[key] === undefined) {
|
||
valuesPerStack[key] = [];
|
||
}
|
||
|
||
data = datasets[datasetIndex].data;
|
||
for (i = 0, ilen = data.length; i < ilen; i++) {
|
||
var values = valuesPerStack[key];
|
||
value = me._parseValue(data[i]);
|
||
// invalid, hidden and negative values are ignored
|
||
if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) {
|
||
continue;
|
||
}
|
||
values[i] = values[i] || 0;
|
||
values[i] += value.max;
|
||
}
|
||
}
|
||
}
|
||
|
||
helpers$1.each(valuesPerStack, function(valuesForType) {
|
||
if (valuesForType.length > 0) {
|
||
var minVal = helpers$1.min(valuesForType);
|
||
var maxVal = helpers$1.max(valuesForType);
|
||
me.min = Math.min(me.min, minVal);
|
||
me.max = Math.max(me.max, maxVal);
|
||
}
|
||
});
|
||
|
||
} else {
|
||
for (datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
|
||
meta = chart.getDatasetMeta(datasetIndex);
|
||
if (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {
|
||
data = datasets[datasetIndex].data;
|
||
for (i = 0, ilen = data.length; i < ilen; i++) {
|
||
value = me._parseValue(data[i]);
|
||
// invalid, hidden and negative values are ignored
|
||
if (isNaN(value.min) || isNaN(value.max) || meta.data[i].hidden || value.min < 0 || value.max < 0) {
|
||
continue;
|
||
}
|
||
|
||
me.min = Math.min(value.min, me.min);
|
||
me.max = Math.max(value.max, me.max);
|
||
|
||
if (value.min !== 0) {
|
||
me.minNotZero = Math.min(value.min, me.minNotZero);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
me.min = helpers$1.isFinite(me.min) ? me.min : null;
|
||
me.max = helpers$1.isFinite(me.max) ? me.max : null;
|
||
me.minNotZero = helpers$1.isFinite(me.minNotZero) ? me.minNotZero : null;
|
||
|
||
// Common base implementation to handle ticks.min, ticks.max
|
||
this.handleTickRangeOptions();
|
||
},
|
||
|
||
handleTickRangeOptions: function() {
|
||
var me = this;
|
||
var tickOpts = me.options.ticks;
|
||
var DEFAULT_MIN = 1;
|
||
var DEFAULT_MAX = 10;
|
||
|
||
me.min = nonNegativeOrDefault(tickOpts.min, me.min);
|
||
me.max = nonNegativeOrDefault(tickOpts.max, me.max);
|
||
|
||
if (me.min === me.max) {
|
||
if (me.min !== 0 && me.min !== null) {
|
||
me.min = Math.pow(10, Math.floor(log10(me.min)) - 1);
|
||
me.max = Math.pow(10, Math.floor(log10(me.max)) + 1);
|
||
} else {
|
||
me.min = DEFAULT_MIN;
|
||
me.max = DEFAULT_MAX;
|
||
}
|
||
}
|
||
if (me.min === null) {
|
||
me.min = Math.pow(10, Math.floor(log10(me.max)) - 1);
|
||
}
|
||
if (me.max === null) {
|
||
me.max = me.min !== 0
|
||
? Math.pow(10, Math.floor(log10(me.min)) + 1)
|
||
: DEFAULT_MAX;
|
||
}
|
||
if (me.minNotZero === null) {
|
||
if (me.min > 0) {
|
||
me.minNotZero = me.min;
|
||
} else if (me.max < 1) {
|
||
me.minNotZero = Math.pow(10, Math.floor(log10(me.max)));
|
||
} else {
|
||
me.minNotZero = DEFAULT_MIN;
|
||
}
|
||
}
|
||
},
|
||
|
||
buildTicks: function() {
|
||
var me = this;
|
||
var tickOpts = me.options.ticks;
|
||
var reverse = !me.isHorizontal();
|
||
|
||
var generationOptions = {
|
||
min: nonNegativeOrDefault(tickOpts.min),
|
||
max: nonNegativeOrDefault(tickOpts.max)
|
||
};
|
||
var ticks = me.ticks = generateTicks$1(generationOptions, me);
|
||
|
||
// At this point, we need to update our max and min given the tick values since we have expanded the
|
||
// range of the scale
|
||
me.max = helpers$1.max(ticks);
|
||
me.min = helpers$1.min(ticks);
|
||
|
||
if (tickOpts.reverse) {
|
||
reverse = !reverse;
|
||
me.start = me.max;
|
||
me.end = me.min;
|
||
} else {
|
||
me.start = me.min;
|
||
me.end = me.max;
|
||
}
|
||
if (reverse) {
|
||
ticks.reverse();
|
||
}
|
||
},
|
||
|
||
convertTicksToLabels: function() {
|
||
this.tickValues = this.ticks.slice();
|
||
|
||
core_scale.prototype.convertTicksToLabels.call(this);
|
||
},
|
||
|
||
// Get the correct tooltip label
|
||
getLabelForIndex: function(index, datasetIndex) {
|
||
return this._getScaleLabel(this.chart.data.datasets[datasetIndex].data[index]);
|
||
},
|
||
|
||
getPixelForTick: function(index) {
|
||
var ticks = this.tickValues;
|
||
if (index < 0 || index > ticks.length - 1) {
|
||
return null;
|
||
}
|
||
return this.getPixelForValue(ticks[index]);
|
||
},
|
||
|
||
/**
|
||
* Returns the value of the first tick.
|
||
* @param {number} value - The minimum not zero value.
|
||
* @return {number} The first tick value.
|
||
* @private
|
||
*/
|
||
_getFirstTickValue: function(value) {
|
||
var exp = Math.floor(log10(value));
|
||
var significand = Math.floor(value / Math.pow(10, exp));
|
||
|
||
return significand * Math.pow(10, exp);
|
||
},
|
||
|
||
_configure: function() {
|
||
var me = this;
|
||
var start = me.min;
|
||
var offset = 0;
|
||
|
||
core_scale.prototype._configure.call(me);
|
||
|
||
if (start === 0) {
|
||
start = me._getFirstTickValue(me.minNotZero);
|
||
offset = valueOrDefault$b(me.options.ticks.fontSize, core_defaults.global.defaultFontSize) / me._length;
|
||
}
|
||
|
||
me._startValue = log10(start);
|
||
me._valueOffset = offset;
|
||
me._valueRange = (log10(me.max) - log10(start)) / (1 - offset);
|
||
},
|
||
|
||
getPixelForValue: function(value) {
|
||
var me = this;
|
||
var decimal = 0;
|
||
|
||
value = +me.getRightValue(value);
|
||
|
||
if (value > me.min && value > 0) {
|
||
decimal = (log10(value) - me._startValue) / me._valueRange + me._valueOffset;
|
||
}
|
||
return me.getPixelForDecimal(decimal);
|
||
},
|
||
|
||
getValueForPixel: function(pixel) {
|
||
var me = this;
|
||
var decimal = me.getDecimalForPixel(pixel);
|
||
return decimal === 0 && me.min === 0
|
||
? 0
|
||
: Math.pow(10, me._startValue + (decimal - me._valueOffset) * me._valueRange);
|
||
}
|
||
});
|
||
|
||
// INTERNAL: static default options, registered in src/index.js
|
||
var _defaults$2 = defaultConfig$2;
|
||
scale_logarithmic._defaults = _defaults$2;
|
||
|
||
var valueOrDefault$c = helpers$1.valueOrDefault;
|
||
var valueAtIndexOrDefault$1 = helpers$1.valueAtIndexOrDefault;
|
||
var resolve$4 = helpers$1.options.resolve;
|
||
|
||
var defaultConfig$3 = {
|
||
display: true,
|
||
|
||
// Boolean - Whether to animate scaling the chart from the centre
|
||
animate: true,
|
||
position: 'chartArea',
|
||
|
||
angleLines: {
|
||
display: true,
|
||
color: 'rgba(0,0,0,0.1)',
|
||
lineWidth: 1,
|
||
borderDash: [],
|
||
borderDashOffset: 0.0
|
||
},
|
||
|
||
gridLines: {
|
||
circular: false
|
||
},
|
||
|
||
// label settings
|
||
ticks: {
|
||
// Boolean - Show a backdrop to the scale label
|
||
showLabelBackdrop: true,
|
||
|
||
// String - The colour of the label backdrop
|
||
backdropColor: 'rgba(255,255,255,0.75)',
|
||
|
||
// Number - The backdrop padding above & below the label in pixels
|
||
backdropPaddingY: 2,
|
||
|
||
// Number - The backdrop padding to the side of the label in pixels
|
||
backdropPaddingX: 2,
|
||
|
||
callback: core_ticks.formatters.linear
|
||
},
|
||
|
||
pointLabels: {
|
||
// Boolean - if true, show point labels
|
||
display: true,
|
||
|
||
// Number - Point label font size in pixels
|
||
fontSize: 10,
|
||
|
||
// Function - Used to convert point labels
|
||
callback: function(label) {
|
||
return label;
|
||
}
|
||
}
|
||
};
|
||
|
||
function getTickBackdropHeight(opts) {
|
||
var tickOpts = opts.ticks;
|
||
|
||
if (tickOpts.display && opts.display) {
|
||
return valueOrDefault$c(tickOpts.fontSize, core_defaults.global.defaultFontSize) + tickOpts.backdropPaddingY * 2;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
function measureLabelSize(ctx, lineHeight, label) {
|
||
if (helpers$1.isArray(label)) {
|
||
return {
|
||
w: helpers$1.longestText(ctx, ctx.font, label),
|
||
h: label.length * lineHeight
|
||
};
|
||
}
|
||
|
||
return {
|
||
w: ctx.measureText(label).width,
|
||
h: lineHeight
|
||
};
|
||
}
|
||
|
||
function determineLimits(angle, pos, size, min, max) {
|
||
if (angle === min || angle === max) {
|
||
return {
|
||
start: pos - (size / 2),
|
||
end: pos + (size / 2)
|
||
};
|
||
} else if (angle < min || angle > max) {
|
||
return {
|
||
start: pos - size,
|
||
end: pos
|
||
};
|
||
}
|
||
|
||
return {
|
||
start: pos,
|
||
end: pos + size
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Helper function to fit a radial linear scale with point labels
|
||
*/
|
||
function fitWithPointLabels(scale) {
|
||
|
||
// Right, this is really confusing and there is a lot of maths going on here
|
||
// The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
|
||
//
|
||
// Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
|
||
//
|
||
// Solution:
|
||
//
|
||
// We assume the radius of the polygon is half the size of the canvas at first
|
||
// at each index we check if the text overlaps.
|
||
//
|
||
// Where it does, we store that angle and that index.
|
||
//
|
||
// After finding the largest index and angle we calculate how much we need to remove
|
||
// from the shape radius to move the point inwards by that x.
|
||
//
|
||
// We average the left and right distances to get the maximum shape radius that can fit in the box
|
||
// along with labels.
|
||
//
|
||
// Once we have that, we can find the centre point for the chart, by taking the x text protrusion
|
||
// on each side, removing that from the size, halving it and adding the left x protrusion width.
|
||
//
|
||
// This will mean we have a shape fitted to the canvas, as large as it can be with the labels
|
||
// and position it in the most space efficient manner
|
||
//
|
||
// https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
|
||
|
||
var plFont = helpers$1.options._parseFont(scale.options.pointLabels);
|
||
|
||
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
|
||
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
|
||
var furthestLimits = {
|
||
l: 0,
|
||
r: scale.width,
|
||
t: 0,
|
||
b: scale.height - scale.paddingTop
|
||
};
|
||
var furthestAngles = {};
|
||
var i, textSize, pointPosition;
|
||
|
||
scale.ctx.font = plFont.string;
|
||
scale._pointLabelSizes = [];
|
||
|
||
var valueCount = scale.chart.data.labels.length;
|
||
for (i = 0; i < valueCount; i++) {
|
||
pointPosition = scale.getPointPosition(i, scale.drawingArea + 5);
|
||
textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]);
|
||
scale._pointLabelSizes[i] = textSize;
|
||
|
||
// Add quarter circle to make degree 0 mean top of circle
|
||
var angleRadians = scale.getIndexAngle(i);
|
||
var angle = helpers$1.toDegrees(angleRadians) % 360;
|
||
var hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);
|
||
var vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);
|
||
|
||
if (hLimits.start < furthestLimits.l) {
|
||
furthestLimits.l = hLimits.start;
|
||
furthestAngles.l = angleRadians;
|
||
}
|
||
|
||
if (hLimits.end > furthestLimits.r) {
|
||
furthestLimits.r = hLimits.end;
|
||
furthestAngles.r = angleRadians;
|
||
}
|
||
|
||
if (vLimits.start < furthestLimits.t) {
|
||
furthestLimits.t = vLimits.start;
|
||
furthestAngles.t = angleRadians;
|
||
}
|
||
|
||
if (vLimits.end > furthestLimits.b) {
|
||
furthestLimits.b = vLimits.end;
|
||
furthestAngles.b = angleRadians;
|
||
}
|
||
}
|
||
|
||
scale.setReductions(scale.drawingArea, furthestLimits, furthestAngles);
|
||
}
|
||
|
||
function getTextAlignForAngle(angle) {
|
||
if (angle === 0 || angle === 180) {
|
||
return 'center';
|
||
} else if (angle < 180) {
|
||
return 'left';
|
||
}
|
||
|
||
return 'right';
|
||
}
|
||
|
||
function fillText(ctx, text, position, lineHeight) {
|
||
var y = position.y + lineHeight / 2;
|
||
var i, ilen;
|
||
|
||
if (helpers$1.isArray(text)) {
|
||
for (i = 0, ilen = text.length; i < ilen; ++i) {
|
||
ctx.fillText(text[i], position.x, y);
|
||
y += lineHeight;
|
||
}
|
||
} else {
|
||
ctx.fillText(text, position.x, y);
|
||
}
|
||
}
|
||
|
||
function adjustPointPositionForLabelHeight(angle, textSize, position) {
|
||
if (angle === 90 || angle === 270) {
|
||
position.y -= (textSize.h / 2);
|
||
} else if (angle > 270 || angle < 90) {
|
||
position.y -= textSize.h;
|
||
}
|
||
}
|
||
|
||
function drawPointLabels(scale) {
|
||
var ctx = scale.ctx;
|
||
var opts = scale.options;
|
||
var pointLabelOpts = opts.pointLabels;
|
||
var tickBackdropHeight = getTickBackdropHeight(opts);
|
||
var outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
|
||
var plFont = helpers$1.options._parseFont(pointLabelOpts);
|
||
|
||
ctx.save();
|
||
|
||
ctx.font = plFont.string;
|
||
ctx.textBaseline = 'middle';
|
||
|
||
for (var i = scale.chart.data.labels.length - 1; i >= 0; i--) {
|
||
// Extra pixels out for some label spacing
|
||
var extra = (i === 0 ? tickBackdropHeight / 2 : 0);
|
||
var pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
|
||
|
||
// Keep this in loop since we may support array properties here
|
||
var pointLabelFontColor = valueAtIndexOrDefault$1(pointLabelOpts.fontColor, i, core_defaults.global.defaultFontColor);
|
||
ctx.fillStyle = pointLabelFontColor;
|
||
|
||
var angleRadians = scale.getIndexAngle(i);
|
||
var angle = helpers$1.toDegrees(angleRadians);
|
||
ctx.textAlign = getTextAlignForAngle(angle);
|
||
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
|
||
fillText(ctx, scale.pointLabels[i], pointLabelPosition, plFont.lineHeight);
|
||
}
|
||
ctx.restore();
|
||
}
|
||
|
||
function drawRadiusLine(scale, gridLineOpts, radius, index) {
|
||
var ctx = scale.ctx;
|
||
var circular = gridLineOpts.circular;
|
||
var valueCount = scale.chart.data.labels.length;
|
||
var lineColor = valueAtIndexOrDefault$1(gridLineOpts.color, index - 1);
|
||
var lineWidth = valueAtIndexOrDefault$1(gridLineOpts.lineWidth, index - 1);
|
||
var pointPosition;
|
||
|
||
if ((!circular && !valueCount) || !lineColor || !lineWidth) {
|
||
return;
|
||
}
|
||
|
||
ctx.save();
|
||
ctx.strokeStyle = lineColor;
|
||
ctx.lineWidth = lineWidth;
|
||
if (ctx.setLineDash) {
|
||
ctx.setLineDash(gridLineOpts.borderDash || []);
|
||
ctx.lineDashOffset = gridLineOpts.borderDashOffset || 0.0;
|
||
}
|
||
|
||
ctx.beginPath();
|
||
if (circular) {
|
||
// Draw circular arcs between the points
|
||
ctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);
|
||
} else {
|
||
// Draw straight lines connecting each index
|
||
pointPosition = scale.getPointPosition(0, radius);
|
||
ctx.moveTo(pointPosition.x, pointPosition.y);
|
||
|
||
for (var i = 1; i < valueCount; i++) {
|
||
pointPosition = scale.getPointPosition(i, radius);
|
||
ctx.lineTo(pointPosition.x, pointPosition.y);
|
||
}
|
||
}
|
||
ctx.closePath();
|
||
ctx.stroke();
|
||
ctx.restore();
|
||
}
|
||
|
||
function numberOrZero(param) {
|
||
return helpers$1.isNumber(param) ? param : 0;
|
||
}
|
||
|
||
var scale_radialLinear = scale_linearbase.extend({
|
||
setDimensions: function() {
|
||
var me = this;
|
||
|
||
// Set the unconstrained dimension before label rotation
|
||
me.width = me.maxWidth;
|
||
me.height = me.maxHeight;
|
||
me.paddingTop = getTickBackdropHeight(me.options) / 2;
|
||
me.xCenter = Math.floor(me.width / 2);
|
||
me.yCenter = Math.floor((me.height - me.paddingTop) / 2);
|
||
me.drawingArea = Math.min(me.height - me.paddingTop, me.width) / 2;
|
||
},
|
||
|
||
determineDataLimits: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var min = Number.POSITIVE_INFINITY;
|
||
var max = Number.NEGATIVE_INFINITY;
|
||
|
||
helpers$1.each(chart.data.datasets, function(dataset, datasetIndex) {
|
||
if (chart.isDatasetVisible(datasetIndex)) {
|
||
var meta = chart.getDatasetMeta(datasetIndex);
|
||
|
||
helpers$1.each(dataset.data, function(rawValue, index) {
|
||
var value = +me.getRightValue(rawValue);
|
||
if (isNaN(value) || meta.data[index].hidden) {
|
||
return;
|
||
}
|
||
|
||
min = Math.min(value, min);
|
||
max = Math.max(value, max);
|
||
});
|
||
}
|
||
});
|
||
|
||
me.min = (min === Number.POSITIVE_INFINITY ? 0 : min);
|
||
me.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);
|
||
|
||
// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero
|
||
me.handleTickRangeOptions();
|
||
},
|
||
|
||
// Returns the maximum number of ticks based on the scale dimension
|
||
_computeTickLimit: function() {
|
||
return Math.ceil(this.drawingArea / getTickBackdropHeight(this.options));
|
||
},
|
||
|
||
convertTicksToLabels: function() {
|
||
var me = this;
|
||
|
||
scale_linearbase.prototype.convertTicksToLabels.call(me);
|
||
|
||
// Point labels
|
||
me.pointLabels = me.chart.data.labels.map(function() {
|
||
var label = helpers$1.callback(me.options.pointLabels.callback, arguments, me);
|
||
return label || label === 0 ? label : '';
|
||
});
|
||
},
|
||
|
||
getLabelForIndex: function(index, datasetIndex) {
|
||
return +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);
|
||
},
|
||
|
||
fit: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
|
||
if (opts.display && opts.pointLabels.display) {
|
||
fitWithPointLabels(me);
|
||
} else {
|
||
me.setCenterPoint(0, 0, 0, 0);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Set radius reductions and determine new radius and center point
|
||
* @private
|
||
*/
|
||
setReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {
|
||
var me = this;
|
||
var radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);
|
||
var radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);
|
||
var radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);
|
||
var radiusReductionBottom = -Math.max(furthestLimits.b - (me.height - me.paddingTop), 0) / Math.cos(furthestAngles.b);
|
||
|
||
radiusReductionLeft = numberOrZero(radiusReductionLeft);
|
||
radiusReductionRight = numberOrZero(radiusReductionRight);
|
||
radiusReductionTop = numberOrZero(radiusReductionTop);
|
||
radiusReductionBottom = numberOrZero(radiusReductionBottom);
|
||
|
||
me.drawingArea = Math.min(
|
||
Math.floor(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),
|
||
Math.floor(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));
|
||
me.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);
|
||
},
|
||
|
||
setCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {
|
||
var me = this;
|
||
var maxRight = me.width - rightMovement - me.drawingArea;
|
||
var maxLeft = leftMovement + me.drawingArea;
|
||
var maxTop = topMovement + me.drawingArea;
|
||
var maxBottom = (me.height - me.paddingTop) - bottomMovement - me.drawingArea;
|
||
|
||
me.xCenter = Math.floor(((maxLeft + maxRight) / 2) + me.left);
|
||
me.yCenter = Math.floor(((maxTop + maxBottom) / 2) + me.top + me.paddingTop);
|
||
},
|
||
|
||
getIndexAngle: function(index) {
|
||
var chart = this.chart;
|
||
var angleMultiplier = 360 / chart.data.labels.length;
|
||
var options = chart.options || {};
|
||
var startAngle = options.startAngle || 0;
|
||
|
||
// Start from the top instead of right, so remove a quarter of the circle
|
||
var angle = (index * angleMultiplier + startAngle) % 360;
|
||
|
||
return (angle < 0 ? angle + 360 : angle) * Math.PI * 2 / 360;
|
||
},
|
||
|
||
getDistanceFromCenterForValue: function(value) {
|
||
var me = this;
|
||
|
||
if (helpers$1.isNullOrUndef(value)) {
|
||
return NaN;
|
||
}
|
||
|
||
// Take into account half font size + the yPadding of the top value
|
||
var scalingFactor = me.drawingArea / (me.max - me.min);
|
||
if (me.options.ticks.reverse) {
|
||
return (me.max - value) * scalingFactor;
|
||
}
|
||
return (value - me.min) * scalingFactor;
|
||
},
|
||
|
||
getPointPosition: function(index, distanceFromCenter) {
|
||
var me = this;
|
||
var thisAngle = me.getIndexAngle(index) - (Math.PI / 2);
|
||
return {
|
||
x: Math.cos(thisAngle) * distanceFromCenter + me.xCenter,
|
||
y: Math.sin(thisAngle) * distanceFromCenter + me.yCenter
|
||
};
|
||
},
|
||
|
||
getPointPositionForValue: function(index, value) {
|
||
return this.getPointPosition(index, this.getDistanceFromCenterForValue(value));
|
||
},
|
||
|
||
getBasePosition: function(index) {
|
||
var me = this;
|
||
var min = me.min;
|
||
var max = me.max;
|
||
|
||
return me.getPointPositionForValue(index || 0,
|
||
me.beginAtZero ? 0 :
|
||
min < 0 && max < 0 ? max :
|
||
min > 0 && max > 0 ? min :
|
||
0);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawGrid: function() {
|
||
var me = this;
|
||
var ctx = me.ctx;
|
||
var opts = me.options;
|
||
var gridLineOpts = opts.gridLines;
|
||
var angleLineOpts = opts.angleLines;
|
||
var lineWidth = valueOrDefault$c(angleLineOpts.lineWidth, gridLineOpts.lineWidth);
|
||
var lineColor = valueOrDefault$c(angleLineOpts.color, gridLineOpts.color);
|
||
var i, offset, position;
|
||
|
||
if (opts.pointLabels.display) {
|
||
drawPointLabels(me);
|
||
}
|
||
|
||
if (gridLineOpts.display) {
|
||
helpers$1.each(me.ticks, function(label, index) {
|
||
if (index !== 0) {
|
||
offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
||
drawRadiusLine(me, gridLineOpts, offset, index);
|
||
}
|
||
});
|
||
}
|
||
|
||
if (angleLineOpts.display && lineWidth && lineColor) {
|
||
ctx.save();
|
||
ctx.lineWidth = lineWidth;
|
||
ctx.strokeStyle = lineColor;
|
||
if (ctx.setLineDash) {
|
||
ctx.setLineDash(resolve$4([angleLineOpts.borderDash, gridLineOpts.borderDash, []]));
|
||
ctx.lineDashOffset = resolve$4([angleLineOpts.borderDashOffset, gridLineOpts.borderDashOffset, 0.0]);
|
||
}
|
||
|
||
for (i = me.chart.data.labels.length - 1; i >= 0; i--) {
|
||
offset = me.getDistanceFromCenterForValue(opts.ticks.reverse ? me.min : me.max);
|
||
position = me.getPointPosition(i, offset);
|
||
ctx.beginPath();
|
||
ctx.moveTo(me.xCenter, me.yCenter);
|
||
ctx.lineTo(position.x, position.y);
|
||
ctx.stroke();
|
||
}
|
||
|
||
ctx.restore();
|
||
}
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawLabels: function() {
|
||
var me = this;
|
||
var ctx = me.ctx;
|
||
var opts = me.options;
|
||
var tickOpts = opts.ticks;
|
||
|
||
if (!tickOpts.display) {
|
||
return;
|
||
}
|
||
|
||
var startAngle = me.getIndexAngle(0);
|
||
var tickFont = helpers$1.options._parseFont(tickOpts);
|
||
var tickFontColor = valueOrDefault$c(tickOpts.fontColor, core_defaults.global.defaultFontColor);
|
||
var offset, width;
|
||
|
||
ctx.save();
|
||
ctx.font = tickFont.string;
|
||
ctx.translate(me.xCenter, me.yCenter);
|
||
ctx.rotate(startAngle);
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
|
||
helpers$1.each(me.ticks, function(label, index) {
|
||
if (index === 0 && !tickOpts.reverse) {
|
||
return;
|
||
}
|
||
|
||
offset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);
|
||
|
||
if (tickOpts.showLabelBackdrop) {
|
||
width = ctx.measureText(label).width;
|
||
ctx.fillStyle = tickOpts.backdropColor;
|
||
|
||
ctx.fillRect(
|
||
-width / 2 - tickOpts.backdropPaddingX,
|
||
-offset - tickFont.size / 2 - tickOpts.backdropPaddingY,
|
||
width + tickOpts.backdropPaddingX * 2,
|
||
tickFont.size + tickOpts.backdropPaddingY * 2
|
||
);
|
||
}
|
||
|
||
ctx.fillStyle = tickFontColor;
|
||
ctx.fillText(label, 0, -offset);
|
||
});
|
||
|
||
ctx.restore();
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_drawTitle: helpers$1.noop
|
||
});
|
||
|
||
// INTERNAL: static default options, registered in src/index.js
|
||
var _defaults$3 = defaultConfig$3;
|
||
scale_radialLinear._defaults = _defaults$3;
|
||
|
||
var deprecated$1 = helpers$1._deprecated;
|
||
var resolve$5 = helpers$1.options.resolve;
|
||
var valueOrDefault$d = helpers$1.valueOrDefault;
|
||
|
||
// Integer constants are from the ES6 spec.
|
||
var MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;
|
||
var MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
|
||
|
||
var INTERVALS = {
|
||
millisecond: {
|
||
common: true,
|
||
size: 1,
|
||
steps: 1000
|
||
},
|
||
second: {
|
||
common: true,
|
||
size: 1000,
|
||
steps: 60
|
||
},
|
||
minute: {
|
||
common: true,
|
||
size: 60000,
|
||
steps: 60
|
||
},
|
||
hour: {
|
||
common: true,
|
||
size: 3600000,
|
||
steps: 24
|
||
},
|
||
day: {
|
||
common: true,
|
||
size: 86400000,
|
||
steps: 30
|
||
},
|
||
week: {
|
||
common: false,
|
||
size: 604800000,
|
||
steps: 4
|
||
},
|
||
month: {
|
||
common: true,
|
||
size: 2.628e9,
|
||
steps: 12
|
||
},
|
||
quarter: {
|
||
common: false,
|
||
size: 7.884e9,
|
||
steps: 4
|
||
},
|
||
year: {
|
||
common: true,
|
||
size: 3.154e10
|
||
}
|
||
};
|
||
|
||
var UNITS = Object.keys(INTERVALS);
|
||
|
||
function sorter(a, b) {
|
||
return a - b;
|
||
}
|
||
|
||
function arrayUnique(items) {
|
||
var hash = {};
|
||
var out = [];
|
||
var i, ilen, item;
|
||
|
||
for (i = 0, ilen = items.length; i < ilen; ++i) {
|
||
item = items[i];
|
||
if (!hash[item]) {
|
||
hash[item] = true;
|
||
out.push(item);
|
||
}
|
||
}
|
||
|
||
return out;
|
||
}
|
||
|
||
function getMin(options) {
|
||
return helpers$1.valueOrDefault(options.time.min, options.ticks.min);
|
||
}
|
||
|
||
function getMax(options) {
|
||
return helpers$1.valueOrDefault(options.time.max, options.ticks.max);
|
||
}
|
||
|
||
/**
|
||
* Returns an array of {time, pos} objects used to interpolate a specific `time` or position
|
||
* (`pos`) on the scale, by searching entries before and after the requested value. `pos` is
|
||
* a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other
|
||
* extremity (left + width or top + height). Note that it would be more optimized to directly
|
||
* store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need
|
||
* to create the lookup table. The table ALWAYS contains at least two items: min and max.
|
||
*
|
||
* @param {number[]} timestamps - timestamps sorted from lowest to highest.
|
||
* @param {string} distribution - If 'linear', timestamps will be spread linearly along the min
|
||
* and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.
|
||
* If 'series', timestamps will be positioned at the same distance from each other. In this
|
||
* case, only timestamps that break the time linearity are registered, meaning that in the
|
||
* best case, all timestamps are linear, the table contains only min and max.
|
||
*/
|
||
function buildLookupTable(timestamps, min, max, distribution) {
|
||
if (distribution === 'linear' || !timestamps.length) {
|
||
return [
|
||
{time: min, pos: 0},
|
||
{time: max, pos: 1}
|
||
];
|
||
}
|
||
|
||
var table = [];
|
||
var items = [min];
|
||
var i, ilen, prev, curr, next;
|
||
|
||
for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
|
||
curr = timestamps[i];
|
||
if (curr > min && curr < max) {
|
||
items.push(curr);
|
||
}
|
||
}
|
||
|
||
items.push(max);
|
||
|
||
for (i = 0, ilen = items.length; i < ilen; ++i) {
|
||
next = items[i + 1];
|
||
prev = items[i - 1];
|
||
curr = items[i];
|
||
|
||
// only add points that breaks the scale linearity
|
||
if (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {
|
||
table.push({time: curr, pos: i / (ilen - 1)});
|
||
}
|
||
}
|
||
|
||
return table;
|
||
}
|
||
|
||
// @see adapted from https://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/
|
||
function lookup(table, key, value) {
|
||
var lo = 0;
|
||
var hi = table.length - 1;
|
||
var mid, i0, i1;
|
||
|
||
while (lo >= 0 && lo <= hi) {
|
||
mid = (lo + hi) >> 1;
|
||
i0 = table[mid - 1] || null;
|
||
i1 = table[mid];
|
||
|
||
if (!i0) {
|
||
// given value is outside table (before first item)
|
||
return {lo: null, hi: i1};
|
||
} else if (i1[key] < value) {
|
||
lo = mid + 1;
|
||
} else if (i0[key] > value) {
|
||
hi = mid - 1;
|
||
} else {
|
||
return {lo: i0, hi: i1};
|
||
}
|
||
}
|
||
|
||
// given value is outside table (after last item)
|
||
return {lo: i1, hi: null};
|
||
}
|
||
|
||
/**
|
||
* Linearly interpolates the given source `value` using the table items `skey` values and
|
||
* returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')
|
||
* returns the position for a timestamp equal to 42. If value is out of bounds, values at
|
||
* index [0, 1] or [n - 1, n] are used for the interpolation.
|
||
*/
|
||
function interpolate$1(table, skey, sval, tkey) {
|
||
var range = lookup(table, skey, sval);
|
||
|
||
// Note: the lookup table ALWAYS contains at least 2 items (min and max)
|
||
var prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;
|
||
var next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;
|
||
|
||
var span = next[skey] - prev[skey];
|
||
var ratio = span ? (sval - prev[skey]) / span : 0;
|
||
var offset = (next[tkey] - prev[tkey]) * ratio;
|
||
|
||
return prev[tkey] + offset;
|
||
}
|
||
|
||
function toTimestamp(scale, input) {
|
||
var adapter = scale._adapter;
|
||
var options = scale.options.time;
|
||
var parser = options.parser;
|
||
var format = parser || options.format;
|
||
var value = input;
|
||
|
||
if (typeof parser === 'function') {
|
||
value = parser(value);
|
||
}
|
||
|
||
// Only parse if its not a timestamp already
|
||
if (!helpers$1.isFinite(value)) {
|
||
value = typeof format === 'string'
|
||
? adapter.parse(value, format)
|
||
: adapter.parse(value);
|
||
}
|
||
|
||
if (value !== null) {
|
||
return +value;
|
||
}
|
||
|
||
// Labels are in an incompatible format and no `parser` has been provided.
|
||
// The user might still use the deprecated `format` option for parsing.
|
||
if (!parser && typeof format === 'function') {
|
||
value = format(input);
|
||
|
||
// `format` could return something else than a timestamp, if so, parse it
|
||
if (!helpers$1.isFinite(value)) {
|
||
value = adapter.parse(value);
|
||
}
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
function parse(scale, input) {
|
||
if (helpers$1.isNullOrUndef(input)) {
|
||
return null;
|
||
}
|
||
|
||
var options = scale.options.time;
|
||
var value = toTimestamp(scale, scale.getRightValue(input));
|
||
if (value === null) {
|
||
return value;
|
||
}
|
||
|
||
if (options.round) {
|
||
value = +scale._adapter.startOf(value, options.round);
|
||
}
|
||
|
||
return value;
|
||
}
|
||
|
||
/**
|
||
* Figures out what unit results in an appropriate number of auto-generated ticks
|
||
*/
|
||
function determineUnitForAutoTicks(minUnit, min, max, capacity) {
|
||
var ilen = UNITS.length;
|
||
var i, interval, factor;
|
||
|
||
for (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {
|
||
interval = INTERVALS[UNITS[i]];
|
||
factor = interval.steps ? interval.steps : MAX_INTEGER;
|
||
|
||
if (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {
|
||
return UNITS[i];
|
||
}
|
||
}
|
||
|
||
return UNITS[ilen - 1];
|
||
}
|
||
|
||
/**
|
||
* Figures out what unit to format a set of ticks with
|
||
*/
|
||
function determineUnitForFormatting(scale, numTicks, minUnit, min, max) {
|
||
var i, unit;
|
||
|
||
for (i = UNITS.length - 1; i >= UNITS.indexOf(minUnit); i--) {
|
||
unit = UNITS[i];
|
||
if (INTERVALS[unit].common && scale._adapter.diff(max, min, unit) >= numTicks - 1) {
|
||
return unit;
|
||
}
|
||
}
|
||
|
||
return UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];
|
||
}
|
||
|
||
function determineMajorUnit(unit) {
|
||
for (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {
|
||
if (INTERVALS[UNITS[i]].common) {
|
||
return UNITS[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Generates a maximum of `capacity` timestamps between min and max, rounded to the
|
||
* `minor` unit using the given scale time `options`.
|
||
* Important: this method can return ticks outside the min and max range, it's the
|
||
* responsibility of the calling code to clamp values if needed.
|
||
*/
|
||
function generate(scale, min, max, capacity) {
|
||
var adapter = scale._adapter;
|
||
var options = scale.options;
|
||
var timeOpts = options.time;
|
||
var minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);
|
||
var stepSize = resolve$5([timeOpts.stepSize, timeOpts.unitStepSize, 1]);
|
||
var weekday = minor === 'week' ? timeOpts.isoWeekday : false;
|
||
var first = min;
|
||
var ticks = [];
|
||
var time;
|
||
|
||
// For 'week' unit, handle the first day of week option
|
||
if (weekday) {
|
||
first = +adapter.startOf(first, 'isoWeek', weekday);
|
||
}
|
||
|
||
// Align first ticks on unit
|
||
first = +adapter.startOf(first, weekday ? 'day' : minor);
|
||
|
||
// Prevent browser from freezing in case user options request millions of milliseconds
|
||
if (adapter.diff(max, min, minor) > 100000 * stepSize) {
|
||
throw min + ' and ' + max + ' are too far apart with stepSize of ' + stepSize + ' ' + minor;
|
||
}
|
||
|
||
for (time = first; time < max; time = +adapter.add(time, stepSize, minor)) {
|
||
ticks.push(time);
|
||
}
|
||
|
||
if (time === max || options.bounds === 'ticks') {
|
||
ticks.push(time);
|
||
}
|
||
|
||
return ticks;
|
||
}
|
||
|
||
/**
|
||
* Returns the start and end offsets from edges in the form of {start, end}
|
||
* where each value is a relative width to the scale and ranges between 0 and 1.
|
||
* They add extra margins on the both sides by scaling down the original scale.
|
||
* Offsets are added when the `offset` option is true.
|
||
*/
|
||
function computeOffsets(table, ticks, min, max, options) {
|
||
var start = 0;
|
||
var end = 0;
|
||
var first, last;
|
||
|
||
if (options.offset && ticks.length) {
|
||
first = interpolate$1(table, 'time', ticks[0], 'pos');
|
||
if (ticks.length === 1) {
|
||
start = 1 - first;
|
||
} else {
|
||
start = (interpolate$1(table, 'time', ticks[1], 'pos') - first) / 2;
|
||
}
|
||
last = interpolate$1(table, 'time', ticks[ticks.length - 1], 'pos');
|
||
if (ticks.length === 1) {
|
||
end = last;
|
||
} else {
|
||
end = (last - interpolate$1(table, 'time', ticks[ticks.length - 2], 'pos')) / 2;
|
||
}
|
||
}
|
||
|
||
return {start: start, end: end, factor: 1 / (start + 1 + end)};
|
||
}
|
||
|
||
function setMajorTicks(scale, ticks, map, majorUnit) {
|
||
var adapter = scale._adapter;
|
||
var first = +adapter.startOf(ticks[0].value, majorUnit);
|
||
var last = ticks[ticks.length - 1].value;
|
||
var major, index;
|
||
|
||
for (major = first; major <= last; major = +adapter.add(major, 1, majorUnit)) {
|
||
index = map[major];
|
||
if (index >= 0) {
|
||
ticks[index].major = true;
|
||
}
|
||
}
|
||
return ticks;
|
||
}
|
||
|
||
function ticksFromTimestamps(scale, values, majorUnit) {
|
||
var ticks = [];
|
||
var map = {};
|
||
var ilen = values.length;
|
||
var i, value;
|
||
|
||
for (i = 0; i < ilen; ++i) {
|
||
value = values[i];
|
||
map[value] = i;
|
||
|
||
ticks.push({
|
||
value: value,
|
||
major: false
|
||
});
|
||
}
|
||
|
||
// We set the major ticks separately from the above loop because calling startOf for every tick
|
||
// is expensive when there is a large number of ticks
|
||
return (ilen === 0 || !majorUnit) ? ticks : setMajorTicks(scale, ticks, map, majorUnit);
|
||
}
|
||
|
||
var defaultConfig$4 = {
|
||
position: 'bottom',
|
||
|
||
/**
|
||
* Data distribution along the scale:
|
||
* - 'linear': data are spread according to their time (distances can vary),
|
||
* - 'series': data are spread at the same distance from each other.
|
||
* @see https://github.com/chartjs/Chart.js/pull/4507
|
||
* @since 2.7.0
|
||
*/
|
||
distribution: 'linear',
|
||
|
||
/**
|
||
* Scale boundary strategy (bypassed by min/max time options)
|
||
* - `data`: make sure data are fully visible, ticks outside are removed
|
||
* - `ticks`: make sure ticks are fully visible, data outside are truncated
|
||
* @see https://github.com/chartjs/Chart.js/pull/4556
|
||
* @since 2.7.0
|
||
*/
|
||
bounds: 'data',
|
||
|
||
adapters: {},
|
||
time: {
|
||
parser: false, // false == a pattern string from https://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment
|
||
unit: false, // false == automatic or override with week, month, year, etc.
|
||
round: false, // none, or override with week, month, year, etc.
|
||
displayFormat: false, // DEPRECATED
|
||
isoWeekday: false, // override week start day - see https://momentjs.com/docs/#/get-set/iso-weekday/
|
||
minUnit: 'millisecond',
|
||
displayFormats: {}
|
||
},
|
||
ticks: {
|
||
autoSkip: false,
|
||
|
||
/**
|
||
* Ticks generation input values:
|
||
* - 'auto': generates "optimal" ticks based on scale size and time options.
|
||
* - 'data': generates ticks from data (including labels from data {t|x|y} objects).
|
||
* - 'labels': generates ticks from user given `data.labels` values ONLY.
|
||
* @see https://github.com/chartjs/Chart.js/pull/4507
|
||
* @since 2.7.0
|
||
*/
|
||
source: 'auto',
|
||
|
||
major: {
|
||
enabled: false
|
||
}
|
||
}
|
||
};
|
||
|
||
var scale_time = core_scale.extend({
|
||
initialize: function() {
|
||
this.mergeTicksOptions();
|
||
core_scale.prototype.initialize.call(this);
|
||
},
|
||
|
||
update: function() {
|
||
var me = this;
|
||
var options = me.options;
|
||
var time = options.time || (options.time = {});
|
||
var adapter = me._adapter = new core_adapters._date(options.adapters.date);
|
||
|
||
// DEPRECATIONS: output a message only one time per update
|
||
deprecated$1('time scale', time.format, 'time.format', 'time.parser');
|
||
deprecated$1('time scale', time.min, 'time.min', 'ticks.min');
|
||
deprecated$1('time scale', time.max, 'time.max', 'ticks.max');
|
||
|
||
// Backward compatibility: before introducing adapter, `displayFormats` was
|
||
// supposed to contain *all* unit/string pairs but this can't be resolved
|
||
// when loading the scale (adapters are loaded afterward), so let's populate
|
||
// missing formats on update
|
||
helpers$1.mergeIf(time.displayFormats, adapter.formats());
|
||
|
||
return core_scale.prototype.update.apply(me, arguments);
|
||
},
|
||
|
||
/**
|
||
* Allows data to be referenced via 't' attribute
|
||
*/
|
||
getRightValue: function(rawValue) {
|
||
if (rawValue && rawValue.t !== undefined) {
|
||
rawValue = rawValue.t;
|
||
}
|
||
return core_scale.prototype.getRightValue.call(this, rawValue);
|
||
},
|
||
|
||
determineDataLimits: function() {
|
||
var me = this;
|
||
var chart = me.chart;
|
||
var adapter = me._adapter;
|
||
var options = me.options;
|
||
var unit = options.time.unit || 'day';
|
||
var min = MAX_INTEGER;
|
||
var max = MIN_INTEGER;
|
||
var timestamps = [];
|
||
var datasets = [];
|
||
var labels = [];
|
||
var i, j, ilen, jlen, data, timestamp, labelsAdded;
|
||
var dataLabels = me._getLabels();
|
||
|
||
for (i = 0, ilen = dataLabels.length; i < ilen; ++i) {
|
||
labels.push(parse(me, dataLabels[i]));
|
||
}
|
||
|
||
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
||
if (chart.isDatasetVisible(i)) {
|
||
data = chart.data.datasets[i].data;
|
||
|
||
// Let's consider that all data have the same format.
|
||
if (helpers$1.isObject(data[0])) {
|
||
datasets[i] = [];
|
||
|
||
for (j = 0, jlen = data.length; j < jlen; ++j) {
|
||
timestamp = parse(me, data[j]);
|
||
timestamps.push(timestamp);
|
||
datasets[i][j] = timestamp;
|
||
}
|
||
} else {
|
||
datasets[i] = labels.slice(0);
|
||
if (!labelsAdded) {
|
||
timestamps = timestamps.concat(labels);
|
||
labelsAdded = true;
|
||
}
|
||
}
|
||
} else {
|
||
datasets[i] = [];
|
||
}
|
||
}
|
||
|
||
if (labels.length) {
|
||
min = Math.min(min, labels[0]);
|
||
max = Math.max(max, labels[labels.length - 1]);
|
||
}
|
||
|
||
if (timestamps.length) {
|
||
timestamps = ilen > 1 ? arrayUnique(timestamps).sort(sorter) : timestamps.sort(sorter);
|
||
min = Math.min(min, timestamps[0]);
|
||
max = Math.max(max, timestamps[timestamps.length - 1]);
|
||
}
|
||
|
||
min = parse(me, getMin(options)) || min;
|
||
max = parse(me, getMax(options)) || max;
|
||
|
||
// In case there is no valid min/max, set limits based on unit time option
|
||
min = min === MAX_INTEGER ? +adapter.startOf(Date.now(), unit) : min;
|
||
max = max === MIN_INTEGER ? +adapter.endOf(Date.now(), unit) + 1 : max;
|
||
|
||
// Make sure that max is strictly higher than min (required by the lookup table)
|
||
me.min = Math.min(min, max);
|
||
me.max = Math.max(min + 1, max);
|
||
|
||
// PRIVATE
|
||
me._table = [];
|
||
me._timestamps = {
|
||
data: timestamps,
|
||
datasets: datasets,
|
||
labels: labels
|
||
};
|
||
},
|
||
|
||
buildTicks: function() {
|
||
var me = this;
|
||
var min = me.min;
|
||
var max = me.max;
|
||
var options = me.options;
|
||
var tickOpts = options.ticks;
|
||
var timeOpts = options.time;
|
||
var timestamps = me._timestamps;
|
||
var ticks = [];
|
||
var capacity = me.getLabelCapacity(min);
|
||
var source = tickOpts.source;
|
||
var distribution = options.distribution;
|
||
var i, ilen, timestamp;
|
||
|
||
if (source === 'data' || (source === 'auto' && distribution === 'series')) {
|
||
timestamps = timestamps.data;
|
||
} else if (source === 'labels') {
|
||
timestamps = timestamps.labels;
|
||
} else {
|
||
timestamps = generate(me, min, max, capacity);
|
||
}
|
||
|
||
if (options.bounds === 'ticks' && timestamps.length) {
|
||
min = timestamps[0];
|
||
max = timestamps[timestamps.length - 1];
|
||
}
|
||
|
||
// Enforce limits with user min/max options
|
||
min = parse(me, getMin(options)) || min;
|
||
max = parse(me, getMax(options)) || max;
|
||
|
||
// Remove ticks outside the min/max range
|
||
for (i = 0, ilen = timestamps.length; i < ilen; ++i) {
|
||
timestamp = timestamps[i];
|
||
if (timestamp >= min && timestamp <= max) {
|
||
ticks.push(timestamp);
|
||
}
|
||
}
|
||
|
||
me.min = min;
|
||
me.max = max;
|
||
|
||
// PRIVATE
|
||
// determineUnitForFormatting relies on the number of ticks so we don't use it when
|
||
// autoSkip is enabled because we don't yet know what the final number of ticks will be
|
||
me._unit = timeOpts.unit || (tickOpts.autoSkip
|
||
? determineUnitForAutoTicks(timeOpts.minUnit, me.min, me.max, capacity)
|
||
: determineUnitForFormatting(me, ticks.length, timeOpts.minUnit, me.min, me.max));
|
||
me._majorUnit = !tickOpts.major.enabled || me._unit === 'year' ? undefined
|
||
: determineMajorUnit(me._unit);
|
||
me._table = buildLookupTable(me._timestamps.data, min, max, distribution);
|
||
me._offsets = computeOffsets(me._table, ticks, min, max, options);
|
||
|
||
if (tickOpts.reverse) {
|
||
ticks.reverse();
|
||
}
|
||
|
||
return ticksFromTimestamps(me, ticks, me._majorUnit);
|
||
},
|
||
|
||
getLabelForIndex: function(index, datasetIndex) {
|
||
var me = this;
|
||
var adapter = me._adapter;
|
||
var data = me.chart.data;
|
||
var timeOpts = me.options.time;
|
||
var label = data.labels && index < data.labels.length ? data.labels[index] : '';
|
||
var value = data.datasets[datasetIndex].data[index];
|
||
|
||
if (helpers$1.isObject(value)) {
|
||
label = me.getRightValue(value);
|
||
}
|
||
if (timeOpts.tooltipFormat) {
|
||
return adapter.format(toTimestamp(me, label), timeOpts.tooltipFormat);
|
||
}
|
||
if (typeof label === 'string') {
|
||
return label;
|
||
}
|
||
return adapter.format(toTimestamp(me, label), timeOpts.displayFormats.datetime);
|
||
},
|
||
|
||
/**
|
||
* Function to format an individual tick mark
|
||
* @private
|
||
*/
|
||
tickFormatFunction: function(time, index, ticks, format) {
|
||
var me = this;
|
||
var adapter = me._adapter;
|
||
var options = me.options;
|
||
var formats = options.time.displayFormats;
|
||
var minorFormat = formats[me._unit];
|
||
var majorUnit = me._majorUnit;
|
||
var majorFormat = formats[majorUnit];
|
||
var tick = ticks[index];
|
||
var tickOpts = options.ticks;
|
||
var major = majorUnit && majorFormat && tick && tick.major;
|
||
var label = adapter.format(time, format ? format : major ? majorFormat : minorFormat);
|
||
var nestedTickOpts = major ? tickOpts.major : tickOpts.minor;
|
||
var formatter = resolve$5([
|
||
nestedTickOpts.callback,
|
||
nestedTickOpts.userCallback,
|
||
tickOpts.callback,
|
||
tickOpts.userCallback
|
||
]);
|
||
|
||
return formatter ? formatter(label, index, ticks) : label;
|
||
},
|
||
|
||
convertTicksToLabels: function(ticks) {
|
||
var labels = [];
|
||
var i, ilen;
|
||
|
||
for (i = 0, ilen = ticks.length; i < ilen; ++i) {
|
||
labels.push(this.tickFormatFunction(ticks[i].value, i, ticks));
|
||
}
|
||
|
||
return labels;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
getPixelForOffset: function(time) {
|
||
var me = this;
|
||
var offsets = me._offsets;
|
||
var pos = interpolate$1(me._table, 'time', time, 'pos');
|
||
return me.getPixelForDecimal((offsets.start + pos) * offsets.factor);
|
||
},
|
||
|
||
getPixelForValue: function(value, index, datasetIndex) {
|
||
var me = this;
|
||
var time = null;
|
||
|
||
if (index !== undefined && datasetIndex !== undefined) {
|
||
time = me._timestamps.datasets[datasetIndex][index];
|
||
}
|
||
|
||
if (time === null) {
|
||
time = parse(me, value);
|
||
}
|
||
|
||
if (time !== null) {
|
||
return me.getPixelForOffset(time);
|
||
}
|
||
},
|
||
|
||
getPixelForTick: function(index) {
|
||
var ticks = this.getTicks();
|
||
return index >= 0 && index < ticks.length ?
|
||
this.getPixelForOffset(ticks[index].value) :
|
||
null;
|
||
},
|
||
|
||
getValueForPixel: function(pixel) {
|
||
var me = this;
|
||
var offsets = me._offsets;
|
||
var pos = me.getDecimalForPixel(pixel) / offsets.factor - offsets.end;
|
||
var time = interpolate$1(me._table, 'pos', pos, 'time');
|
||
|
||
// DEPRECATION, we should return time directly
|
||
return me._adapter._create(time);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getLabelSize: function(label) {
|
||
var me = this;
|
||
var ticksOpts = me.options.ticks;
|
||
var tickLabelWidth = me.ctx.measureText(label).width;
|
||
var angle = helpers$1.toRadians(me.isHorizontal() ? ticksOpts.maxRotation : ticksOpts.minRotation);
|
||
var cosRotation = Math.cos(angle);
|
||
var sinRotation = Math.sin(angle);
|
||
var tickFontSize = valueOrDefault$d(ticksOpts.fontSize, core_defaults.global.defaultFontSize);
|
||
|
||
return {
|
||
w: (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation),
|
||
h: (tickLabelWidth * sinRotation) + (tickFontSize * cosRotation)
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Crude approximation of what the label width might be
|
||
* @private
|
||
*/
|
||
getLabelWidth: function(label) {
|
||
return this._getLabelSize(label).w;
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
getLabelCapacity: function(exampleTime) {
|
||
var me = this;
|
||
var timeOpts = me.options.time;
|
||
var displayFormats = timeOpts.displayFormats;
|
||
|
||
// pick the longest format (milliseconds) for guestimation
|
||
var format = displayFormats[timeOpts.unit] || displayFormats.millisecond;
|
||
var exampleLabel = me.tickFormatFunction(exampleTime, 0, ticksFromTimestamps(me, [exampleTime], me._majorUnit), format);
|
||
var size = me._getLabelSize(exampleLabel);
|
||
var capacity = Math.floor(me.isHorizontal() ? me.width / size.w : me.height / size.h);
|
||
|
||
if (me.options.offset) {
|
||
capacity--;
|
||
}
|
||
|
||
return capacity > 0 ? capacity : 1;
|
||
}
|
||
});
|
||
|
||
// INTERNAL: static default options, registered in src/index.js
|
||
var _defaults$4 = defaultConfig$4;
|
||
scale_time._defaults = _defaults$4;
|
||
|
||
var scales = {
|
||
category: scale_category,
|
||
linear: scale_linear,
|
||
logarithmic: scale_logarithmic,
|
||
radialLinear: scale_radialLinear,
|
||
time: scale_time
|
||
};
|
||
|
||
var FORMATS = {
|
||
datetime: 'MMM D, YYYY, h:mm:ss a',
|
||
millisecond: 'h:mm:ss.SSS a',
|
||
second: 'h:mm:ss a',
|
||
minute: 'h:mm a',
|
||
hour: 'hA',
|
||
day: 'MMM D',
|
||
week: 'll',
|
||
month: 'MMM YYYY',
|
||
quarter: '[Q]Q - YYYY',
|
||
year: 'YYYY'
|
||
};
|
||
|
||
core_adapters._date.override(typeof moment === 'function' ? {
|
||
_id: 'moment', // DEBUG ONLY
|
||
|
||
formats: function() {
|
||
return FORMATS;
|
||
},
|
||
|
||
parse: function(value, format) {
|
||
if (typeof value === 'string' && typeof format === 'string') {
|
||
value = moment(value, format);
|
||
} else if (!(value instanceof moment)) {
|
||
value = moment(value);
|
||
}
|
||
return value.isValid() ? value.valueOf() : null;
|
||
},
|
||
|
||
format: function(time, format) {
|
||
return moment(time).format(format);
|
||
},
|
||
|
||
add: function(time, amount, unit) {
|
||
return moment(time).add(amount, unit).valueOf();
|
||
},
|
||
|
||
diff: function(max, min, unit) {
|
||
return moment(max).diff(moment(min), unit);
|
||
},
|
||
|
||
startOf: function(time, unit, weekday) {
|
||
time = moment(time);
|
||
if (unit === 'isoWeek') {
|
||
return time.isoWeekday(weekday).valueOf();
|
||
}
|
||
return time.startOf(unit).valueOf();
|
||
},
|
||
|
||
endOf: function(time, unit) {
|
||
return moment(time).endOf(unit).valueOf();
|
||
},
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility with scale.getValueForPixel().
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
_create: function(time) {
|
||
return moment(time);
|
||
},
|
||
} : {});
|
||
|
||
core_defaults._set('global', {
|
||
plugins: {
|
||
filler: {
|
||
propagate: true
|
||
}
|
||
}
|
||
});
|
||
|
||
var mappers = {
|
||
dataset: function(source) {
|
||
var index = source.fill;
|
||
var chart = source.chart;
|
||
var meta = chart.getDatasetMeta(index);
|
||
var visible = meta && chart.isDatasetVisible(index);
|
||
var points = (visible && meta.dataset._children) || [];
|
||
var length = points.length || 0;
|
||
|
||
return !length ? null : function(point, i) {
|
||
return (i < length && points[i]._view) || null;
|
||
};
|
||
},
|
||
|
||
boundary: function(source) {
|
||
var boundary = source.boundary;
|
||
var x = boundary ? boundary.x : null;
|
||
var y = boundary ? boundary.y : null;
|
||
|
||
if (helpers$1.isArray(boundary)) {
|
||
return function(point, i) {
|
||
return boundary[i];
|
||
};
|
||
}
|
||
|
||
return function(point) {
|
||
return {
|
||
x: x === null ? point.x : x,
|
||
y: y === null ? point.y : y,
|
||
};
|
||
};
|
||
}
|
||
};
|
||
|
||
// @todo if (fill[0] === '#')
|
||
function decodeFill(el, index, count) {
|
||
var model = el._model || {};
|
||
var fill = model.fill;
|
||
var target;
|
||
|
||
if (fill === undefined) {
|
||
fill = !!model.backgroundColor;
|
||
}
|
||
|
||
if (fill === false || fill === null) {
|
||
return false;
|
||
}
|
||
|
||
if (fill === true) {
|
||
return 'origin';
|
||
}
|
||
|
||
target = parseFloat(fill, 10);
|
||
if (isFinite(target) && Math.floor(target) === target) {
|
||
if (fill[0] === '-' || fill[0] === '+') {
|
||
target = index + target;
|
||
}
|
||
|
||
if (target === index || target < 0 || target >= count) {
|
||
return false;
|
||
}
|
||
|
||
return target;
|
||
}
|
||
|
||
switch (fill) {
|
||
// compatibility
|
||
case 'bottom':
|
||
return 'start';
|
||
case 'top':
|
||
return 'end';
|
||
case 'zero':
|
||
return 'origin';
|
||
// supported boundaries
|
||
case 'origin':
|
||
case 'start':
|
||
case 'end':
|
||
return fill;
|
||
// invalid fill values
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function computeLinearBoundary(source) {
|
||
var model = source.el._model || {};
|
||
var scale = source.el._scale || {};
|
||
var fill = source.fill;
|
||
var target = null;
|
||
var horizontal;
|
||
|
||
if (isFinite(fill)) {
|
||
return null;
|
||
}
|
||
|
||
// Backward compatibility: until v3, we still need to support boundary values set on
|
||
// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and
|
||
// controllers might still use it (e.g. the Smith chart).
|
||
|
||
if (fill === 'start') {
|
||
target = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;
|
||
} else if (fill === 'end') {
|
||
target = model.scaleTop === undefined ? scale.top : model.scaleTop;
|
||
} else if (model.scaleZero !== undefined) {
|
||
target = model.scaleZero;
|
||
} else if (scale.getBasePixel) {
|
||
target = scale.getBasePixel();
|
||
}
|
||
|
||
if (target !== undefined && target !== null) {
|
||
if (target.x !== undefined && target.y !== undefined) {
|
||
return target;
|
||
}
|
||
|
||
if (helpers$1.isFinite(target)) {
|
||
horizontal = scale.isHorizontal();
|
||
return {
|
||
x: horizontal ? target : null,
|
||
y: horizontal ? null : target
|
||
};
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function computeCircularBoundary(source) {
|
||
var scale = source.el._scale;
|
||
var options = scale.options;
|
||
var length = scale.chart.data.labels.length;
|
||
var fill = source.fill;
|
||
var target = [];
|
||
var start, end, center, i, point;
|
||
|
||
if (!length) {
|
||
return null;
|
||
}
|
||
|
||
start = options.ticks.reverse ? scale.max : scale.min;
|
||
end = options.ticks.reverse ? scale.min : scale.max;
|
||
center = scale.getPointPositionForValue(0, start);
|
||
for (i = 0; i < length; ++i) {
|
||
point = fill === 'start' || fill === 'end'
|
||
? scale.getPointPositionForValue(i, fill === 'start' ? start : end)
|
||
: scale.getBasePosition(i);
|
||
if (options.gridLines.circular) {
|
||
point.cx = center.x;
|
||
point.cy = center.y;
|
||
point.angle = scale.getIndexAngle(i) - Math.PI / 2;
|
||
}
|
||
target.push(point);
|
||
}
|
||
return target;
|
||
}
|
||
|
||
function computeBoundary(source) {
|
||
var scale = source.el._scale || {};
|
||
|
||
if (scale.getPointPositionForValue) {
|
||
return computeCircularBoundary(source);
|
||
}
|
||
return computeLinearBoundary(source);
|
||
}
|
||
|
||
function resolveTarget(sources, index, propagate) {
|
||
var source = sources[index];
|
||
var fill = source.fill;
|
||
var visited = [index];
|
||
var target;
|
||
|
||
if (!propagate) {
|
||
return fill;
|
||
}
|
||
|
||
while (fill !== false && visited.indexOf(fill) === -1) {
|
||
if (!isFinite(fill)) {
|
||
return fill;
|
||
}
|
||
|
||
target = sources[fill];
|
||
if (!target) {
|
||
return false;
|
||
}
|
||
|
||
if (target.visible) {
|
||
return fill;
|
||
}
|
||
|
||
visited.push(fill);
|
||
fill = target.fill;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
function createMapper(source) {
|
||
var fill = source.fill;
|
||
var type = 'dataset';
|
||
|
||
if (fill === false) {
|
||
return null;
|
||
}
|
||
|
||
if (!isFinite(fill)) {
|
||
type = 'boundary';
|
||
}
|
||
|
||
return mappers[type](source);
|
||
}
|
||
|
||
function isDrawable(point) {
|
||
return point && !point.skip;
|
||
}
|
||
|
||
function drawArea(ctx, curve0, curve1, len0, len1) {
|
||
var i, cx, cy, r;
|
||
|
||
if (!len0 || !len1) {
|
||
return;
|
||
}
|
||
|
||
// building first area curve (normal)
|
||
ctx.moveTo(curve0[0].x, curve0[0].y);
|
||
for (i = 1; i < len0; ++i) {
|
||
helpers$1.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);
|
||
}
|
||
|
||
if (curve1[0].angle !== undefined) {
|
||
cx = curve1[0].cx;
|
||
cy = curve1[0].cy;
|
||
r = Math.sqrt(Math.pow(curve1[0].x - cx, 2) + Math.pow(curve1[0].y - cy, 2));
|
||
for (i = len1 - 1; i > 0; --i) {
|
||
ctx.arc(cx, cy, r, curve1[i].angle, curve1[i - 1].angle, true);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// joining the two area curves
|
||
ctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);
|
||
|
||
// building opposite area curve (reverse)
|
||
for (i = len1 - 1; i > 0; --i) {
|
||
helpers$1.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);
|
||
}
|
||
}
|
||
|
||
function doFill(ctx, points, mapper, view, color, loop) {
|
||
var count = points.length;
|
||
var span = view.spanGaps;
|
||
var curve0 = [];
|
||
var curve1 = [];
|
||
var len0 = 0;
|
||
var len1 = 0;
|
||
var i, ilen, index, p0, p1, d0, d1, loopOffset;
|
||
|
||
ctx.beginPath();
|
||
|
||
for (i = 0, ilen = count; i < ilen; ++i) {
|
||
index = i % count;
|
||
p0 = points[index]._view;
|
||
p1 = mapper(p0, index, view);
|
||
d0 = isDrawable(p0);
|
||
d1 = isDrawable(p1);
|
||
|
||
if (loop && loopOffset === undefined && d0) {
|
||
loopOffset = i + 1;
|
||
ilen = count + loopOffset;
|
||
}
|
||
|
||
if (d0 && d1) {
|
||
len0 = curve0.push(p0);
|
||
len1 = curve1.push(p1);
|
||
} else if (len0 && len1) {
|
||
if (!span) {
|
||
drawArea(ctx, curve0, curve1, len0, len1);
|
||
len0 = len1 = 0;
|
||
curve0 = [];
|
||
curve1 = [];
|
||
} else {
|
||
if (d0) {
|
||
curve0.push(p0);
|
||
}
|
||
if (d1) {
|
||
curve1.push(p1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
drawArea(ctx, curve0, curve1, len0, len1);
|
||
|
||
ctx.closePath();
|
||
ctx.fillStyle = color;
|
||
ctx.fill();
|
||
}
|
||
|
||
var plugin_filler = {
|
||
id: 'filler',
|
||
|
||
afterDatasetsUpdate: function(chart, options) {
|
||
var count = (chart.data.datasets || []).length;
|
||
var propagate = options.propagate;
|
||
var sources = [];
|
||
var meta, i, el, source;
|
||
|
||
for (i = 0; i < count; ++i) {
|
||
meta = chart.getDatasetMeta(i);
|
||
el = meta.dataset;
|
||
source = null;
|
||
|
||
if (el && el._model && el instanceof elements.Line) {
|
||
source = {
|
||
visible: chart.isDatasetVisible(i),
|
||
fill: decodeFill(el, i, count),
|
||
chart: chart,
|
||
el: el
|
||
};
|
||
}
|
||
|
||
meta.$filler = source;
|
||
sources.push(source);
|
||
}
|
||
|
||
for (i = 0; i < count; ++i) {
|
||
source = sources[i];
|
||
if (!source) {
|
||
continue;
|
||
}
|
||
|
||
source.fill = resolveTarget(sources, i, propagate);
|
||
source.boundary = computeBoundary(source);
|
||
source.mapper = createMapper(source);
|
||
}
|
||
},
|
||
|
||
beforeDatasetsDraw: function(chart) {
|
||
var metasets = chart._getSortedVisibleDatasetMetas();
|
||
var ctx = chart.ctx;
|
||
var meta, i, el, view, points, mapper, color;
|
||
|
||
for (i = metasets.length - 1; i >= 0; --i) {
|
||
meta = metasets[i].$filler;
|
||
|
||
if (!meta || !meta.visible) {
|
||
continue;
|
||
}
|
||
|
||
el = meta.el;
|
||
view = el._view;
|
||
points = el._children || [];
|
||
mapper = meta.mapper;
|
||
color = view.backgroundColor || core_defaults.global.defaultColor;
|
||
|
||
if (mapper && color && points.length) {
|
||
helpers$1.canvas.clipArea(ctx, chart.chartArea);
|
||
doFill(ctx, points, mapper, view, color, el._loop);
|
||
helpers$1.canvas.unclipArea(ctx);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
var getRtlHelper$1 = helpers$1.rtl.getRtlAdapter;
|
||
var noop$1 = helpers$1.noop;
|
||
var valueOrDefault$e = helpers$1.valueOrDefault;
|
||
|
||
core_defaults._set('global', {
|
||
legend: {
|
||
display: true,
|
||
position: 'top',
|
||
align: 'center',
|
||
fullWidth: true,
|
||
reverse: false,
|
||
weight: 1000,
|
||
|
||
// a callback that will handle
|
||
onClick: function(e, legendItem) {
|
||
var index = legendItem.datasetIndex;
|
||
var ci = this.chart;
|
||
var meta = ci.getDatasetMeta(index);
|
||
|
||
// See controller.isDatasetVisible comment
|
||
meta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;
|
||
|
||
// We hid a dataset ... rerender the chart
|
||
ci.update();
|
||
},
|
||
|
||
onHover: null,
|
||
onLeave: null,
|
||
|
||
labels: {
|
||
boxWidth: 40,
|
||
padding: 10,
|
||
// Generates labels shown in the legend
|
||
// Valid properties to return:
|
||
// text : text to display
|
||
// fillStyle : fill of coloured box
|
||
// strokeStyle: stroke of coloured box
|
||
// hidden : if this legend item refers to a hidden item
|
||
// lineCap : cap style for line
|
||
// lineDash
|
||
// lineDashOffset :
|
||
// lineJoin :
|
||
// lineWidth :
|
||
generateLabels: function(chart) {
|
||
var datasets = chart.data.datasets;
|
||
var options = chart.options.legend || {};
|
||
var usePointStyle = options.labels && options.labels.usePointStyle;
|
||
|
||
return chart._getSortedDatasetMetas().map(function(meta) {
|
||
var style = meta.controller.getStyle(usePointStyle ? 0 : undefined);
|
||
|
||
return {
|
||
text: datasets[meta.index].label,
|
||
fillStyle: style.backgroundColor,
|
||
hidden: !chart.isDatasetVisible(meta.index),
|
||
lineCap: style.borderCapStyle,
|
||
lineDash: style.borderDash,
|
||
lineDashOffset: style.borderDashOffset,
|
||
lineJoin: style.borderJoinStyle,
|
||
lineWidth: style.borderWidth,
|
||
strokeStyle: style.borderColor,
|
||
pointStyle: style.pointStyle,
|
||
rotation: style.rotation,
|
||
|
||
// Below is extra data used for toggling the datasets
|
||
datasetIndex: meta.index
|
||
};
|
||
}, this);
|
||
}
|
||
}
|
||
},
|
||
|
||
legendCallback: function(chart) {
|
||
var list = document.createElement('ul');
|
||
var datasets = chart.data.datasets;
|
||
var i, ilen, listItem, listItemSpan;
|
||
|
||
list.setAttribute('class', chart.id + '-legend');
|
||
|
||
for (i = 0, ilen = datasets.length; i < ilen; i++) {
|
||
listItem = list.appendChild(document.createElement('li'));
|
||
listItemSpan = listItem.appendChild(document.createElement('span'));
|
||
listItemSpan.style.backgroundColor = datasets[i].backgroundColor;
|
||
if (datasets[i].label) {
|
||
listItem.appendChild(document.createTextNode(datasets[i].label));
|
||
}
|
||
}
|
||
|
||
return list.outerHTML;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* Helper function to get the box width based on the usePointStyle option
|
||
* @param {object} labelopts - the label options on the legend
|
||
* @param {number} fontSize - the label font size
|
||
* @return {number} width of the color box area
|
||
*/
|
||
function getBoxWidth(labelOpts, fontSize) {
|
||
return labelOpts.usePointStyle && labelOpts.boxWidth > fontSize ?
|
||
fontSize :
|
||
labelOpts.boxWidth;
|
||
}
|
||
|
||
/**
|
||
* IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
|
||
*/
|
||
var Legend = core_element.extend({
|
||
|
||
initialize: function(config) {
|
||
var me = this;
|
||
helpers$1.extend(me, config);
|
||
|
||
// Contains hit boxes for each dataset (in dataset order)
|
||
me.legendHitBoxes = [];
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
me._hoveredItem = null;
|
||
|
||
// Are we in doughnut mode which has a different data type
|
||
me.doughnutMode = false;
|
||
},
|
||
|
||
// These methods are ordered by lifecycle. Utilities then follow.
|
||
// Any function defined here is inherited by all legend types.
|
||
// Any function can be extended by the legend type
|
||
|
||
beforeUpdate: noop$1,
|
||
update: function(maxWidth, maxHeight, margins) {
|
||
var me = this;
|
||
|
||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||
me.beforeUpdate();
|
||
|
||
// Absorb the master measurements
|
||
me.maxWidth = maxWidth;
|
||
me.maxHeight = maxHeight;
|
||
me.margins = margins;
|
||
|
||
// Dimensions
|
||
me.beforeSetDimensions();
|
||
me.setDimensions();
|
||
me.afterSetDimensions();
|
||
// Labels
|
||
me.beforeBuildLabels();
|
||
me.buildLabels();
|
||
me.afterBuildLabels();
|
||
|
||
// Fit
|
||
me.beforeFit();
|
||
me.fit();
|
||
me.afterFit();
|
||
//
|
||
me.afterUpdate();
|
||
|
||
return me.minSize;
|
||
},
|
||
afterUpdate: noop$1,
|
||
|
||
//
|
||
|
||
beforeSetDimensions: noop$1,
|
||
setDimensions: function() {
|
||
var me = this;
|
||
// Set the unconstrained dimension before label rotation
|
||
if (me.isHorizontal()) {
|
||
// Reset position before calculating rotation
|
||
me.width = me.maxWidth;
|
||
me.left = 0;
|
||
me.right = me.width;
|
||
} else {
|
||
me.height = me.maxHeight;
|
||
|
||
// Reset position before calculating rotation
|
||
me.top = 0;
|
||
me.bottom = me.height;
|
||
}
|
||
|
||
// Reset padding
|
||
me.paddingLeft = 0;
|
||
me.paddingTop = 0;
|
||
me.paddingRight = 0;
|
||
me.paddingBottom = 0;
|
||
|
||
// Reset minSize
|
||
me.minSize = {
|
||
width: 0,
|
||
height: 0
|
||
};
|
||
},
|
||
afterSetDimensions: noop$1,
|
||
|
||
//
|
||
|
||
beforeBuildLabels: noop$1,
|
||
buildLabels: function() {
|
||
var me = this;
|
||
var labelOpts = me.options.labels || {};
|
||
var legendItems = helpers$1.callback(labelOpts.generateLabels, [me.chart], me) || [];
|
||
|
||
if (labelOpts.filter) {
|
||
legendItems = legendItems.filter(function(item) {
|
||
return labelOpts.filter(item, me.chart.data);
|
||
});
|
||
}
|
||
|
||
if (me.options.reverse) {
|
||
legendItems.reverse();
|
||
}
|
||
|
||
me.legendItems = legendItems;
|
||
},
|
||
afterBuildLabels: noop$1,
|
||
|
||
//
|
||
|
||
beforeFit: noop$1,
|
||
fit: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var labelOpts = opts.labels;
|
||
var display = opts.display;
|
||
|
||
var ctx = me.ctx;
|
||
|
||
var labelFont = helpers$1.options._parseFont(labelOpts);
|
||
var fontSize = labelFont.size;
|
||
|
||
// Reset hit boxes
|
||
var hitboxes = me.legendHitBoxes = [];
|
||
|
||
var minSize = me.minSize;
|
||
var isHorizontal = me.isHorizontal();
|
||
|
||
if (isHorizontal) {
|
||
minSize.width = me.maxWidth; // fill all the width
|
||
minSize.height = display ? 10 : 0;
|
||
} else {
|
||
minSize.width = display ? 10 : 0;
|
||
minSize.height = me.maxHeight; // fill all the height
|
||
}
|
||
|
||
// Increase sizes here
|
||
if (!display) {
|
||
me.width = minSize.width = me.height = minSize.height = 0;
|
||
return;
|
||
}
|
||
ctx.font = labelFont.string;
|
||
|
||
if (isHorizontal) {
|
||
// Labels
|
||
|
||
// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one
|
||
var lineWidths = me.lineWidths = [0];
|
||
var totalHeight = 0;
|
||
|
||
ctx.textAlign = 'left';
|
||
ctx.textBaseline = 'middle';
|
||
|
||
helpers$1.each(me.legendItems, function(legendItem, i) {
|
||
var boxWidth = getBoxWidth(labelOpts, fontSize);
|
||
var width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||
|
||
if (i === 0 || lineWidths[lineWidths.length - 1] + width + 2 * labelOpts.padding > minSize.width) {
|
||
totalHeight += fontSize + labelOpts.padding;
|
||
lineWidths[lineWidths.length - (i > 0 ? 0 : 1)] = 0;
|
||
}
|
||
|
||
// Store the hitbox width and height here. Final position will be updated in `draw`
|
||
hitboxes[i] = {
|
||
left: 0,
|
||
top: 0,
|
||
width: width,
|
||
height: fontSize
|
||
};
|
||
|
||
lineWidths[lineWidths.length - 1] += width + labelOpts.padding;
|
||
});
|
||
|
||
minSize.height += totalHeight;
|
||
|
||
} else {
|
||
var vPadding = labelOpts.padding;
|
||
var columnWidths = me.columnWidths = [];
|
||
var columnHeights = me.columnHeights = [];
|
||
var totalWidth = labelOpts.padding;
|
||
var currentColWidth = 0;
|
||
var currentColHeight = 0;
|
||
|
||
helpers$1.each(me.legendItems, function(legendItem, i) {
|
||
var boxWidth = getBoxWidth(labelOpts, fontSize);
|
||
var itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;
|
||
|
||
// If too tall, go to new column
|
||
if (i > 0 && currentColHeight + fontSize + 2 * vPadding > minSize.height) {
|
||
totalWidth += currentColWidth + labelOpts.padding;
|
||
columnWidths.push(currentColWidth); // previous column width
|
||
columnHeights.push(currentColHeight);
|
||
currentColWidth = 0;
|
||
currentColHeight = 0;
|
||
}
|
||
|
||
// Get max width
|
||
currentColWidth = Math.max(currentColWidth, itemWidth);
|
||
currentColHeight += fontSize + vPadding;
|
||
|
||
// Store the hitbox width and height here. Final position will be updated in `draw`
|
||
hitboxes[i] = {
|
||
left: 0,
|
||
top: 0,
|
||
width: itemWidth,
|
||
height: fontSize
|
||
};
|
||
});
|
||
|
||
totalWidth += currentColWidth;
|
||
columnWidths.push(currentColWidth);
|
||
columnHeights.push(currentColHeight);
|
||
minSize.width += totalWidth;
|
||
}
|
||
|
||
me.width = minSize.width;
|
||
me.height = minSize.height;
|
||
},
|
||
afterFit: noop$1,
|
||
|
||
// Shared Methods
|
||
isHorizontal: function() {
|
||
return this.options.position === 'top' || this.options.position === 'bottom';
|
||
},
|
||
|
||
// Actually draw the legend on the canvas
|
||
draw: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var labelOpts = opts.labels;
|
||
var globalDefaults = core_defaults.global;
|
||
var defaultColor = globalDefaults.defaultColor;
|
||
var lineDefault = globalDefaults.elements.line;
|
||
var legendHeight = me.height;
|
||
var columnHeights = me.columnHeights;
|
||
var legendWidth = me.width;
|
||
var lineWidths = me.lineWidths;
|
||
|
||
if (!opts.display) {
|
||
return;
|
||
}
|
||
|
||
var rtlHelper = getRtlHelper$1(opts.rtl, me.left, me.minSize.width);
|
||
var ctx = me.ctx;
|
||
var fontColor = valueOrDefault$e(labelOpts.fontColor, globalDefaults.defaultFontColor);
|
||
var labelFont = helpers$1.options._parseFont(labelOpts);
|
||
var fontSize = labelFont.size;
|
||
var cursor;
|
||
|
||
// Canvas setup
|
||
ctx.textAlign = rtlHelper.textAlign('left');
|
||
ctx.textBaseline = 'middle';
|
||
ctx.lineWidth = 0.5;
|
||
ctx.strokeStyle = fontColor; // for strikethrough effect
|
||
ctx.fillStyle = fontColor; // render in correct colour
|
||
ctx.font = labelFont.string;
|
||
|
||
var boxWidth = getBoxWidth(labelOpts, fontSize);
|
||
var hitboxes = me.legendHitBoxes;
|
||
|
||
// current position
|
||
var drawLegendBox = function(x, y, legendItem) {
|
||
if (isNaN(boxWidth) || boxWidth <= 0) {
|
||
return;
|
||
}
|
||
|
||
// Set the ctx for the box
|
||
ctx.save();
|
||
|
||
var lineWidth = valueOrDefault$e(legendItem.lineWidth, lineDefault.borderWidth);
|
||
ctx.fillStyle = valueOrDefault$e(legendItem.fillStyle, defaultColor);
|
||
ctx.lineCap = valueOrDefault$e(legendItem.lineCap, lineDefault.borderCapStyle);
|
||
ctx.lineDashOffset = valueOrDefault$e(legendItem.lineDashOffset, lineDefault.borderDashOffset);
|
||
ctx.lineJoin = valueOrDefault$e(legendItem.lineJoin, lineDefault.borderJoinStyle);
|
||
ctx.lineWidth = lineWidth;
|
||
ctx.strokeStyle = valueOrDefault$e(legendItem.strokeStyle, defaultColor);
|
||
|
||
if (ctx.setLineDash) {
|
||
// IE 9 and 10 do not support line dash
|
||
ctx.setLineDash(valueOrDefault$e(legendItem.lineDash, lineDefault.borderDash));
|
||
}
|
||
|
||
if (labelOpts && labelOpts.usePointStyle) {
|
||
// Recalculate x and y for drawPoint() because its expecting
|
||
// x and y to be center of figure (instead of top left)
|
||
var radius = boxWidth * Math.SQRT2 / 2;
|
||
var centerX = rtlHelper.xPlus(x, boxWidth / 2);
|
||
var centerY = y + fontSize / 2;
|
||
|
||
// Draw pointStyle as legend symbol
|
||
helpers$1.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY, legendItem.rotation);
|
||
} else {
|
||
// Draw box as legend symbol
|
||
ctx.fillRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
|
||
if (lineWidth !== 0) {
|
||
ctx.strokeRect(rtlHelper.leftForLtr(x, boxWidth), y, boxWidth, fontSize);
|
||
}
|
||
}
|
||
|
||
ctx.restore();
|
||
};
|
||
|
||
var fillText = function(x, y, legendItem, textWidth) {
|
||
var halfFontSize = fontSize / 2;
|
||
var xLeft = rtlHelper.xPlus(x, boxWidth + halfFontSize);
|
||
var yMiddle = y + halfFontSize;
|
||
|
||
ctx.fillText(legendItem.text, xLeft, yMiddle);
|
||
|
||
if (legendItem.hidden) {
|
||
// Strikethrough the text if hidden
|
||
ctx.beginPath();
|
||
ctx.lineWidth = 2;
|
||
ctx.moveTo(xLeft, yMiddle);
|
||
ctx.lineTo(rtlHelper.xPlus(xLeft, textWidth), yMiddle);
|
||
ctx.stroke();
|
||
}
|
||
};
|
||
|
||
var alignmentOffset = function(dimension, blockSize) {
|
||
switch (opts.align) {
|
||
case 'start':
|
||
return labelOpts.padding;
|
||
case 'end':
|
||
return dimension - blockSize;
|
||
default: // center
|
||
return (dimension - blockSize + labelOpts.padding) / 2;
|
||
}
|
||
};
|
||
|
||
// Horizontal
|
||
var isHorizontal = me.isHorizontal();
|
||
if (isHorizontal) {
|
||
cursor = {
|
||
x: me.left + alignmentOffset(legendWidth, lineWidths[0]),
|
||
y: me.top + labelOpts.padding,
|
||
line: 0
|
||
};
|
||
} else {
|
||
cursor = {
|
||
x: me.left + labelOpts.padding,
|
||
y: me.top + alignmentOffset(legendHeight, columnHeights[0]),
|
||
line: 0
|
||
};
|
||
}
|
||
|
||
helpers$1.rtl.overrideTextDirection(me.ctx, opts.textDirection);
|
||
|
||
var itemHeight = fontSize + labelOpts.padding;
|
||
helpers$1.each(me.legendItems, function(legendItem, i) {
|
||
var textWidth = ctx.measureText(legendItem.text).width;
|
||
var width = boxWidth + (fontSize / 2) + textWidth;
|
||
var x = cursor.x;
|
||
var y = cursor.y;
|
||
|
||
rtlHelper.setWidth(me.minSize.width);
|
||
|
||
// Use (me.left + me.minSize.width) and (me.top + me.minSize.height)
|
||
// instead of me.right and me.bottom because me.width and me.height
|
||
// may have been changed since me.minSize was calculated
|
||
if (isHorizontal) {
|
||
if (i > 0 && x + width + labelOpts.padding > me.left + me.minSize.width) {
|
||
y = cursor.y += itemHeight;
|
||
cursor.line++;
|
||
x = cursor.x = me.left + alignmentOffset(legendWidth, lineWidths[cursor.line]);
|
||
}
|
||
} else if (i > 0 && y + itemHeight > me.top + me.minSize.height) {
|
||
x = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;
|
||
cursor.line++;
|
||
y = cursor.y = me.top + alignmentOffset(legendHeight, columnHeights[cursor.line]);
|
||
}
|
||
|
||
var realX = rtlHelper.x(x);
|
||
|
||
drawLegendBox(realX, y, legendItem);
|
||
|
||
hitboxes[i].left = rtlHelper.leftForLtr(realX, hitboxes[i].width);
|
||
hitboxes[i].top = y;
|
||
|
||
// Fill the actual label
|
||
fillText(realX, y, legendItem, textWidth);
|
||
|
||
if (isHorizontal) {
|
||
cursor.x += width + labelOpts.padding;
|
||
} else {
|
||
cursor.y += itemHeight;
|
||
}
|
||
});
|
||
|
||
helpers$1.rtl.restoreTextDirection(me.ctx, opts.textDirection);
|
||
},
|
||
|
||
/**
|
||
* @private
|
||
*/
|
||
_getLegendItemAt: function(x, y) {
|
||
var me = this;
|
||
var i, hitBox, lh;
|
||
|
||
if (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {
|
||
// See if we are touching one of the dataset boxes
|
||
lh = me.legendHitBoxes;
|
||
for (i = 0; i < lh.length; ++i) {
|
||
hitBox = lh[i];
|
||
|
||
if (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {
|
||
// Touching an element
|
||
return me.legendItems[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
},
|
||
|
||
/**
|
||
* Handle an event
|
||
* @private
|
||
* @param {IEvent} event - The event to handle
|
||
*/
|
||
handleEvent: function(e) {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var type = e.type === 'mouseup' ? 'click' : e.type;
|
||
var hoveredItem;
|
||
|
||
if (type === 'mousemove') {
|
||
if (!opts.onHover && !opts.onLeave) {
|
||
return;
|
||
}
|
||
} else if (type === 'click') {
|
||
if (!opts.onClick) {
|
||
return;
|
||
}
|
||
} else {
|
||
return;
|
||
}
|
||
|
||
// Chart event already has relative position in it
|
||
hoveredItem = me._getLegendItemAt(e.x, e.y);
|
||
|
||
if (type === 'click') {
|
||
if (hoveredItem && opts.onClick) {
|
||
// use e.native for backwards compatibility
|
||
opts.onClick.call(me, e.native, hoveredItem);
|
||
}
|
||
} else {
|
||
if (opts.onLeave && hoveredItem !== me._hoveredItem) {
|
||
if (me._hoveredItem) {
|
||
opts.onLeave.call(me, e.native, me._hoveredItem);
|
||
}
|
||
me._hoveredItem = hoveredItem;
|
||
}
|
||
|
||
if (opts.onHover && hoveredItem) {
|
||
// use e.native for backwards compatibility
|
||
opts.onHover.call(me, e.native, hoveredItem);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
function createNewLegendAndAttach(chart, legendOpts) {
|
||
var legend = new Legend({
|
||
ctx: chart.ctx,
|
||
options: legendOpts,
|
||
chart: chart
|
||
});
|
||
|
||
core_layouts.configure(chart, legend, legendOpts);
|
||
core_layouts.addBox(chart, legend);
|
||
chart.legend = legend;
|
||
}
|
||
|
||
var plugin_legend = {
|
||
id: 'legend',
|
||
|
||
/**
|
||
* Backward compatibility: since 2.1.5, the legend is registered as a plugin, making
|
||
* Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of
|
||
* the plugin, which one will be re-exposed in the chart.js file.
|
||
* https://github.com/chartjs/Chart.js/pull/2640
|
||
* @private
|
||
*/
|
||
_element: Legend,
|
||
|
||
beforeInit: function(chart) {
|
||
var legendOpts = chart.options.legend;
|
||
|
||
if (legendOpts) {
|
||
createNewLegendAndAttach(chart, legendOpts);
|
||
}
|
||
},
|
||
|
||
beforeUpdate: function(chart) {
|
||
var legendOpts = chart.options.legend;
|
||
var legend = chart.legend;
|
||
|
||
if (legendOpts) {
|
||
helpers$1.mergeIf(legendOpts, core_defaults.global.legend);
|
||
|
||
if (legend) {
|
||
core_layouts.configure(chart, legend, legendOpts);
|
||
legend.options = legendOpts;
|
||
} else {
|
||
createNewLegendAndAttach(chart, legendOpts);
|
||
}
|
||
} else if (legend) {
|
||
core_layouts.removeBox(chart, legend);
|
||
delete chart.legend;
|
||
}
|
||
},
|
||
|
||
afterEvent: function(chart, e) {
|
||
var legend = chart.legend;
|
||
if (legend) {
|
||
legend.handleEvent(e);
|
||
}
|
||
}
|
||
};
|
||
|
||
var noop$2 = helpers$1.noop;
|
||
|
||
core_defaults._set('global', {
|
||
title: {
|
||
display: false,
|
||
fontStyle: 'bold',
|
||
fullWidth: true,
|
||
padding: 10,
|
||
position: 'top',
|
||
text: '',
|
||
weight: 2000 // by default greater than legend (1000) to be above
|
||
}
|
||
});
|
||
|
||
/**
|
||
* IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!
|
||
*/
|
||
var Title = core_element.extend({
|
||
initialize: function(config) {
|
||
var me = this;
|
||
helpers$1.extend(me, config);
|
||
|
||
// Contains hit boxes for each dataset (in dataset order)
|
||
me.legendHitBoxes = [];
|
||
},
|
||
|
||
// These methods are ordered by lifecycle. Utilities then follow.
|
||
|
||
beforeUpdate: noop$2,
|
||
update: function(maxWidth, maxHeight, margins) {
|
||
var me = this;
|
||
|
||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||
me.beforeUpdate();
|
||
|
||
// Absorb the master measurements
|
||
me.maxWidth = maxWidth;
|
||
me.maxHeight = maxHeight;
|
||
me.margins = margins;
|
||
|
||
// Dimensions
|
||
me.beforeSetDimensions();
|
||
me.setDimensions();
|
||
me.afterSetDimensions();
|
||
// Labels
|
||
me.beforeBuildLabels();
|
||
me.buildLabels();
|
||
me.afterBuildLabels();
|
||
|
||
// Fit
|
||
me.beforeFit();
|
||
me.fit();
|
||
me.afterFit();
|
||
//
|
||
me.afterUpdate();
|
||
|
||
return me.minSize;
|
||
|
||
},
|
||
afterUpdate: noop$2,
|
||
|
||
//
|
||
|
||
beforeSetDimensions: noop$2,
|
||
setDimensions: function() {
|
||
var me = this;
|
||
// Set the unconstrained dimension before label rotation
|
||
if (me.isHorizontal()) {
|
||
// Reset position before calculating rotation
|
||
me.width = me.maxWidth;
|
||
me.left = 0;
|
||
me.right = me.width;
|
||
} else {
|
||
me.height = me.maxHeight;
|
||
|
||
// Reset position before calculating rotation
|
||
me.top = 0;
|
||
me.bottom = me.height;
|
||
}
|
||
|
||
// Reset padding
|
||
me.paddingLeft = 0;
|
||
me.paddingTop = 0;
|
||
me.paddingRight = 0;
|
||
me.paddingBottom = 0;
|
||
|
||
// Reset minSize
|
||
me.minSize = {
|
||
width: 0,
|
||
height: 0
|
||
};
|
||
},
|
||
afterSetDimensions: noop$2,
|
||
|
||
//
|
||
|
||
beforeBuildLabels: noop$2,
|
||
buildLabels: noop$2,
|
||
afterBuildLabels: noop$2,
|
||
|
||
//
|
||
|
||
beforeFit: noop$2,
|
||
fit: function() {
|
||
var me = this;
|
||
var opts = me.options;
|
||
var minSize = me.minSize = {};
|
||
var isHorizontal = me.isHorizontal();
|
||
var lineCount, textSize;
|
||
|
||
if (!opts.display) {
|
||
me.width = minSize.width = me.height = minSize.height = 0;
|
||
return;
|
||
}
|
||
|
||
lineCount = helpers$1.isArray(opts.text) ? opts.text.length : 1;
|
||
textSize = lineCount * helpers$1.options._parseFont(opts).lineHeight + opts.padding * 2;
|
||
|
||
me.width = minSize.width = isHorizontal ? me.maxWidth : textSize;
|
||
me.height = minSize.height = isHorizontal ? textSize : me.maxHeight;
|
||
},
|
||
afterFit: noop$2,
|
||
|
||
// Shared Methods
|
||
isHorizontal: function() {
|
||
var pos = this.options.position;
|
||
return pos === 'top' || pos === 'bottom';
|
||
},
|
||
|
||
// Actually draw the title block on the canvas
|
||
draw: function() {
|
||
var me = this;
|
||
var ctx = me.ctx;
|
||
var opts = me.options;
|
||
|
||
if (!opts.display) {
|
||
return;
|
||
}
|
||
|
||
var fontOpts = helpers$1.options._parseFont(opts);
|
||
var lineHeight = fontOpts.lineHeight;
|
||
var offset = lineHeight / 2 + opts.padding;
|
||
var rotation = 0;
|
||
var top = me.top;
|
||
var left = me.left;
|
||
var bottom = me.bottom;
|
||
var right = me.right;
|
||
var maxWidth, titleX, titleY;
|
||
|
||
ctx.fillStyle = helpers$1.valueOrDefault(opts.fontColor, core_defaults.global.defaultFontColor); // render in correct colour
|
||
ctx.font = fontOpts.string;
|
||
|
||
// Horizontal
|
||
if (me.isHorizontal()) {
|
||
titleX = left + ((right - left) / 2); // midpoint of the width
|
||
titleY = top + offset;
|
||
maxWidth = right - left;
|
||
} else {
|
||
titleX = opts.position === 'left' ? left + offset : right - offset;
|
||
titleY = top + ((bottom - top) / 2);
|
||
maxWidth = bottom - top;
|
||
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
|
||
}
|
||
|
||
ctx.save();
|
||
ctx.translate(titleX, titleY);
|
||
ctx.rotate(rotation);
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
|
||
var text = opts.text;
|
||
if (helpers$1.isArray(text)) {
|
||
var y = 0;
|
||
for (var i = 0; i < text.length; ++i) {
|
||
ctx.fillText(text[i], 0, y, maxWidth);
|
||
y += lineHeight;
|
||
}
|
||
} else {
|
||
ctx.fillText(text, 0, 0, maxWidth);
|
||
}
|
||
|
||
ctx.restore();
|
||
}
|
||
});
|
||
|
||
function createNewTitleBlockAndAttach(chart, titleOpts) {
|
||
var title = new Title({
|
||
ctx: chart.ctx,
|
||
options: titleOpts,
|
||
chart: chart
|
||
});
|
||
|
||
core_layouts.configure(chart, title, titleOpts);
|
||
core_layouts.addBox(chart, title);
|
||
chart.titleBlock = title;
|
||
}
|
||
|
||
var plugin_title = {
|
||
id: 'title',
|
||
|
||
/**
|
||
* Backward compatibility: since 2.1.5, the title is registered as a plugin, making
|
||
* Chart.Title obsolete. To avoid a breaking change, we export the Title as part of
|
||
* the plugin, which one will be re-exposed in the chart.js file.
|
||
* https://github.com/chartjs/Chart.js/pull/2640
|
||
* @private
|
||
*/
|
||
_element: Title,
|
||
|
||
beforeInit: function(chart) {
|
||
var titleOpts = chart.options.title;
|
||
|
||
if (titleOpts) {
|
||
createNewTitleBlockAndAttach(chart, titleOpts);
|
||
}
|
||
},
|
||
|
||
beforeUpdate: function(chart) {
|
||
var titleOpts = chart.options.title;
|
||
var titleBlock = chart.titleBlock;
|
||
|
||
if (titleOpts) {
|
||
helpers$1.mergeIf(titleOpts, core_defaults.global.title);
|
||
|
||
if (titleBlock) {
|
||
core_layouts.configure(chart, titleBlock, titleOpts);
|
||
titleBlock.options = titleOpts;
|
||
} else {
|
||
createNewTitleBlockAndAttach(chart, titleOpts);
|
||
}
|
||
} else if (titleBlock) {
|
||
core_layouts.removeBox(chart, titleBlock);
|
||
delete chart.titleBlock;
|
||
}
|
||
}
|
||
};
|
||
|
||
var plugins = {};
|
||
var filler = plugin_filler;
|
||
var legend = plugin_legend;
|
||
var title = plugin_title;
|
||
plugins.filler = filler;
|
||
plugins.legend = legend;
|
||
plugins.title = title;
|
||
|
||
/**
|
||
* @namespace Chart
|
||
*/
|
||
|
||
|
||
core_controller.helpers = helpers$1;
|
||
|
||
// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!
|
||
core_helpers();
|
||
|
||
core_controller._adapters = core_adapters;
|
||
core_controller.Animation = core_animation;
|
||
core_controller.animationService = core_animations;
|
||
core_controller.controllers = controllers;
|
||
core_controller.DatasetController = core_datasetController;
|
||
core_controller.defaults = core_defaults;
|
||
core_controller.Element = core_element;
|
||
core_controller.elements = elements;
|
||
core_controller.Interaction = core_interaction;
|
||
core_controller.layouts = core_layouts;
|
||
core_controller.platform = platform;
|
||
core_controller.plugins = core_plugins;
|
||
core_controller.Scale = core_scale;
|
||
core_controller.scaleService = core_scaleService;
|
||
core_controller.Ticks = core_ticks;
|
||
core_controller.Tooltip = core_tooltip;
|
||
|
||
// Register built-in scales
|
||
|
||
core_controller.helpers.each(scales, function(scale, type) {
|
||
core_controller.scaleService.registerScaleType(type, scale, scale._defaults);
|
||
});
|
||
|
||
// Load to register built-in adapters (as side effects)
|
||
|
||
|
||
// Loading built-in plugins
|
||
|
||
for (var k in plugins) {
|
||
if (plugins.hasOwnProperty(k)) {
|
||
core_controller.plugins.register(plugins[k]);
|
||
}
|
||
}
|
||
|
||
core_controller.platform.initialize();
|
||
|
||
var src = core_controller;
|
||
if (typeof window !== 'undefined') {
|
||
window.Chart = core_controller;
|
||
}
|
||
|
||
// DEPRECATIONS
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore
|
||
* @namespace Chart.Chart
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.Chart = core_controller;
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore
|
||
* @namespace Chart.Legend
|
||
* @deprecated since version 2.1.5
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.Legend = plugins.legend._element;
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore
|
||
* @namespace Chart.Title
|
||
* @deprecated since version 2.1.5
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.Title = plugins.title._element;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.plugins instead
|
||
* @namespace Chart.pluginService
|
||
* @deprecated since version 2.1.5
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.pluginService = core_controller.plugins;
|
||
|
||
/**
|
||
* Provided for backward compatibility, inheriting from Chart.PlugingBase has no
|
||
* effect, instead simply create/register plugins via plain JavaScript objects.
|
||
* @interface Chart.PluginBase
|
||
* @deprecated since version 2.5.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.PluginBase = core_controller.Element.extend({});
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.helpers.canvas instead.
|
||
* @namespace Chart.canvasHelpers
|
||
* @deprecated since version 2.6.0
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.canvasHelpers = core_controller.helpers.canvas;
|
||
|
||
/**
|
||
* Provided for backward compatibility, use Chart.layouts instead.
|
||
* @namespace Chart.layoutService
|
||
* @deprecated since version 2.7.3
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.layoutService = core_controller.layouts;
|
||
|
||
/**
|
||
* Provided for backward compatibility, not available anymore.
|
||
* @namespace Chart.LinearScaleBase
|
||
* @deprecated since version 2.8
|
||
* @todo remove at version 3
|
||
* @private
|
||
*/
|
||
core_controller.LinearScaleBase = scale_linearbase;
|
||
|
||
/**
|
||
* Provided for backward compatibility, instead we should create a new Chart
|
||
* by setting the type in the config (`new Chart(id, {type: '{chart-type}'}`).
|
||
* @deprecated since version 2.8.0
|
||
* @todo remove at version 3
|
||
*/
|
||
core_controller.helpers.each(
|
||
[
|
||
'Bar',
|
||
'Bubble',
|
||
'Doughnut',
|
||
'Line',
|
||
'PolarArea',
|
||
'Radar',
|
||
'Scatter'
|
||
],
|
||
function(klass) {
|
||
core_controller[klass] = function(ctx, cfg) {
|
||
return new core_controller(ctx, core_controller.helpers.merge(cfg || {}, {
|
||
type: klass.charAt(0).toLowerCase() + klass.slice(1)
|
||
}));
|
||
};
|
||
}
|
||
);
|
||
|
||
return src;
|
||
|
||
})));
|
||
|
||
(function (root, factory) {
|
||
if (typeof define === 'function' && define.amd) {
|
||
// AMD. Register as an anonymous module unless amdModuleId is set
|
||
define([], function () {
|
||
return (root['SignaturePad'] = factory());
|
||
});
|
||
} else if (typeof exports === 'object') {
|
||
// Node. Does not work with strict CommonJS, but
|
||
// only CommonJS-like environments that support module.exports,
|
||
// like Node.
|
||
module.exports = factory();
|
||
} else {
|
||
root['SignaturePad'] = factory();
|
||
}
|
||
}(this, function () {
|
||
|
||
/*!
|
||
* Signature Pad v1.5.3
|
||
* https://github.com/szimek/signature_pad
|
||
*
|
||
* Copyright 2016 Szymon Nowak
|
||
* Released under the MIT license
|
||
*
|
||
* The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from:
|
||
* http://corner.squareup.com/2012/07/smoother-signatures.html
|
||
*
|
||
* Implementation of interpolation using cubic Bézier curves is taken from:
|
||
* http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript
|
||
*
|
||
* Algorithm for approximated length of a Bézier curve is taken from:
|
||
* http://www.lemoda.net/maths/bezier-length/index.html
|
||
*
|
||
*/
|
||
var SignaturePad = (function (document) {
|
||
"use strict";
|
||
|
||
var SignaturePad = function (canvas, options) {
|
||
var self = this,
|
||
opts = options || {};
|
||
|
||
this.velocityFilterWeight = opts.velocityFilterWeight || 0.7;
|
||
this.minWidth = opts.minWidth || 0.5;
|
||
this.maxWidth = opts.maxWidth || 2.5;
|
||
this.dotSize = opts.dotSize || function () {
|
||
return (this.minWidth + this.maxWidth) / 2;
|
||
};
|
||
this.penColor = opts.penColor || "black";
|
||
this.backgroundColor = opts.backgroundColor || "rgba(0,0,0,0)";
|
||
this.onEnd = opts.onEnd;
|
||
this.onBegin = opts.onBegin;
|
||
|
||
this._canvas = canvas;
|
||
this._ctx = canvas.getContext("2d");
|
||
this.clear();
|
||
|
||
// we need add these inline so they are available to unbind while still having
|
||
// access to 'self' we could use _.bind but it's not worth adding a dependency
|
||
this._handleMouseDown = function (event) {
|
||
if (event.which === 1) {
|
||
self._mouseButtonDown = true;
|
||
self._strokeBegin(event);
|
||
}
|
||
};
|
||
|
||
this._handleMouseMove = function (event) {
|
||
if (self._mouseButtonDown) {
|
||
self._strokeUpdate(event);
|
||
}
|
||
};
|
||
|
||
this._handleMouseUp = function (event) {
|
||
if (event.which === 1 && self._mouseButtonDown) {
|
||
self._mouseButtonDown = false;
|
||
self._strokeEnd(event);
|
||
}
|
||
};
|
||
|
||
this._handleTouchStart = function (event) {
|
||
if (event.targetTouches.length == 1) {
|
||
var touch = event.changedTouches[0];
|
||
self._strokeBegin(touch);
|
||
}
|
||
};
|
||
|
||
this._handleTouchMove = function (event) {
|
||
// Prevent scrolling.
|
||
event.preventDefault();
|
||
|
||
var touch = event.targetTouches[0];
|
||
self._strokeUpdate(touch);
|
||
};
|
||
|
||
this._handleTouchEnd = function (event) {
|
||
var wasCanvasTouched = event.target === self._canvas;
|
||
if (wasCanvasTouched) {
|
||
event.preventDefault();
|
||
self._strokeEnd(event);
|
||
}
|
||
};
|
||
|
||
this._handleMouseEvents();
|
||
this._handleTouchEvents();
|
||
};
|
||
|
||
SignaturePad.prototype.clear = function () {
|
||
var ctx = this._ctx,
|
||
canvas = this._canvas;
|
||
|
||
ctx.fillStyle = this.backgroundColor;
|
||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||
this._reset();
|
||
};
|
||
|
||
SignaturePad.prototype.toDataURL = function (imageType, quality) {
|
||
var canvas = this._canvas;
|
||
return canvas.toDataURL.apply(canvas, arguments);
|
||
};
|
||
|
||
SignaturePad.prototype.fromDataURL = function (dataUrl) {
|
||
var self = this,
|
||
image = new Image(),
|
||
ratio = window.devicePixelRatio || 1,
|
||
width = this._canvas.width / ratio,
|
||
height = this._canvas.height / ratio;
|
||
|
||
this._reset();
|
||
image.src = dataUrl;
|
||
image.onload = function () {
|
||
self._ctx.drawImage(image, 0, 0, width, height);
|
||
};
|
||
this._isEmpty = false;
|
||
};
|
||
|
||
SignaturePad.prototype._strokeUpdate = function (event) {
|
||
var point = this._createPoint(event);
|
||
this._addPoint(point);
|
||
};
|
||
|
||
SignaturePad.prototype._strokeBegin = function (event) {
|
||
this._reset();
|
||
this._strokeUpdate(event);
|
||
if (typeof this.onBegin === 'function') {
|
||
this.onBegin(event);
|
||
}
|
||
};
|
||
|
||
SignaturePad.prototype._strokeDraw = function (point) {
|
||
var ctx = this._ctx,
|
||
dotSize = typeof(this.dotSize) === 'function' ? this.dotSize() : this.dotSize;
|
||
|
||
ctx.beginPath();
|
||
this._drawPoint(point.x, point.y, dotSize);
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
};
|
||
|
||
SignaturePad.prototype._strokeEnd = function (event) {
|
||
var canDrawCurve = this.points.length > 2,
|
||
point = this.points[0];
|
||
|
||
if (!canDrawCurve && point) {
|
||
this._strokeDraw(point);
|
||
}
|
||
if (typeof this.onEnd === 'function') {
|
||
this.onEnd(event);
|
||
}
|
||
};
|
||
|
||
SignaturePad.prototype._handleMouseEvents = function () {
|
||
this._mouseButtonDown = false;
|
||
|
||
this._canvas.addEventListener("mousedown", this._handleMouseDown);
|
||
this._canvas.addEventListener("mousemove", this._handleMouseMove);
|
||
document.addEventListener("mouseup", this._handleMouseUp);
|
||
};
|
||
|
||
SignaturePad.prototype._handleTouchEvents = function () {
|
||
// Pass touch events to canvas element on mobile IE11 and Edge.
|
||
this._canvas.style.msTouchAction = 'none';
|
||
this._canvas.style.touchAction = 'none';
|
||
|
||
this._canvas.addEventListener("touchstart", this._handleTouchStart);
|
||
this._canvas.addEventListener("touchmove", this._handleTouchMove);
|
||
this._canvas.addEventListener("touchend", this._handleTouchEnd);
|
||
};
|
||
|
||
SignaturePad.prototype.on = function () {
|
||
this._handleMouseEvents();
|
||
this._handleTouchEvents();
|
||
};
|
||
|
||
SignaturePad.prototype.off = function () {
|
||
this._canvas.removeEventListener("mousedown", this._handleMouseDown);
|
||
this._canvas.removeEventListener("mousemove", this._handleMouseMove);
|
||
document.removeEventListener("mouseup", this._handleMouseUp);
|
||
|
||
this._canvas.removeEventListener("touchstart", this._handleTouchStart);
|
||
this._canvas.removeEventListener("touchmove", this._handleTouchMove);
|
||
this._canvas.removeEventListener("touchend", this._handleTouchEnd);
|
||
};
|
||
|
||
SignaturePad.prototype.isEmpty = function () {
|
||
return this._isEmpty;
|
||
};
|
||
|
||
SignaturePad.prototype._reset = function () {
|
||
this.points = [];
|
||
this._lastVelocity = 0;
|
||
this._lastWidth = (this.minWidth + this.maxWidth) / 2;
|
||
this._isEmpty = true;
|
||
this._ctx.fillStyle = this.penColor;
|
||
};
|
||
|
||
SignaturePad.prototype._createPoint = function (event) {
|
||
var rect = this._canvas.getBoundingClientRect();
|
||
return new Point(
|
||
event.clientX - rect.left,
|
||
event.clientY - rect.top
|
||
);
|
||
};
|
||
|
||
SignaturePad.prototype._addPoint = function (point) {
|
||
var points = this.points,
|
||
c2, c3,
|
||
curve, tmp;
|
||
|
||
points.push(point);
|
||
|
||
if (points.length > 2) {
|
||
// To reduce the initial lag make it work with 3 points
|
||
// by copying the first point to the beginning.
|
||
if (points.length === 3) points.unshift(points[0]);
|
||
|
||
tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]);
|
||
c2 = tmp.c2;
|
||
tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]);
|
||
c3 = tmp.c1;
|
||
curve = new Bezier(points[1], c2, c3, points[2]);
|
||
this._addCurve(curve);
|
||
|
||
// Remove the first element from the list,
|
||
// so that we always have no more than 4 points in points array.
|
||
points.shift();
|
||
}
|
||
};
|
||
|
||
SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) {
|
||
var dx1 = s1.x - s2.x, dy1 = s1.y - s2.y,
|
||
dx2 = s2.x - s3.x, dy2 = s2.y - s3.y,
|
||
|
||
m1 = {x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0},
|
||
m2 = {x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0},
|
||
|
||
l1 = Math.sqrt(dx1*dx1 + dy1*dy1),
|
||
l2 = Math.sqrt(dx2*dx2 + dy2*dy2),
|
||
|
||
dxm = (m1.x - m2.x),
|
||
dym = (m1.y - m2.y),
|
||
|
||
k = l2 / (l1 + l2),
|
||
cm = {x: m2.x + dxm*k, y: m2.y + dym*k},
|
||
|
||
tx = s2.x - cm.x,
|
||
ty = s2.y - cm.y;
|
||
|
||
return {
|
||
c1: new Point(m1.x + tx, m1.y + ty),
|
||
c2: new Point(m2.x + tx, m2.y + ty)
|
||
};
|
||
};
|
||
|
||
SignaturePad.prototype._addCurve = function (curve) {
|
||
var startPoint = curve.startPoint,
|
||
endPoint = curve.endPoint,
|
||
velocity, newWidth;
|
||
|
||
velocity = endPoint.velocityFrom(startPoint);
|
||
velocity = this.velocityFilterWeight * velocity
|
||
+ (1 - this.velocityFilterWeight) * this._lastVelocity;
|
||
|
||
newWidth = this._strokeWidth(velocity);
|
||
this._drawCurve(curve, this._lastWidth, newWidth);
|
||
|
||
this._lastVelocity = velocity;
|
||
this._lastWidth = newWidth;
|
||
};
|
||
|
||
SignaturePad.prototype._drawPoint = function (x, y, size) {
|
||
var ctx = this._ctx;
|
||
|
||
ctx.moveTo(x, y);
|
||
ctx.arc(x, y, size, 0, 2 * Math.PI, false);
|
||
this._isEmpty = false;
|
||
};
|
||
|
||
SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) {
|
||
var ctx = this._ctx,
|
||
widthDelta = endWidth - startWidth,
|
||
drawSteps, width, i, t, tt, ttt, u, uu, uuu, x, y;
|
||
|
||
drawSteps = Math.floor(curve.length());
|
||
ctx.beginPath();
|
||
for (i = 0; i < drawSteps; i++) {
|
||
// Calculate the Bezier (x, y) coordinate for this step.
|
||
t = i / drawSteps;
|
||
tt = t * t;
|
||
ttt = tt * t;
|
||
u = 1 - t;
|
||
uu = u * u;
|
||
uuu = uu * u;
|
||
|
||
x = uuu * curve.startPoint.x;
|
||
x += 3 * uu * t * curve.control1.x;
|
||
x += 3 * u * tt * curve.control2.x;
|
||
x += ttt * curve.endPoint.x;
|
||
|
||
y = uuu * curve.startPoint.y;
|
||
y += 3 * uu * t * curve.control1.y;
|
||
y += 3 * u * tt * curve.control2.y;
|
||
y += ttt * curve.endPoint.y;
|
||
|
||
width = startWidth + ttt * widthDelta;
|
||
this._drawPoint(x, y, width);
|
||
}
|
||
ctx.closePath();
|
||
ctx.fill();
|
||
};
|
||
|
||
SignaturePad.prototype._strokeWidth = function (velocity) {
|
||
return Math.max(this.maxWidth / (velocity + 1), this.minWidth);
|
||
};
|
||
|
||
|
||
var Point = function (x, y, time) {
|
||
this.x = x;
|
||
this.y = y;
|
||
this.time = time || new Date().getTime();
|
||
};
|
||
|
||
Point.prototype.velocityFrom = function (start) {
|
||
return (this.time !== start.time) ? this.distanceTo(start) / (this.time - start.time) : 1;
|
||
};
|
||
|
||
Point.prototype.distanceTo = function (start) {
|
||
return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
|
||
};
|
||
|
||
var Bezier = function (startPoint, control1, control2, endPoint) {
|
||
this.startPoint = startPoint;
|
||
this.control1 = control1;
|
||
this.control2 = control2;
|
||
this.endPoint = endPoint;
|
||
};
|
||
|
||
// Returns approximated length.
|
||
Bezier.prototype.length = function () {
|
||
var steps = 10,
|
||
length = 0,
|
||
i, t, cx, cy, px, py, xdiff, ydiff;
|
||
|
||
for (i = 0; i <= steps; i++) {
|
||
t = i / steps;
|
||
cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
|
||
cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
|
||
if (i > 0) {
|
||
xdiff = cx - px;
|
||
ydiff = cy - py;
|
||
length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
|
||
}
|
||
px = cx;
|
||
py = cy;
|
||
}
|
||
return length;
|
||
};
|
||
|
||
Bezier.prototype._point = function (t, start, c1, c2, end) {
|
||
return start * (1.0 - t) * (1.0 - t) * (1.0 - t)
|
||
+ 3.0 * c1 * (1.0 - t) * (1.0 - t) * t
|
||
+ 3.0 * c2 * (1.0 - t) * t * t
|
||
+ end * t * t * t;
|
||
};
|
||
|
||
return SignaturePad;
|
||
})(document);
|
||
|
||
return SignaturePad;
|
||
|
||
}));
|
||
|
||
(function (root, factory) {
|
||
if (typeof define === 'function' && define.amd) {
|
||
// AMD. Register as an anonymous module unless amdModuleId is set
|
||
define(["jquery"], function (a0) {
|
||
return (factory(a0));
|
||
});
|
||
} else if (typeof module === 'object' && module.exports) {
|
||
// Node. Does not work with strict CommonJS, but
|
||
// only CommonJS-like environments that support module.exports,
|
||
// like Node.
|
||
module.exports = factory(require("jquery"));
|
||
} else {
|
||
factory(root["jQuery"]);
|
||
}
|
||
}(this, function (jQuery) {
|
||
|
||
/** File generated by Grunt -- do not modify
|
||
* JQUERY-FORM-VALIDATOR
|
||
*
|
||
* @version 2.3.79
|
||
* @website http://formvalidator.net/
|
||
* @author Victor Jonsson, http://victorjonsson.se
|
||
* @license MIT
|
||
*/
|
||
/**
|
||
*/
|
||
(function ($, window, undefined) {
|
||
|
||
var disableFormSubmit = function () {
|
||
return false;
|
||
},
|
||
lastFormEvent = null,
|
||
HaltManager = {
|
||
numHalted: 0,
|
||
haltValidation: function($form) {
|
||
this.numHalted++;
|
||
$.formUtils.haltValidation = true;
|
||
$form
|
||
.unbind('submit', disableFormSubmit)
|
||
.bind('submit', disableFormSubmit)
|
||
.find('*[type="submit"]')
|
||
.addClass('disabled')
|
||
.attr('disabled', 'disabled');
|
||
},
|
||
unHaltValidation: function($form) {
|
||
this.numHalted--;
|
||
if (this.numHalted === 0) {
|
||
$.formUtils.haltValidation = false;
|
||
$form
|
||
.unbind('submit', disableFormSubmit)
|
||
.find('*[type="submit"]')
|
||
.removeClass('disabled')
|
||
.removeAttr('disabled', 'disabled');
|
||
}
|
||
}
|
||
};
|
||
|
||
function AsyncValidation($form, $input) {
|
||
this.$form = $form;
|
||
this.$input = $input;
|
||
this.reset();
|
||
$input.on('change paste', this.reset.bind(this));
|
||
}
|
||
|
||
AsyncValidation.prototype.reset = function() {
|
||
this.haltedFormValidation = false;
|
||
this.hasRun = false;
|
||
this.isRunning = false;
|
||
this.result = undefined;
|
||
};
|
||
|
||
AsyncValidation.prototype.run = function(eventContext, callback) {
|
||
if (eventContext === 'keyup') {
|
||
return null;
|
||
} else if (this.isRunning) {
|
||
lastFormEvent = eventContext;
|
||
if (!this.haltedFormValidation) {
|
||
HaltManager.haltValidation();
|
||
this.haltedFormValidation = true;
|
||
}
|
||
return null; // Waiting for result
|
||
} else if(this.hasRun) {
|
||
//this.$input.one('keyup change paste', this.reset.bind(this));
|
||
return this.result;
|
||
} else {
|
||
lastFormEvent = eventContext;
|
||
HaltManager.haltValidation(this.$form);
|
||
this.haltedFormValidation = true;
|
||
this.isRunning = true;
|
||
this.$input
|
||
.attr('disabled', 'disabled')
|
||
.addClass('async-validation');
|
||
this.$form.addClass('async-validation');
|
||
|
||
callback(function(result) {
|
||
this.done(result);
|
||
}.bind(this));
|
||
|
||
return null;
|
||
}
|
||
};
|
||
|
||
AsyncValidation.prototype.done = function(result) {
|
||
this.result = result;
|
||
this.hasRun = true;
|
||
this.isRunning = false;
|
||
this.$input
|
||
.removeAttr('disabled')
|
||
.removeClass('async-validation');
|
||
this.$form.removeClass('async-validation');
|
||
if (this.haltedFormValidation) {
|
||
HaltManager.unHaltValidation(this.$form);
|
||
if (lastFormEvent === 'submit') {
|
||
this.$form.trigger('submit');
|
||
} else {
|
||
this.$input.trigger('validation.revalidate');
|
||
}
|
||
}
|
||
};
|
||
|
||
AsyncValidation.loadInstance = function(validatorName, $input, $form) {
|
||
// Return async validator attached to this input element
|
||
// or create a new async validator and attach it to the input
|
||
var asyncValidation,
|
||
input = $input.get(0);
|
||
|
||
if (!input.asyncValidators) {
|
||
input.asyncValidators = {};
|
||
}
|
||
|
||
if (input.asyncValidators[validatorName]) {
|
||
asyncValidation = input.asyncValidators[validatorName];
|
||
} else {
|
||
asyncValidation = new AsyncValidation($form, $input);
|
||
input.asyncValidators[validatorName] = asyncValidation;
|
||
}
|
||
|
||
return asyncValidation;
|
||
};
|
||
|
||
$.formUtils = $.extend($.formUtils || {}, {
|
||
|
||
/**
|
||
* @deprecated
|
||
* @param validatorName
|
||
* @param $input
|
||
* @param $form
|
||
*/
|
||
asyncValidation: function(validatorName, $input, $form) {
|
||
// @todo: Remove when moving up to version 3.0
|
||
this.warn('Use of deprecated function $.formUtils.asyncValidation, use $.formUtils.addAsyncValidator() instead');
|
||
return AsyncValidation.loadInstance(validatorName, $input, $form);
|
||
},
|
||
|
||
/**
|
||
* @param {Object} asyncValidator
|
||
*/
|
||
addAsyncValidator: function (asyncValidator) {
|
||
var validator = $.extend({}, asyncValidator),
|
||
originalValidatorFunc = validator.validatorFunction;
|
||
validator.async = true;
|
||
validator.validatorFunction = function (value, $el, config, language, $form, eventContext) {
|
||
var asyncValidation = AsyncValidation.loadInstance(this.name, $el, $form);
|
||
return asyncValidation.run(eventContext, function(done) {
|
||
originalValidatorFunc.apply(validator, [
|
||
done, value, $el, config, language, $form, eventContext
|
||
]);
|
||
});
|
||
};
|
||
this.addValidator(validator);
|
||
}
|
||
});
|
||
|
||
// Tag elements having async validators
|
||
$(window).bind('validatorsLoaded formValidationSetup', function (evt, $form) {
|
||
if (!$form) {
|
||
$form = $('form');
|
||
}
|
||
$form.find('[data-validation]').each(function () {
|
||
var $input = $(this);
|
||
$input.valAttr('async', false);
|
||
$.each($.split($input.attr('data-validation')), function (i, validatorName) {
|
||
var validator = $.formUtils.validators['validate_'+validatorName];
|
||
if (validator && validator.async) {
|
||
$input.valAttr('async', 'yes');
|
||
}
|
||
});
|
||
});
|
||
});
|
||
|
||
})(jQuery, window);
|
||
|
||
/**
|
||
* Deprecated functions and attributes
|
||
* @todo: Remove in release of 3.0
|
||
*/
|
||
(function ($, undefined) {
|
||
|
||
'use strict';
|
||
|
||
/**
|
||
* @deprecated
|
||
* @param language
|
||
* @param conf
|
||
*/
|
||
$.fn.validateForm = function (language, conf) {
|
||
$.formUtils.warn('Use of deprecated function $.validateForm, use $.isValid instead');
|
||
return this.isValid(language, conf, true);
|
||
};
|
||
|
||
$(window)
|
||
.on('formValidationPluginInit', function(evt, config) {
|
||
convertDeprecatedLangCodeToISO6391(config);
|
||
addSupportForCustomErrorMessageCallback(config);
|
||
addSupportForElementReferenceInPositionParam(config);
|
||
})
|
||
.on('validatorsLoaded formValidationSetup', function(evt, $form) {
|
||
if( !$form ) {
|
||
$form = $('form');
|
||
}
|
||
addSupportForValidationDependingOnCheckedInput($form);
|
||
});
|
||
|
||
|
||
function addSupportForCustomErrorMessageCallback(config) {
|
||
if (config &&
|
||
config.errorMessagePosition === 'custom' &&
|
||
typeof config.errorMessageCustom === 'function') {
|
||
|
||
$.formUtils.warn('Use of deprecated function errorMessageCustom, use config.submitErrorMessageCallback instead');
|
||
|
||
config.submitErrorMessageCallback = function($form, errorMessages) {
|
||
config.errorMessageCustom(
|
||
$form,
|
||
config.language.errorTitle,
|
||
errorMessages,
|
||
config
|
||
);
|
||
};
|
||
}
|
||
}
|
||
|
||
function addSupportForElementReferenceInPositionParam(config) {
|
||
if (config.errorMessagePosition && typeof config.errorMessagePosition === 'object') {
|
||
$.formUtils.warn('Deprecated use of config parameter errorMessagePosition, use config.submitErrorMessageCallback instead');
|
||
var $errorMessageContainer = config.errorMessagePosition;
|
||
config.errorMessagePosition = 'top';
|
||
config.submitErrorMessageCallback = function() {
|
||
return $errorMessageContainer;
|
||
};
|
||
}
|
||
}
|
||
|
||
function addSupportForValidationDependingOnCheckedInput($form) {
|
||
var $inputsDependingOnCheckedInputs = $form.find('[data-validation-if-checked]');
|
||
if ($inputsDependingOnCheckedInputs.length) {
|
||
$.formUtils.warn(
|
||
'Detected use of attribute "data-validation-if-checked" which is '+
|
||
'deprecated. Use "data-validation-depends-on" provided by module "logic"'
|
||
);
|
||
}
|
||
|
||
$inputsDependingOnCheckedInputs
|
||
.on('beforeValidation', function() {
|
||
|
||
var $elem = $(this),
|
||
nameOfDependingInput = $elem.valAttr('if-checked');
|
||
|
||
// Set the boolean telling us that the validation depends
|
||
// on another input being checked
|
||
var $dependingInput = $('input[name="' + nameOfDependingInput + '"]', $form),
|
||
dependingInputIsChecked = $dependingInput.is(':checked'),
|
||
valueOfDependingInput = ($.formUtils.getValue($dependingInput) || '').toString(),
|
||
requiredValueOfDependingInput = $elem.valAttr('if-checked-value');
|
||
|
||
if (!dependingInputIsChecked || !(
|
||
!requiredValueOfDependingInput ||
|
||
requiredValueOfDependingInput === valueOfDependingInput
|
||
)) {
|
||
$elem.valAttr('skipped', true);
|
||
}
|
||
|
||
});
|
||
}
|
||
|
||
function convertDeprecatedLangCodeToISO6391(config) {
|
||
var deprecatedLangCodes = {
|
||
se: 'sv',
|
||
cz: 'cs',
|
||
dk: 'da'
|
||
};
|
||
|
||
if (config.lang in deprecatedLangCodes) {
|
||
var newLangCode = deprecatedLangCodes[config.lang];
|
||
$.formUtils.warn(
|
||
'Deprecated use of lang code "'+config.lang+'" use "'+newLangCode+'" instead'
|
||
);
|
||
config.lang = newLangCode;
|
||
}
|
||
}
|
||
|
||
})(jQuery);
|
||
|
||
/**
|
||
* Utility methods used for displaying error messages (attached to $.formUtils)
|
||
*/
|
||
(function ($) {
|
||
|
||
'use strict';
|
||
|
||
var dialogs = {
|
||
|
||
resolveErrorMessage: function($elem, validator, validatorName, conf, language) {
|
||
var errorMsgAttr = conf.validationErrorMsgAttribute + '-' + validatorName.replace('validate_', ''),
|
||
validationErrorMsg = $elem.attr(errorMsgAttr);
|
||
|
||
if (!validationErrorMsg) {
|
||
validationErrorMsg = $elem.attr(conf.validationErrorMsgAttribute);
|
||
if (!validationErrorMsg) {
|
||
if (typeof validator.errorMessageKey !== 'function') {
|
||
validationErrorMsg = language[validator.errorMessageKey];
|
||
}
|
||
else {
|
||
validationErrorMsg = language[validator.errorMessageKey(conf)];
|
||
}
|
||
if (!validationErrorMsg) {
|
||
validationErrorMsg = validator.errorMessage;
|
||
}
|
||
}
|
||
}
|
||
return validationErrorMsg;
|
||
},
|
||
getParentContainer: function ($elem) {
|
||
if ($elem.valAttr('error-msg-container')) {
|
||
return $($elem.valAttr('error-msg-container'));
|
||
} else {
|
||
var $parent = $elem.parent();
|
||
if($elem.attr('type') === 'checkbox' && $elem.closest('.checkbox').length) {
|
||
$parent = $elem.closest('.checkbox').parent();
|
||
} else if($elem.attr('type') === 'radio' && $elem.closest('.radio').length) {
|
||
$parent = $elem.closest('.radio').parent();
|
||
}
|
||
if($parent.closest('.input-group').length) {
|
||
$parent = $parent.closest('.input-group').parent();
|
||
}
|
||
return $parent;
|
||
}
|
||
},
|
||
applyInputErrorStyling: function ($input, conf) {
|
||
$input
|
||
.addClass(conf.errorElementClass)
|
||
.removeClass(conf.successElementClass);
|
||
|
||
this.getParentContainer($input)
|
||
.addClass(conf.inputParentClassOnError)
|
||
.removeClass(conf.inputParentClassOnSuccess);
|
||
|
||
if (conf.borderColorOnError !== '') {
|
||
$input.css('border-color', conf.borderColorOnError);
|
||
}
|
||
},
|
||
applyInputSuccessStyling: function($input, conf) {
|
||
$input.addClass(conf.successElementClass);
|
||
this.getParentContainer($input)
|
||
.addClass(conf.inputParentClassOnSuccess);
|
||
},
|
||
removeInputStylingAndMessage: function($input, conf) {
|
||
|
||
// Reset input css
|
||
$input
|
||
.removeClass(conf.successElementClass)
|
||
.removeClass(conf.errorElementClass)
|
||
.css('border-color', '');
|
||
|
||
var $parentContainer = dialogs.getParentContainer($input);
|
||
|
||
// Reset parent css
|
||
$parentContainer
|
||
.removeClass(conf.inputParentClassOnError)
|
||
.removeClass(conf.inputParentClassOnSuccess);
|
||
|
||
// Remove possible error message
|
||
if (typeof conf.inlineErrorMessageCallback === 'function') {
|
||
var $errorMessage = conf.inlineErrorMessageCallback($input, false, conf);
|
||
if ($errorMessage) {
|
||
$errorMessage.html('');
|
||
}
|
||
} else {
|
||
$parentContainer
|
||
.find('.' + conf.errorMessageClass)
|
||
.remove();
|
||
}
|
||
|
||
},
|
||
removeAllMessagesAndStyling: function($form, conf) {
|
||
|
||
// Remove error messages in top of form
|
||
if (typeof conf.submitErrorMessageCallback === 'function') {
|
||
var $errorMessagesInTopOfForm = conf.submitErrorMessageCallback($form, false, conf);
|
||
if ($errorMessagesInTopOfForm) {
|
||
$errorMessagesInTopOfForm.html('');
|
||
}
|
||
} else {
|
||
$form.find('.' + conf.errorMessageClass + '.alert').remove();
|
||
}
|
||
|
||
// Remove input css/messages
|
||
$form.find('.' + conf.errorElementClass + ',.' + conf.successElementClass).each(function() {
|
||
dialogs.removeInputStylingAndMessage($(this), conf);
|
||
});
|
||
},
|
||
setInlineMessage: function ($input, errorMsg, conf) {
|
||
|
||
this.applyInputErrorStyling($input, conf);
|
||
|
||
var custom = document.getElementById($input.attr('name') + '_err_msg'),
|
||
$messageContainer = false,
|
||
setErrorMessage = function ($elem) {
|
||
$.formUtils.$win.trigger('validationErrorDisplay', [$input, $elem]);
|
||
$elem.html(errorMsg);
|
||
},
|
||
addErrorToMessageContainer = function() {
|
||
var $found = false;
|
||
$messageContainer.find('.' + conf.errorMessageClass).each(function () {
|
||
if (this.inputReferer === $input[0]) {
|
||
$found = $(this);
|
||
return false;
|
||
}
|
||
});
|
||
if ($found) {
|
||
if (!errorMsg) {
|
||
$found.remove();
|
||
} else {
|
||
setErrorMessage($found);
|
||
}
|
||
} else if(errorMsg !== '') {
|
||
$message = $('<div class="' + conf.errorMessageClass + ' alert"></div>');
|
||
setErrorMessage($message);
|
||
$message[0].inputReferer = $input[0];
|
||
$messageContainer.prepend($message);
|
||
}
|
||
},
|
||
$message;
|
||
|
||
if (custom) {
|
||
// Todo: remove in 3.0
|
||
$.formUtils.warn('Using deprecated element reference ' + custom.id);
|
||
$messageContainer = $(custom);
|
||
addErrorToMessageContainer();
|
||
} else if (typeof conf.inlineErrorMessageCallback === 'function') {
|
||
$messageContainer = conf.inlineErrorMessageCallback($input, errorMsg, conf);
|
||
if (!$messageContainer) {
|
||
// Error display taken care of by inlineErrorMessageCallback
|
||
return;
|
||
}
|
||
addErrorToMessageContainer();
|
||
} else {
|
||
var $parent = this.getParentContainer($input);
|
||
$message = $parent.find('.' + conf.errorMessageClass + '.help-block');
|
||
if ($message.length === 0) {
|
||
$message = $('<span></span>').addClass('help-block').addClass(conf.errorMessageClass);
|
||
$message.appendTo($parent);
|
||
}
|
||
setErrorMessage($message);
|
||
}
|
||
},
|
||
setMessageInTopOfForm: function ($form, errorMessages, conf, lang) {
|
||
var view = '<div class="{errorMessageClass} alert alert-danger">'+
|
||
'<strong>{errorTitle}</strong>'+
|
||
'<ul>{fields}</ul>'+
|
||
'</div>',
|
||
$container = false;
|
||
|
||
if (typeof conf.submitErrorMessageCallback === 'function') {
|
||
$container = conf.submitErrorMessageCallback($form, errorMessages, conf);
|
||
if (!$container) {
|
||
// message display taken care of by callback
|
||
return;
|
||
}
|
||
}
|
||
|
||
var viewParams = {
|
||
errorTitle: lang.errorTitle,
|
||
fields: '',
|
||
errorMessageClass: conf.errorMessageClass
|
||
};
|
||
|
||
$.each(errorMessages, function (i, msg) {
|
||
viewParams.fields += '<li>'+msg+'</li>';
|
||
});
|
||
|
||
$.each(viewParams, function(param, value) {
|
||
view = view.replace('{'+param+'}', value);
|
||
});
|
||
|
||
if ($container) {
|
||
$container.html(view);
|
||
} else {
|
||
$form.children().eq(0).before($(view));
|
||
}
|
||
}
|
||
};
|
||
|
||
$.formUtils = $.extend($.formUtils || {}, {
|
||
dialogs: dialogs
|
||
});
|
||
|
||
})(jQuery);
|
||
|
||
/**
|
||
* File declaring all methods if this plugin which is applied to $.fn.
|
||
*/
|
||
(function($, window, undefined) {
|
||
|
||
'use strict';
|
||
|
||
var _helpers = 0;
|
||
|
||
|
||
/**
|
||
* Assigns validateInputOnBlur function to elements blur event
|
||
*
|
||
* @param {Object} language Optional, will override $.formUtils.LANG
|
||
* @param {Object} conf Optional, will override the default settings
|
||
* @return {jQuery}
|
||
*/
|
||
$.fn.validateOnBlur = function (language, conf) {
|
||
var $form = this,
|
||
$elems = this.find('*[data-validation]');
|
||
|
||
$elems.each(function(){
|
||
var $this = $(this);
|
||
if ($this.is('[type=radio]')){
|
||
var $additionals = $form.find('[type=radio][name="' + $this.attr('name') + '"]');
|
||
$additionals.bind('blur.validation', function(){
|
||
$this.validateInputOnBlur(language, conf, true, 'blur');
|
||
});
|
||
if (conf.validateCheckboxRadioOnClick) {
|
||
$additionals.bind('click.validation', function () {
|
||
$this.validateInputOnBlur(language, conf, true, 'click');
|
||
});
|
||
}
|
||
}
|
||
});
|
||
|
||
$elems.bind('blur.validation', function () {
|
||
$(this).validateInputOnBlur(language, conf, true, 'blur');
|
||
});
|
||
|
||
if (conf.validateCheckboxRadioOnClick) {
|
||
// bind click event to validate on click for radio & checkboxes for nice UX
|
||
this.find('input[type=checkbox][data-validation],input[type=radio][data-validation]')
|
||
.bind('click.validation', function () {
|
||
$(this).validateInputOnBlur(language, conf, true, 'click');
|
||
});
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
/*
|
||
* Assigns validateInputOnBlur function to elements custom event
|
||
* @param {Object} language Optional, will override $.formUtils.LANG
|
||
* @param {Object} settings Optional, will override the default settings
|
||
* * @return {jQuery}
|
||
*/
|
||
$.fn.validateOnEvent = function (language, config) {
|
||
if(this.length === 0) {
|
||
return;
|
||
}
|
||
|
||
var $elements = this[0].nodeName === 'FORM' ? this.find('*[data-validation-event]') : this;
|
||
$elements
|
||
.each(function () {
|
||
var $el = $(this),
|
||
etype = $el.valAttr('event');
|
||
if (etype) {
|
||
$el
|
||
.unbind(etype + '.validation')
|
||
.bind(etype + '.validation', function (evt) {
|
||
if( (evt || {}).keyCode !== 9 ) {
|
||
$(this).validateInputOnBlur(language, config, true, etype);
|
||
}
|
||
});
|
||
}
|
||
});
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* fade in help message when input gains focus
|
||
* fade out when input loses focus
|
||
* <input data-help="The info that I want to display for the user when input is focused" ... />
|
||
*
|
||
* @param {String} attrName - Optional, default is data-help
|
||
* @return {jQuery}
|
||
*/
|
||
$.fn.showHelpOnFocus = function (attrName) {
|
||
if (!attrName) {
|
||
attrName = 'data-validation-help';
|
||
}
|
||
|
||
// Add help text listeners
|
||
this.find('textarea,input').each(function () {
|
||
var $elem = $(this),
|
||
className = 'jquery_form_help_' + (++_helpers),
|
||
help = $elem.attr(attrName);
|
||
|
||
// Reset
|
||
$elem
|
||
.removeClass('has-help-text')
|
||
.unbind('focus.help')
|
||
.unbind('blur.help');
|
||
|
||
if (help) {
|
||
$elem
|
||
.addClass('has-help-txt')
|
||
.bind('focus.help', function () {
|
||
var $help = $elem.parent().find('.' + className);
|
||
if ($help.length === 0) {
|
||
$help = $('<span />')
|
||
.addClass(className)
|
||
.addClass('help')
|
||
.addClass('help-block') // twitter bs
|
||
.text(help)
|
||
.hide();
|
||
|
||
$elem.after($help);
|
||
}
|
||
$help.fadeIn();
|
||
})
|
||
.bind('blur.help', function () {
|
||
$(this)
|
||
.parent()
|
||
.find('.' + className)
|
||
.fadeOut('slow');
|
||
});
|
||
}
|
||
});
|
||
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* @param {Function} cb
|
||
* @param {Object} [conf]
|
||
* @param {Object} [lang]
|
||
*/
|
||
$.fn.validate = function(cb, conf, lang) {
|
||
var language = $.extend({}, $.formUtils.LANG, lang || {});
|
||
this.each(function() {
|
||
var $elem = $(this),
|
||
closestFormElem = $elem.closest('form').get(0) || {},
|
||
formDefaultConfig = closestFormElem.validationConfig || $.formUtils.defaultConfig();
|
||
|
||
$elem.one('validation', function(evt, isValid) {
|
||
if ( typeof cb === 'function' ) {
|
||
cb(isValid, this, evt);
|
||
}
|
||
});
|
||
|
||
$elem.validateInputOnBlur(
|
||
language,
|
||
$.extend({}, formDefaultConfig, conf || {}),
|
||
true
|
||
);
|
||
});
|
||
};
|
||
|
||
/**
|
||
* Tells whether or not validation of this input will have to postpone the form submit ()
|
||
* @returns {Boolean}
|
||
*/
|
||
$.fn.willPostponeValidation = function() {
|
||
return (this.valAttr('suggestion-nr') ||
|
||
this.valAttr('postpone') ||
|
||
this.hasClass('hasDatepicker')) &&
|
||
!window.postponedValidation;
|
||
};
|
||
|
||
/**
|
||
* Validate single input when it loses focus
|
||
* shows error message in a span element
|
||
* that is appended to the parent element
|
||
*
|
||
* @param {Object} [language] Optional, will override $.formUtils.LANG
|
||
* @param {Object} [conf] Optional, will override the default settings
|
||
* @param {Boolean} attachKeyupEvent Optional
|
||
* @param {String} eventContext
|
||
* @return {jQuery}
|
||
*/
|
||
$.fn.validateInputOnBlur = function (language, conf, attachKeyupEvent, eventContext) {
|
||
|
||
$.formUtils.eventType = eventContext;
|
||
|
||
if ( this.willPostponeValidation() ) {
|
||
// This validation has to be postponed
|
||
var _self = this,
|
||
postponeTime = this.valAttr('postpone') || 200;
|
||
|
||
window.postponedValidation = function () {
|
||
_self.validateInputOnBlur(language, conf, attachKeyupEvent, eventContext);
|
||
window.postponedValidation = false;
|
||
};
|
||
|
||
setTimeout(function () {
|
||
if (window.postponedValidation) {
|
||
window.postponedValidation();
|
||
}
|
||
}, postponeTime);
|
||
|
||
return this;
|
||
}
|
||
|
||
language = $.extend({}, $.formUtils.LANG, language || {});
|
||
$.formUtils.dialogs.removeInputStylingAndMessage(this, conf);
|
||
|
||
var $elem = this,
|
||
$form = $elem.closest('form'),
|
||
result = $.formUtils.validateInput(
|
||
$elem,
|
||
language,
|
||
conf,
|
||
$form,
|
||
eventContext
|
||
);
|
||
|
||
var reValidate = function() {
|
||
$elem.validateInputOnBlur(language, conf, false, 'blur.revalidated');
|
||
};
|
||
|
||
if (eventContext === 'blur') {
|
||
$elem
|
||
.unbind('validation.revalidate', reValidate)
|
||
.one('validation.revalidate', reValidate);
|
||
}
|
||
|
||
if (attachKeyupEvent) {
|
||
$elem.removeKeyUpValidation();
|
||
}
|
||
|
||
if (result.shouldChangeDisplay) {
|
||
if (result.isValid) {
|
||
$.formUtils.dialogs.applyInputSuccessStyling($elem, conf);
|
||
} else {
|
||
$.formUtils.dialogs.setInlineMessage($elem, result.errorMsg, conf);
|
||
}
|
||
}
|
||
|
||
if (!result.isValid && attachKeyupEvent) {
|
||
$elem.validateOnKeyUp(language, conf);
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Validate element on keyup-event
|
||
*/
|
||
$.fn.validateOnKeyUp = function(language, conf) {
|
||
this.each(function() {
|
||
var $input = $(this);
|
||
if (!$input.valAttr('has-keyup-event')) {
|
||
$input
|
||
.valAttr('has-keyup-event', 'true')
|
||
.bind('keyup.validation', function (evt) {
|
||
if( evt.keyCode !== 9 ) {
|
||
$input.validateInputOnBlur(language, conf, false, 'keyup');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Remove validation on keyup
|
||
*/
|
||
$.fn.removeKeyUpValidation = function() {
|
||
this.each(function() {
|
||
$(this)
|
||
.valAttr('has-keyup-event', false)
|
||
.unbind('keyup.validation');
|
||
});
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Short hand for fetching/adding/removing element attributes
|
||
* prefixed with 'data-validation-'
|
||
*
|
||
* @param {String} name
|
||
* @param {String|Boolean} [val]
|
||
* @return {String|undefined|jQuery}
|
||
* @protected
|
||
*/
|
||
$.fn.valAttr = function (name, val) {
|
||
if (val === undefined) {
|
||
return this.attr('data-validation-' + name);
|
||
} else if (val === false || val === null) {
|
||
return this.removeAttr('data-validation-' + name);
|
||
} else {
|
||
name = ((name.length > 0) ? '-' + name : '');
|
||
return this.attr('data-validation' + name, val);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Function that validates all inputs in active form
|
||
*
|
||
* @param {Object} [language]
|
||
* @param {Object} [conf]
|
||
* @param {Boolean} [displayError] Defaults to true
|
||
*/
|
||
$.fn.isValid = function (language, conf, displayError) {
|
||
|
||
if ($.formUtils.isLoadingModules) {
|
||
var $self = this;
|
||
setTimeout(function () {
|
||
$self.isValid(language, conf, displayError);
|
||
}, 200);
|
||
return null;
|
||
}
|
||
|
||
conf = $.extend({}, $.formUtils.defaultConfig(), conf || {});
|
||
language = $.extend({}, $.formUtils.LANG, language || {});
|
||
displayError = displayError !== false;
|
||
|
||
if ($.formUtils.errorDisplayPreventedWhenHalted) {
|
||
// isValid() was called programmatically with argument displayError set
|
||
// to false when the validation was halted by any of the validators
|
||
delete $.formUtils.errorDisplayPreventedWhenHalted;
|
||
displayError = false;
|
||
}
|
||
|
||
/**
|
||
* Adds message to error message stack if not already in the message stack
|
||
*
|
||
* @param {String} mess
|
||
* @para {jQuery} $elem
|
||
*/
|
||
var addErrorMessage = function (mess, $elem) {
|
||
if ($.inArray(mess, errorMessages) < 0) {
|
||
errorMessages.push(mess);
|
||
}
|
||
errorInputs.push($elem);
|
||
$elem.valAttr('current-error', mess);
|
||
if (displayError) {
|
||
$.formUtils.dialogs.applyInputErrorStyling($elem, conf);
|
||
}
|
||
},
|
||
|
||
/** Holds inputs (of type checkox or radio) already validated, to prevent recheck of mulitple checkboxes & radios */
|
||
checkedInputs = [],
|
||
|
||
/** Error messages for this validation */
|
||
errorMessages = [],
|
||
|
||
/** Input elements which value was not valid */
|
||
errorInputs = [],
|
||
|
||
/** Form instance */
|
||
$form = this,
|
||
|
||
/**
|
||
* Tells whether or not to validate element with this name and of this type
|
||
*
|
||
* @param {String} name
|
||
* @param {String} type
|
||
* @return {Boolean}
|
||
*/
|
||
ignoreInput = function (name, type) {
|
||
if (type === 'submit' || type === 'button' || type === 'reset') {
|
||
return true;
|
||
}
|
||
return $.inArray(name, conf.ignore || []) > -1;
|
||
};
|
||
|
||
// Reset style and remove error class
|
||
if (displayError) {
|
||
$.formUtils.dialogs.removeAllMessagesAndStyling($form, conf);
|
||
}
|
||
|
||
// Validate element values
|
||
$form.find('input,textarea,select').filter(':not([type="submit"],[type="button"])').each(function () {
|
||
var $elem = $(this),
|
||
elementType = $elem.attr('type'),
|
||
isCheckboxOrRadioBtn = elementType === 'radio' || elementType === 'checkbox',
|
||
elementName = $elem.attr('name');
|
||
|
||
if (!ignoreInput(elementName, elementType) && (!isCheckboxOrRadioBtn || $.inArray(elementName, checkedInputs) < 0)) {
|
||
|
||
if (isCheckboxOrRadioBtn) {
|
||
checkedInputs.push(elementName);
|
||
}
|
||
|
||
var result = $.formUtils.validateInput(
|
||
$elem,
|
||
language,
|
||
conf,
|
||
$form,
|
||
'submit'
|
||
);
|
||
|
||
if (!result.isValid) {
|
||
addErrorMessage(result.errorMsg, $elem);
|
||
} else if (result.isValid && result.shouldChangeDisplay) {
|
||
$elem.valAttr('current-error', false);
|
||
$.formUtils.dialogs.applyInputSuccessStyling($elem, conf);
|
||
}
|
||
}
|
||
|
||
});
|
||
|
||
// Run validation callback
|
||
if (typeof conf.onValidate === 'function') {
|
||
var errors = conf.onValidate($form);
|
||
if ($.isArray(errors)) {
|
||
$.each(errors, function (i, err) {
|
||
addErrorMessage(err.message, err.element);
|
||
});
|
||
}
|
||
else if (errors && errors.element && errors.message) {
|
||
addErrorMessage(errors.message, errors.element);
|
||
}
|
||
}
|
||
|
||
// Reset form validation flag
|
||
$.formUtils.isValidatingEntireForm = false;
|
||
|
||
// Validation failed
|
||
if (errorInputs.length > 0) {
|
||
if (displayError) {
|
||
if (conf.errorMessagePosition === 'top') {
|
||
$.formUtils.dialogs.setMessageInTopOfForm($form, errorMessages, conf, language);
|
||
} else {
|
||
$.each(errorInputs, function (i, $input) {
|
||
$.formUtils.dialogs.setInlineMessage($input, $input.valAttr('current-error'), conf);
|
||
});
|
||
}
|
||
if (conf.scrollToTopOnError) {
|
||
$.formUtils.$win.scrollTop($form.offset().top - 20);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!displayError && $.formUtils.haltValidation) {
|
||
$.formUtils.errorDisplayPreventedWhenHalted = true;
|
||
}
|
||
|
||
return errorInputs.length === 0 && !$.formUtils.haltValidation;
|
||
};
|
||
|
||
/**
|
||
* Plugin for displaying input length restriction
|
||
*/
|
||
$.fn.restrictLength = function (maxLengthElement) {
|
||
new $.formUtils.lengthRestriction(this, maxLengthElement);
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Add suggestion dropdown to inputs having data-suggestions with a comma
|
||
* separated string with suggestions
|
||
* @param {Array} [settings]
|
||
* @returns {jQuery}
|
||
*/
|
||
$.fn.addSuggestions = function (settings) {
|
||
var sugs = false;
|
||
this.find('input').each(function () {
|
||
var $field = $(this);
|
||
|
||
sugs = $.split($field.attr('data-suggestions'));
|
||
|
||
if (sugs.length > 0 && !$field.hasClass('has-suggestions')) {
|
||
$.formUtils.suggest($field, sugs, settings);
|
||
$field.addClass('has-suggestions');
|
||
}
|
||
});
|
||
return this;
|
||
};
|
||
|
||
|
||
})(jQuery, window);
|
||
|
||
/**
|
||
* Utility methods used for handling loading of modules (attached to $.formUtils)
|
||
*/
|
||
(function($) {
|
||
|
||
'use strict';
|
||
|
||
$.formUtils = $.extend($.formUtils || {}, {
|
||
|
||
/**
|
||
* @var {Boolean}
|
||
*/
|
||
isLoadingModules: false,
|
||
|
||
/**
|
||
* @var {Object}
|
||
*/
|
||
loadedModules: {},
|
||
|
||
/**
|
||
* @param {String} name
|
||
*/
|
||
registerLoadedModule: function (name) {
|
||
this.loadedModules[$.trim(name).toLowerCase()] = true;
|
||
},
|
||
|
||
/**
|
||
* @param {String} name
|
||
* @return {Boolean}
|
||
*/
|
||
hasLoadedModule: function (name) {
|
||
return $.trim(name).toLowerCase() in this.loadedModules;
|
||
},
|
||
|
||
/**
|
||
* @example
|
||
* $.formUtils.loadModules('date, security.dev');
|
||
*
|
||
* Will load the scripts date.js and security.dev.js from the
|
||
* directory where this script resides. If you want to load
|
||
* the modules from another directory you can use the
|
||
* path argument.
|
||
*
|
||
* The script will be cached by the browser unless the module
|
||
* name ends with .dev
|
||
*
|
||
* @param {String} modules - Comma separated string with module file names (no directory nor file extension)
|
||
* @param {String} [path] - Path where the module files are located if their not in the same directory as the core modules
|
||
* @param {function} [callback] - Callback invoked when all modules are loaded
|
||
*/
|
||
loadModules: function (modules, path, callback) {
|
||
|
||
if ($.formUtils.isLoadingModules) {
|
||
setTimeout(function () {
|
||
$.formUtils.loadModules(modules, path, callback);
|
||
}, 100);
|
||
return;
|
||
}
|
||
|
||
var loadModuleScripts = function (modules, path) {
|
||
|
||
var moduleList = $.split(modules),
|
||
numModules = moduleList.length,
|
||
moduleLoadedCallback = function () {
|
||
numModules--;
|
||
if (numModules === 0) {
|
||
$.formUtils.isLoadingModules = false;
|
||
if (typeof callback === 'function') {
|
||
callback();
|
||
}
|
||
}
|
||
};
|
||
|
||
if (numModules > 0) {
|
||
$.formUtils.isLoadingModules = true;
|
||
}
|
||
|
||
var cacheSuffix = '?_=' + ( new Date().getTime() ),
|
||
appendToElement = document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0];
|
||
|
||
$.each(moduleList, function (i, modName) {
|
||
modName = $.trim(modName);
|
||
if (modName.length === 0 || $.formUtils.hasLoadedModule(modName)) {
|
||
moduleLoadedCallback();
|
||
} else {
|
||
var scriptUrl = path + modName + (modName.slice(-3) === '.js' ? '' : '.js'),
|
||
script = document.createElement('SCRIPT');
|
||
|
||
if (typeof define === 'function' && define.amd) {
|
||
require([scriptUrl + ( scriptUrl.slice(-7) === '.dev.js' ? cacheSuffix : '' )], moduleLoadedCallback);
|
||
} else {
|
||
// Load the script
|
||
script.type = 'text/javascript';
|
||
script.onload = moduleLoadedCallback;
|
||
script.src = scriptUrl + ( scriptUrl.slice(-7) === '.dev.js' ? cacheSuffix : '' );
|
||
script.onerror = function() {
|
||
$.formUtils.warn('Unable to load form validation module '+scriptUrl, true);
|
||
moduleLoadedCallback();
|
||
};
|
||
script.onreadystatechange = function () {
|
||
// IE 7 fix
|
||
if (this.readyState === 'complete' || this.readyState === 'loaded') {
|
||
moduleLoadedCallback();
|
||
// Handle memory leak in IE
|
||
this.onload = null;
|
||
this.onreadystatechange = null;
|
||
}
|
||
};
|
||
appendToElement.appendChild(script);
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
if (path) {
|
||
loadModuleScripts(modules, path);
|
||
} else {
|
||
var findScriptPathAndLoadModules = function () {
|
||
var foundPath = false;
|
||
$('script[src*="form-validator"]').each(function () {
|
||
var isScriptFromPluginNodeModulesDirectory = this.src.split('form-validator')[1].split('node_modules').length > 1;
|
||
if (!isScriptFromPluginNodeModulesDirectory) {
|
||
foundPath = this.src.substr(0, this.src.lastIndexOf('/')) + '/';
|
||
if (foundPath === '/') {
|
||
foundPath = '';
|
||
}
|
||
return false;
|
||
}
|
||
});
|
||
|
||
if (foundPath !== false) {
|
||
loadModuleScripts(modules, foundPath);
|
||
return true;
|
||
}
|
||
return false;
|
||
};
|
||
|
||
if (!findScriptPathAndLoadModules()) {
|
||
$(function () {
|
||
var hasLoadedModuleScripts = findScriptPathAndLoadModules();
|
||
if (!hasLoadedModuleScripts) {
|
||
// The modules may have been inserted via a minified script
|
||
if (typeof callback === 'function') {
|
||
callback();
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
});
|
||
|
||
})(jQuery);
|
||
|
||
/**
|
||
* Setup function for the plugin
|
||
*/
|
||
(function ($) {
|
||
|
||
'use strict';
|
||
|
||
|
||
/**
|
||
* A bit smarter split function
|
||
* delimiter can be space, comma, dash or pipe
|
||
* @param {String} val
|
||
* @param {Function|String} [callback]
|
||
* @param {Boolean} [allowSpaceAsDelimiter]
|
||
* @returns {Array|void}
|
||
*/
|
||
$.split = function (val, callback, allowSpaceAsDelimiter) {
|
||
// default to true
|
||
allowSpaceAsDelimiter = allowSpaceAsDelimiter === undefined || allowSpaceAsDelimiter === true;
|
||
var pattern = '[,|'+(allowSpaceAsDelimiter ? '\\s':'')+'-]\\s*',
|
||
regex = new RegExp(pattern, 'g');
|
||
if (typeof callback !== 'function') {
|
||
// return array
|
||
if (!val) {
|
||
return [];
|
||
}
|
||
var values = [];
|
||
$.each(val.split(callback ? callback : regex),
|
||
function (i, str) {
|
||
str = $.trim(str);
|
||
if (str.length) {
|
||
values.push(str);
|
||
}
|
||
}
|
||
);
|
||
return values;
|
||
} else if (val) {
|
||
// exec callback func on each
|
||
$.each(val.split(regex),
|
||
function (i, str) {
|
||
str = $.trim(str);
|
||
if (str.length) {
|
||
return callback(str, i);
|
||
}
|
||
}
|
||
);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Short hand function that makes the validation setup require less code
|
||
* @param conf
|
||
*/
|
||
$.validate = function (conf) {
|
||
|
||
var defaultConf = $.extend($.formUtils.defaultConfig(), {
|
||
form: 'form',
|
||
validateOnEvent: false,
|
||
validateOnBlur: true,
|
||
validateCheckboxRadioOnClick: true,
|
||
showHelpOnFocus: true,
|
||
addSuggestions: true,
|
||
modules: '',
|
||
onModulesLoaded: null,
|
||
language: false,
|
||
onSuccess: false,
|
||
onError: false,
|
||
onElementValidate: false
|
||
});
|
||
|
||
conf = $.extend(defaultConf, conf || {});
|
||
|
||
$(window).trigger('formValidationPluginInit', [conf]);
|
||
|
||
if( conf.lang && conf.lang !== 'en' ) {
|
||
var langModule = 'lang/'+conf.lang+'.js';
|
||
conf.modules += conf.modules.length ? ','+langModule : langModule;
|
||
}
|
||
|
||
// Add validation to forms
|
||
$(conf.form).each(function (i, form) {
|
||
|
||
// Make a reference to the config for this form
|
||
form.validationConfig = conf;
|
||
|
||
// Trigger jQuery event that we're about to setup validation
|
||
var $form = $(form);
|
||
$form.trigger('formValidationSetup', [$form, conf]);
|
||
|
||
// Remove classes and event handlers that might have been
|
||
// added by a previous call to $.validate
|
||
$form.find('.has-help-txt')
|
||
.unbind('focus.validation')
|
||
.unbind('blur.validation');
|
||
|
||
$form
|
||
.removeClass('has-validation-callback')
|
||
.unbind('submit.validation')
|
||
.unbind('reset.validation')
|
||
.find('input[data-validation],textarea[data-validation]')
|
||
.unbind('blur.validation');
|
||
|
||
// Validate when submitted
|
||
$form.bind('submit.validation', function (evt) {
|
||
|
||
var $form = $(this),
|
||
stop = function() {
|
||
evt.stopImmediatePropagation();
|
||
return false;
|
||
};
|
||
|
||
if ($.formUtils.haltValidation) {
|
||
// pressing several times on submit button while validation is halted
|
||
return stop();
|
||
}
|
||
|
||
if ($.formUtils.isLoadingModules) {
|
||
setTimeout(function () {
|
||
$form.trigger('submit.validation');
|
||
}, 200);
|
||
return stop();
|
||
}
|
||
|
||
var valid = $form.isValid(conf.language, conf);
|
||
if ($.formUtils.haltValidation) {
|
||
// Validation got halted by one of the validators
|
||
return stop();
|
||
} else {
|
||
if (valid && typeof conf.onSuccess === 'function') {
|
||
var callbackResponse = conf.onSuccess($form);
|
||
if (callbackResponse === false) {
|
||
return stop();
|
||
}
|
||
} else if (!valid && typeof conf.onError === 'function') {
|
||
conf.onError($form);
|
||
return stop();
|
||
} else {
|
||
return valid ? true : stop();
|
||
}
|
||
}
|
||
})
|
||
.bind('reset.validation', function () {
|
||
$.formUtils.dialogs.removeAllMessagesAndStyling($form, conf);
|
||
})
|
||
.addClass('has-validation-callback');
|
||
|
||
if (conf.showHelpOnFocus) {
|
||
$form.showHelpOnFocus();
|
||
}
|
||
if (conf.addSuggestions) {
|
||
$form.addSuggestions();
|
||
}
|
||
if (conf.validateOnBlur) {
|
||
$form.validateOnBlur(conf.language, conf);
|
||
$form.bind('html5ValidationAttrsFound', function () {
|
||
$form.validateOnBlur(conf.language, conf);
|
||
});
|
||
}
|
||
if (conf.validateOnEvent) {
|
||
$form.validateOnEvent(conf.language, conf);
|
||
}
|
||
});
|
||
|
||
if (conf.modules !== '') {
|
||
$.formUtils.loadModules(conf.modules, null, function() {
|
||
if (typeof conf.onModulesLoaded === 'function') {
|
||
conf.onModulesLoaded();
|
||
}
|
||
var $form = typeof conf.form === 'string' ? $(conf.form) : conf.form;
|
||
$.formUtils.$win.trigger('validatorsLoaded', [$form, conf]);
|
||
});
|
||
}
|
||
};
|
||
|
||
})(jQuery);
|
||
|
||
/**
|
||
* Utility methods and properties attached to $.formUtils
|
||
*/
|
||
(function($, window) {
|
||
|
||
'use strict';
|
||
|
||
var $win = $(window);
|
||
|
||
$.formUtils = $.extend($.formUtils || {}, {
|
||
|
||
$win: $win,
|
||
|
||
/**
|
||
* Default config for $(...).isValid();
|
||
*/
|
||
defaultConfig: function () {
|
||
return {
|
||
ignore: [], // Names of inputs not to be validated even though `validationRuleAttribute` containing the validation rules tells us to
|
||
errorElementClass: 'error', // Class that will be put on elements which value is invalid
|
||
successElementClass: 'valid', // Class that will be put on elements that has been validated with success
|
||
borderColorOnError: '#b94a48', // Border color of elements which value is invalid, empty string to not change border color
|
||
errorMessageClass: 'form-error', // class name of div containing error messages when validation fails
|
||
validationRuleAttribute: 'data-validation', // name of the attribute holding the validation rules
|
||
validationErrorMsgAttribute: 'data-validation-error-msg', // define custom err msg inline with element
|
||
errorMessagePosition: 'inline', // Can be either "top" or "inline"
|
||
errorMessageTemplate: {
|
||
container: '<div class="{errorMessageClass} alert alert-danger">{messages}</div>',
|
||
messages: '<strong>{errorTitle}</strong><ul>{fields}</ul>',
|
||
field: '<li>{msg}</li>'
|
||
},
|
||
scrollToTopOnError: true,
|
||
dateFormat: 'yyyy-mm-dd',
|
||
addValidClassOnAll: false, // whether or not to apply class="valid" even if the input wasn't validated
|
||
decimalSeparator: '.',
|
||
inputParentClassOnError: 'has-error', // twitter-bootstrap default class name
|
||
inputParentClassOnSuccess: 'has-success', // twitter-bootstrap default class name
|
||
validateHiddenInputs: false, // whether or not hidden inputs should be validated
|
||
inlineErrorMessageCallback: false,
|
||
submitErrorMessageCallback: false
|
||
};
|
||
},
|
||
|
||
/**
|
||
* Available validators
|
||
*/
|
||
validators: {},
|
||
|
||
/**
|
||
* Available sanitizers
|
||
*/
|
||
sanitizers: {},
|
||
|
||
/**
|
||
* Events triggered by form validator
|
||
*/
|
||
_events: {load: [], valid: [], invalid: []},
|
||
|
||
/**
|
||
* Setting this property to true during validation will
|
||
* stop further validation from taking place and form will
|
||
* not be sent
|
||
*/
|
||
haltValidation: false,
|
||
|
||
/**
|
||
* Function for adding a validator
|
||
* @see $.formUtils.addAsyncValidator (async.js)
|
||
* @param {Object} validator
|
||
*/
|
||
addValidator: function (validator) {
|
||
// prefix with "validate_" for backward compatibility reasons
|
||
var name = validator.name.indexOf('validate_') === 0 ? validator.name : 'validate_' + validator.name;
|
||
if (validator.validateOnKeyUp === undefined) {
|
||
validator.validateOnKeyUp = true;
|
||
}
|
||
this.validators[name] = validator;
|
||
},
|
||
|
||
/**
|
||
* Function for adding a sanitizer
|
||
* @param {Object} sanitizer
|
||
*/
|
||
addSanitizer: function (sanitizer) {
|
||
this.sanitizers[sanitizer.name] = sanitizer;
|
||
},
|
||
|
||
/**
|
||
* Warn user via the console if available
|
||
*/
|
||
warn: function(msg, fallbackOnAlert) {
|
||
if( 'console' in window ) {
|
||
if( typeof window.console.warn === 'function' ) {
|
||
window.console.warn(msg);
|
||
} else if( typeof window.console.log === 'function' ) {
|
||
window.console.log(msg);
|
||
}
|
||
} else if (fallbackOnAlert) {
|
||
// This is for some old IE version...
|
||
alert(msg);
|
||
}
|
||
},
|
||
|
||
/**
|
||
* Same as input $.fn.val() but also supporting input of typ radio or checkbox
|
||
* @example
|
||
*
|
||
* $.formUtils.getValue('.myRadioButtons', $('#some-form'));
|
||
* $.formUtils.getValue($('#some-form').find('.check-boxes'));
|
||
*
|
||
* @param query
|
||
* @param $parent
|
||
* @returns {String|Boolean}
|
||
*/
|
||
getValue: function(query, $parent) {
|
||
var $inputs = $parent ? $parent.find(query) : query;
|
||
if ($inputs.length > 0 ) {
|
||
var type = $inputs.eq(0).attr('type');
|
||
if (type === 'radio' || type === 'checkbox') {
|
||
return $inputs.filter(':checked').val() || '';
|
||
} else {
|
||
return $inputs.val() || '';
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
|
||
/**
|
||
* Validate the value of given element according to the validation rules
|
||
* found in the attribute data-validation. Will return an object representing
|
||
* a validation result, having the props shouldChangeDisplay, isValid and errorMsg
|
||
* @param {jQuery} $elem
|
||
* @param {Object} language ($.formUtils.LANG)
|
||
* @param {Object} conf
|
||
* @param {jQuery} $form
|
||
* @param {String} [eventContext]
|
||
* @return {Object}
|
||
*/
|
||
validateInput: function ($elem, language, conf, $form, eventContext) {
|
||
|
||
conf = conf || $.formUtils.defaultConfig();
|
||
language = language || $.formUtils.LANG;
|
||
|
||
if (!$form.length) {
|
||
$form = $elem.parent();
|
||
}
|
||
|
||
var value = this.getValue($elem);
|
||
|
||
$elem
|
||
.valAttr('skipped', false)
|
||
.one('beforeValidation', function() {
|
||
// Skip input because its hidden or disabled
|
||
// Doing this in a callback makes it possible for others to prevent the default
|
||
// behaviour by binding to the same event and call evt.stopImmediatePropagation()
|
||
if ($elem.attr('disabled') || (!$elem.is(':visible') && !conf.validateHiddenInputs)) {
|
||
$elem.valAttr('skipped', 1);
|
||
}
|
||
})
|
||
.trigger('beforeValidation', [value, language, conf]);
|
||
|
||
var inputIsOptional = $elem.valAttr('optional') === 'true',
|
||
skipBecauseItsEmpty = !value && inputIsOptional,
|
||
validationRules = $elem.attr(conf.validationRuleAttribute),
|
||
isValid = true,
|
||
errorMsg = '',
|
||
result = {isValid: true, shouldChangeDisplay:true, errorMsg:''};
|
||
|
||
// For input type="number", browsers attempt to parse the entered value into a number.
|
||
// If the input is not numeric, browsers handle the situation differently:
|
||
// Chrome 48 simply disallows non-numeric input; FF 44 clears out the input box on blur;
|
||
// Safari 5 parses the entered string to find a leading number.
|
||
// If the input fails browser validation, the browser sets the input value equal to an empty string.
|
||
// Therefore, we cannot distinguish (apart from hacks) between an empty input type="text" and one with a
|
||
// value that can't be parsed by the browser.
|
||
|
||
if (!validationRules || skipBecauseItsEmpty || $elem.valAttr('skipped')) {
|
||
result.shouldChangeDisplay = conf.addValidClassOnAll;
|
||
return result;
|
||
}
|
||
|
||
// Filter out specified characters
|
||
var ignore = $elem.valAttr('ignore');
|
||
if (ignore) {
|
||
$.each(ignore.split(''), function(i, character) {
|
||
value = value.replace(new RegExp('\\'+character, 'g'), '');
|
||
});
|
||
}
|
||
|
||
$.split(validationRules, function (rule) {
|
||
|
||
if (rule.indexOf('validate_') !== 0) {
|
||
rule = 'validate_' + rule;
|
||
}
|
||
|
||
var validator = $.formUtils.validators[rule];
|
||
|
||
if (validator) {
|
||
|
||
// special change of element for checkbox_group rule
|
||
if (rule === 'validate_checkbox_group') {
|
||
// set element to first in group, so error msg attr doesn't need to be set on all elements in group
|
||
$elem = $form.find('[name="' + $elem.attr('name') + '"]:eq(0)');
|
||
}
|
||
|
||
if (eventContext !== 'keyup' || validator.validateOnKeyUp) {
|
||
// A validator can prevent itself from getting triggered on keyup
|
||
isValid = validator.validatorFunction(value, $elem, conf, language, $form, eventContext);
|
||
}
|
||
|
||
if (!isValid) {
|
||
if (conf.validateOnBlur) {
|
||
$elem.validateOnKeyUp(language, conf);
|
||
}
|
||
errorMsg = $.formUtils.dialogs.resolveErrorMessage($elem, validator, rule, conf, language);
|
||
return false; // break iteration
|
||
}
|
||
|
||
} else {
|
||
|
||
// todo: Add some validator lookup function and tell immediately which module is missing
|
||
throw new Error('Using undefined validator "' + rule +
|
||
'". Maybe you have forgotten to load the module that "' + rule +'" belongs to?');
|
||
|
||
}
|
||
|
||
});
|
||
|
||
|
||
if (isValid === false) {
|
||
$elem.trigger('validation', false);
|
||
result.errorMsg = errorMsg;
|
||
result.isValid = false;
|
||
result.shouldChangeDisplay = true;
|
||
} else if (isValid === null) {
|
||
// A validatorFunction returning null means that it's not able to validate
|
||
// the input at this time. Most probably some async stuff need to gets finished
|
||
// first and then the validator will re-trigger the validation.
|
||
result.shouldChangeDisplay = false;
|
||
} else {
|
||
$elem.trigger('validation', true);
|
||
result.shouldChangeDisplay = true;
|
||
}
|
||
|
||
// Run element validation callback
|
||
if (typeof conf.onElementValidate === 'function' && errorMsg !== null) {
|
||
conf.onElementValidate(result.isValid, $elem, $form, errorMsg);
|
||
}
|
||
|
||
$elem.trigger('afterValidation', [result, eventContext]);
|
||
|
||
return result;
|
||
},
|
||
|
||
/**
|
||
* Is it a correct date according to given dateFormat. Will return false if not, otherwise
|
||
* an array 0=>year 1=>month 2=>day
|
||
*
|
||
* @param {String} val
|
||
* @param {String} dateFormat
|
||
* @param {Boolean} [addMissingLeadingZeros]
|
||
* @return {Array}|{Boolean}
|
||
*/
|
||
parseDate: function (val, dateFormat, addMissingLeadingZeros) {
|
||
var divider = dateFormat.replace(/[a-zA-Z]/gi, '').substring(0, 1),
|
||
regexp = '^',
|
||
formatParts = dateFormat.split(divider || null),
|
||
matches, day, month, year;
|
||
|
||
$.each(formatParts, function (i, part) {
|
||
regexp += (i > 0 ? '\\' + divider : '') + '(\\d{' + part.length + '})';
|
||
});
|
||
|
||
regexp += '$';
|
||
|
||
if (addMissingLeadingZeros) {
|
||
var newValueParts = [];
|
||
$.each(val.split(divider), function(i, part) {
|
||
if(part.length === 1) {
|
||
part = '0'+part;
|
||
}
|
||
newValueParts.push(part);
|
||
});
|
||
val = newValueParts.join(divider);
|
||
}
|
||
|
||
matches = val.match(new RegExp(regexp));
|
||
if (matches === null) {
|
||
return false;
|
||
}
|
||
|
||
var findDateUnit = function (unit, formatParts, matches) {
|
||
for (var i = 0; i < formatParts.length; i++) {
|
||
if (formatParts[i].substring(0, 1) === unit) {
|
||
return $.formUtils.parseDateInt(matches[i + 1]);
|
||
}
|
||
}
|
||
return -1;
|
||
};
|
||
|
||
month = findDateUnit('m', formatParts, matches);
|
||
day = findDateUnit('d', formatParts, matches);
|
||
year = findDateUnit('y', formatParts, matches);
|
||
|
||
if ((month === 2 && day > 28 && (year % 4 !== 0 || year % 100 === 0 && year % 400 !== 0)) ||
|
||
(month === 2 && day > 29 && (year % 4 === 0 || year % 100 !== 0 && year % 400 === 0)) ||
|
||
month > 12 || month === 0) {
|
||
return false;
|
||
}
|
||
if ((this.isShortMonth(month) && day > 30) || (!this.isShortMonth(month) && day > 31) || day === 0) {
|
||
return false;
|
||
}
|
||
|
||
return [year, month, day];
|
||
},
|
||
|
||
/**
|
||
* skum fix. är talet 05 eller lägre ger parseInt rätt int annars får man 0 när man kör parseInt?
|
||
*
|
||
* @param {String} val
|
||
* @return {Number}
|
||
*/
|
||
parseDateInt: function (val) {
|
||
if (val.indexOf('0') === 0) {
|
||
val = val.replace('0', '');
|
||
}
|
||
return parseInt(val, 10);
|
||
},
|
||
|
||
/**
|
||
* Has month only 30 days?
|
||
*
|
||
* @param {Number} m
|
||
* @return {Boolean}
|
||
*/
|
||
isShortMonth: function (m) {
|
||
return (m % 2 === 0 && m < 7) || (m % 2 !== 0 && m > 7);
|
||
},
|
||
|
||
/**
|
||
* Restrict input length
|
||
*
|
||
* @param {jQuery} $inputElement Jquery Html object
|
||
* @param {jQuery} $maxLengthElement jQuery Html Object
|
||
* @return void
|
||
*/
|
||
lengthRestriction: function ($inputElement, $maxLengthElement) {
|
||
// read maxChars from counter display initial text value
|
||
var maxChars = parseInt($maxLengthElement.text(), 10),
|
||
charsLeft = 0,
|
||
|
||
// internal function does the counting and sets display value
|
||
countCharacters = function () {
|
||
var numChars = $inputElement.val().length;
|
||
if (numChars > maxChars) {
|
||
// get current scroll bar position
|
||
var currScrollTopPos = $inputElement.scrollTop();
|
||
// trim value to max length
|
||
$inputElement.val($inputElement.val().substring(0, maxChars));
|
||
$inputElement.scrollTop(currScrollTopPos);
|
||
}
|
||
charsLeft = maxChars - numChars;
|
||
if (charsLeft < 0) {
|
||
charsLeft = 0;
|
||
}
|
||
|
||
// set counter text
|
||
$maxLengthElement.text(charsLeft);
|
||
};
|
||
|
||
// bind events to this element
|
||
// setTimeout is needed, cut or paste fires before val is available
|
||
$($inputElement).bind('keydown keyup keypress focus blur', countCharacters)
|
||
.bind('cut paste', function () {
|
||
setTimeout(countCharacters, 100);
|
||
});
|
||
|
||
// count chars on pageload, if there are prefilled input-values
|
||
$(document).bind('ready', countCharacters);
|
||
},
|
||
|
||
/**
|
||
* Test numeric against allowed range
|
||
*
|
||
* @param $value int
|
||
* @param $rangeAllowed str; (1-2, min1, max2, 10)
|
||
* @return array
|
||
*/
|
||
numericRangeCheck: function (value, rangeAllowed) {
|
||
// split by dash
|
||
var range = $.split(rangeAllowed),
|
||
// min or max
|
||
minmax = parseInt(rangeAllowed.substr(3), 10);
|
||
|
||
if( range.length === 1 && rangeAllowed.indexOf('min') === -1 && rangeAllowed.indexOf('max') === -1 ) {
|
||
range = [rangeAllowed, rangeAllowed]; // only a number, checking agains an exact number of characters
|
||
}
|
||
|
||
// range ?
|
||
if (range.length === 2 && (value < parseInt(range[0], 10) || value > parseInt(range[1], 10) )) {
|
||
return [ 'out', range[0], range[1] ];
|
||
} // value is out of range
|
||
else if (rangeAllowed.indexOf('min') === 0 && (value < minmax )) // min
|
||
{
|
||
return ['min', minmax];
|
||
} // value is below min
|
||
else if (rangeAllowed.indexOf('max') === 0 && (value > minmax )) // max
|
||
{
|
||
return ['max', minmax];
|
||
} // value is above max
|
||
// since no other returns executed, value is in allowed range
|
||
return [ 'ok' ];
|
||
},
|
||
|
||
|
||
_numSuggestionElements: 0,
|
||
_selectedSuggestion: null,
|
||
_previousTypedVal: null,
|
||
|
||
/**
|
||
* Utility function that can be used to create plugins that gives
|
||
* suggestions when inputs is typed into
|
||
* @param {jQuery} $elem
|
||
* @param {Array} suggestions
|
||
* @param {Object} settings - Optional
|
||
* @return {jQuery}
|
||
*/
|
||
suggest: function ($elem, suggestions, settings) {
|
||
var conf = {
|
||
css: {
|
||
maxHeight: '150px',
|
||
background: '#FFF',
|
||
lineHeight: '150%',
|
||
textDecoration: 'underline',
|
||
overflowX: 'hidden',
|
||
overflowY: 'auto',
|
||
border: '#CCC solid 1px',
|
||
borderTop: 'none',
|
||
cursor: 'pointer'
|
||
},
|
||
activeSuggestionCSS: {
|
||
background: '#E9E9E9'
|
||
}
|
||
},
|
||
setSuggsetionPosition = function ($suggestionContainer, $input) {
|
||
var offset = $input.offset();
|
||
$suggestionContainer.css({
|
||
width: $input.outerWidth(),
|
||
left: offset.left + 'px',
|
||
top: (offset.top + $input.outerHeight()) + 'px'
|
||
});
|
||
};
|
||
|
||
if (settings) {
|
||
$.extend(conf, settings);
|
||
}
|
||
|
||
conf.css.position = 'absolute';
|
||
conf.css['z-index'] = 9999;
|
||
$elem.attr('autocomplete', 'off');
|
||
|
||
if (this._numSuggestionElements === 0) {
|
||
// Re-position suggestion container if window size changes
|
||
$win.bind('resize', function () {
|
||
$('.jquery-form-suggestions').each(function () {
|
||
var $container = $(this),
|
||
suggestID = $container.attr('data-suggest-container');
|
||
setSuggsetionPosition($container, $('.suggestions-' + suggestID).eq(0));
|
||
});
|
||
});
|
||
}
|
||
|
||
this._numSuggestionElements++;
|
||
|
||
var onSelectSuggestion = function ($el) {
|
||
var suggestionId = $el.valAttr('suggestion-nr');
|
||
$.formUtils._selectedSuggestion = null;
|
||
$.formUtils._previousTypedVal = null;
|
||
$('.jquery-form-suggestion-' + suggestionId).fadeOut('fast');
|
||
};
|
||
|
||
$elem
|
||
.data('suggestions', suggestions)
|
||
.valAttr('suggestion-nr', this._numSuggestionElements)
|
||
.unbind('focus.suggest')
|
||
.bind('focus.suggest', function () {
|
||
$(this).trigger('keyup');
|
||
$.formUtils._selectedSuggestion = null;
|
||
})
|
||
.unbind('keyup.suggest')
|
||
.bind('keyup.suggest', function () {
|
||
var $input = $(this),
|
||
foundSuggestions = [],
|
||
val = $.trim($input.val()).toLocaleLowerCase();
|
||
|
||
if (val === $.formUtils._previousTypedVal) {
|
||
return;
|
||
}
|
||
else {
|
||
$.formUtils._previousTypedVal = val;
|
||
}
|
||
|
||
var hasTypedSuggestion = false,
|
||
suggestionId = $input.valAttr('suggestion-nr'),
|
||
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
|
||
|
||
$suggestionContainer.scrollTop(0);
|
||
|
||
// Find the right suggestions
|
||
if (val !== '') {
|
||
var findPartial = val.length > 2;
|
||
$.each($input.data('suggestions'), function (i, suggestion) {
|
||
var lowerCaseVal = suggestion.toLocaleLowerCase();
|
||
if (lowerCaseVal === val) {
|
||
foundSuggestions.push('<strong>' + suggestion + '</strong>');
|
||
hasTypedSuggestion = true;
|
||
return false;
|
||
} else if (lowerCaseVal.indexOf(val) === 0 || (findPartial && lowerCaseVal.indexOf(val) > -1)) {
|
||
foundSuggestions.push(suggestion.replace(new RegExp(val, 'gi'), '<strong>$&</strong>'));
|
||
}
|
||
});
|
||
}
|
||
|
||
// Hide suggestion container
|
||
if (hasTypedSuggestion || (foundSuggestions.length === 0 && $suggestionContainer.length > 0)) {
|
||
$suggestionContainer.hide();
|
||
}
|
||
|
||
// Create suggestion container if not already exists
|
||
else if (foundSuggestions.length > 0 && $suggestionContainer.length === 0) {
|
||
$suggestionContainer = $('<div></div>').css(conf.css).appendTo('body');
|
||
$elem.addClass('suggestions-' + suggestionId);
|
||
$suggestionContainer
|
||
.attr('data-suggest-container', suggestionId)
|
||
.addClass('jquery-form-suggestions')
|
||
.addClass('jquery-form-suggestion-' + suggestionId);
|
||
}
|
||
|
||
// Show hidden container
|
||
else if (foundSuggestions.length > 0 && !$suggestionContainer.is(':visible')) {
|
||
$suggestionContainer.show();
|
||
}
|
||
|
||
// add suggestions
|
||
if (foundSuggestions.length > 0 && val.length !== foundSuggestions[0].length) {
|
||
|
||
// put container in place every time, just in case
|
||
setSuggsetionPosition($suggestionContainer, $input);
|
||
|
||
// Add suggestions HTML to container
|
||
$suggestionContainer.html('');
|
||
$.each(foundSuggestions, function (i, text) {
|
||
$('<div></div>')
|
||
.append(text)
|
||
.css({
|
||
overflow: 'hidden',
|
||
textOverflow: 'ellipsis',
|
||
whiteSpace: 'nowrap',
|
||
padding: '5px'
|
||
})
|
||
.addClass('form-suggest-element')
|
||
.appendTo($suggestionContainer)
|
||
.click(function () {
|
||
$input.focus();
|
||
$input.val($(this).text());
|
||
$input.trigger('change');
|
||
onSelectSuggestion($input);
|
||
});
|
||
});
|
||
}
|
||
})
|
||
.unbind('keydown.validation')
|
||
.bind('keydown.validation', function (e) {
|
||
var code = (e.keyCode ? e.keyCode : e.which),
|
||
suggestionId,
|
||
$suggestionContainer,
|
||
$input = $(this);
|
||
|
||
if (code === 13 && $.formUtils._selectedSuggestion !== null) {
|
||
suggestionId = $input.valAttr('suggestion-nr');
|
||
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
|
||
if ($suggestionContainer.length > 0) {
|
||
var newText = $suggestionContainer.find('div').eq($.formUtils._selectedSuggestion).text();
|
||
$input.val(newText);
|
||
$input.trigger('change');
|
||
onSelectSuggestion($input);
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
else {
|
||
suggestionId = $input.valAttr('suggestion-nr');
|
||
$suggestionContainer = $('.jquery-form-suggestion-' + suggestionId);
|
||
var $suggestions = $suggestionContainer.children();
|
||
if ($suggestions.length > 0 && $.inArray(code, [38, 40]) > -1) {
|
||
if (code === 38) { // key up
|
||
if ($.formUtils._selectedSuggestion === null) {
|
||
$.formUtils._selectedSuggestion = $suggestions.length - 1;
|
||
}
|
||
else{
|
||
$.formUtils._selectedSuggestion--;
|
||
}
|
||
if ($.formUtils._selectedSuggestion < 0) {
|
||
$.formUtils._selectedSuggestion = $suggestions.length - 1;
|
||
}
|
||
}
|
||
else if (code === 40) { // key down
|
||
if ($.formUtils._selectedSuggestion === null) {
|
||
$.formUtils._selectedSuggestion = 0;
|
||
}
|
||
else {
|
||
$.formUtils._selectedSuggestion++;
|
||
}
|
||
if ($.formUtils._selectedSuggestion > ($suggestions.length - 1)) {
|
||
$.formUtils._selectedSuggestion = 0;
|
||
}
|
||
}
|
||
|
||
// Scroll in suggestion window
|
||
var containerInnerHeight = $suggestionContainer.innerHeight(),
|
||
containerScrollTop = $suggestionContainer.scrollTop(),
|
||
suggestionHeight = $suggestionContainer.children().eq(0).outerHeight(),
|
||
activeSuggestionPosY = suggestionHeight * ($.formUtils._selectedSuggestion);
|
||
|
||
if (activeSuggestionPosY < containerScrollTop || activeSuggestionPosY > (containerScrollTop + containerInnerHeight)) {
|
||
$suggestionContainer.scrollTop(activeSuggestionPosY);
|
||
}
|
||
|
||
$suggestions
|
||
.removeClass('active-suggestion')
|
||
.css('background', 'none')
|
||
.eq($.formUtils._selectedSuggestion)
|
||
.addClass('active-suggestion')
|
||
.css(conf.activeSuggestionCSS);
|
||
|
||
e.preventDefault();
|
||
return false;
|
||
}
|
||
}
|
||
})
|
||
.unbind('blur.suggest')
|
||
.bind('blur.suggest', function () {
|
||
onSelectSuggestion($(this));
|
||
});
|
||
|
||
return $elem;
|
||
},
|
||
|
||
/**
|
||
* Error dialogs
|
||
*
|
||
* @var {Object}
|
||
*/
|
||
LANG: {
|
||
errorTitle: 'Form submission failed!',
|
||
requiredField: 'This is a required field',
|
||
requiredFields: 'You have not answered all required fields',
|
||
badTime: 'You have not given a correct time',
|
||
badEmail: 'You have not given a correct e-mail address',
|
||
badTelephone: 'You have not given a correct phone number',
|
||
badSecurityAnswer: 'You have not given a correct answer to the security question',
|
||
badDate: 'You have not given a correct date',
|
||
lengthBadStart: 'The input value must be between ',
|
||
lengthBadEnd: ' characters',
|
||
lengthTooLongStart: 'The input value is longer than ',
|
||
lengthTooShortStart: 'The input value is shorter than ',
|
||
notConfirmed: 'Input values could not be confirmed',
|
||
badDomain: 'Incorrect domain value',
|
||
badUrl: 'The input value is not a correct URL',
|
||
badCustomVal: 'The input value is incorrect',
|
||
andSpaces: ' and spaces ',
|
||
badInt: 'The input value was not a correct number',
|
||
badSecurityNumber: 'Your social security number was incorrect',
|
||
badUKVatAnswer: 'Incorrect UK VAT Number',
|
||
badUKNin: 'Incorrect UK NIN',
|
||
badUKUtr: 'Incorrect UK UTR Number',
|
||
badStrength: 'The password isn\'t strong enough',
|
||
badNumberOfSelectedOptionsStart: 'You have to choose at least ',
|
||
badNumberOfSelectedOptionsEnd: ' answers',
|
||
badAlphaNumeric: 'The input value can only contain alphanumeric characters ',
|
||
badAlphaNumericExtra: ' and ',
|
||
wrongFileSize: 'The file you are trying to upload is too large (max %s)',
|
||
wrongFileType: 'Only files of type %s is allowed',
|
||
groupCheckedRangeStart: 'Please choose between ',
|
||
groupCheckedTooFewStart: 'Please choose at least ',
|
||
groupCheckedTooManyStart: 'Please choose a maximum of ',
|
||
groupCheckedEnd: ' item(s)',
|
||
badCreditCard: 'The credit card number is not correct',
|
||
badCVV: 'The CVV number was not correct',
|
||
wrongFileDim : 'Incorrect image dimensions,',
|
||
imageTooTall : 'the image can not be taller than',
|
||
imageTooWide : 'the image can not be wider than',
|
||
imageTooSmall : 'the image was too small',
|
||
min : 'min',
|
||
max : 'max',
|
||
imageRatioNotAccepted : 'Image ratio is not be accepted',
|
||
badBrazilTelephoneAnswer: 'The phone number entered is invalid',
|
||
badBrazilCEPAnswer: 'The CEP entered is invalid',
|
||
badBrazilCPFAnswer: 'The CPF entered is invalid',
|
||
badPlPesel: 'The PESEL entered is invalid',
|
||
badPlNip: 'The NIP entered is invalid',
|
||
badPlRegon: 'The REGON entered is invalid',
|
||
badreCaptcha: 'Please confirm that you are not a bot',
|
||
passwordComplexityStart: 'Password must contain at least ',
|
||
passwordComplexitySeparator: ', ',
|
||
passwordComplexityUppercaseInfo: ' uppercase letter(s)',
|
||
passwordComplexityLowercaseInfo: ' lowercase letter(s)',
|
||
passwordComplexitySpecialCharsInfo: ' special character(s)',
|
||
passwordComplexityNumericCharsInfo: ' numeric character(s)',
|
||
passwordComplexityEnd: '.'
|
||
}
|
||
});
|
||
|
||
})(jQuery, window);
|
||
|
||
/**
|
||
* File declaring all default validators.
|
||
*/
|
||
(function($) {
|
||
|
||
/*
|
||
* Validate email
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'email',
|
||
validatorFunction: function (email) {
|
||
|
||
var emailParts = email.toLowerCase().split('@'),
|
||
localPart = emailParts[0],
|
||
domain = emailParts[1];
|
||
|
||
if (localPart && domain) {
|
||
|
||
if( localPart.indexOf('"') === 0 ) {
|
||
var len = localPart.length;
|
||
localPart = localPart.replace(/\"/g, '');
|
||
if( localPart.length !== (len-2) ) {
|
||
return false; // It was not allowed to have more than two apostrophes
|
||
}
|
||
}
|
||
|
||
return $.formUtils.validators.validate_domain.validatorFunction(emailParts[1]) &&
|
||
localPart.indexOf('.') !== 0 &&
|
||
localPart.substring(localPart.length-1, localPart.length) !== '.' &&
|
||
localPart.indexOf('..') === -1 &&
|
||
!(/[^\w\+\.\-\#\-\_\~\!\$\&\'\(\)\*\+\,\;\=\:]/.test(localPart));
|
||
}
|
||
|
||
return false;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badEmail'
|
||
});
|
||
|
||
/*
|
||
* Validate domain name
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'domain',
|
||
validatorFunction: function (val) {
|
||
return val.length > 0 &&
|
||
val.length <= 253 && // Including sub domains
|
||
!(/[^a-zA-Z0-9]/.test(val.slice(-2))) && !(/[^a-zA-Z0-9]/.test(val.substr(0, 1))) && !(/[^a-zA-Z0-9\.\-]/.test(val)) &&
|
||
val.split('..').length === 1 &&
|
||
val.split('.').length > 1;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badDomain'
|
||
});
|
||
|
||
/*
|
||
* Validate required
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'required',
|
||
validatorFunction: function (val, $el, config, language, $form) {
|
||
switch ($el.attr('type')) {
|
||
case 'checkbox':
|
||
return $el.is(':checked');
|
||
case 'radio':
|
||
return $form.find('input[name="' + $el.attr('name') + '"]').filter(':checked').length > 0;
|
||
default:
|
||
return $.trim(val) !== '';
|
||
}
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: function(config) {
|
||
if (config.errorMessagePosition === 'top' || typeof config.errorMessagePosition === 'function') {
|
||
return 'requiredFields';
|
||
}
|
||
else {
|
||
return 'requiredField';
|
||
}
|
||
}
|
||
});
|
||
|
||
/*
|
||
* Validate length range
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'length',
|
||
validatorFunction: function (val, $el, conf, lang) {
|
||
var lengthAllowed = $el.valAttr('length'),
|
||
type = $el.attr('type');
|
||
|
||
if (lengthAllowed === undefined) {
|
||
alert('Please add attribute "data-validation-length" to ' + $el[0].nodeName + ' named ' + $el.attr('name'));
|
||
return true;
|
||
}
|
||
|
||
// check if length is above min, below max or within range.
|
||
var len = type === 'file' && $el.get(0).files !== undefined ? $el.get(0).files.length : val.length,
|
||
lengthCheckResults = $.formUtils.numericRangeCheck(len, lengthAllowed),
|
||
checkResult;
|
||
|
||
switch (lengthCheckResults[0]) { // outside of allowed range
|
||
case 'out':
|
||
this.errorMessage = lang.lengthBadStart + lengthAllowed + lang.lengthBadEnd;
|
||
checkResult = false;
|
||
break;
|
||
// too short
|
||
case 'min':
|
||
this.errorMessage = lang.lengthTooShortStart + lengthCheckResults[1] + lang.lengthBadEnd;
|
||
checkResult = false;
|
||
break;
|
||
// too long
|
||
case 'max':
|
||
this.errorMessage = lang.lengthTooLongStart + lengthCheckResults[1] + lang.lengthBadEnd;
|
||
checkResult = false;
|
||
break;
|
||
// ok
|
||
default:
|
||
checkResult = true;
|
||
}
|
||
|
||
return checkResult;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: ''
|
||
});
|
||
|
||
/*
|
||
* Validate url
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'url',
|
||
validatorFunction: function (url) {
|
||
// written by Scott Gonzalez: http://projects.scottsplayground.com/iri/
|
||
// - Victor Jonsson added support for arrays in the url ?arg[]=sdfsdf
|
||
// - General improvements made by Stéphane Moureau <https://github.com/TraderStf>
|
||
|
||
var urlFilter = /^(https?|ftp):\/\/((((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])(\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|\[|\]|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#(((\w|-|\.|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i;
|
||
if (urlFilter.test(url)) {
|
||
var domain = url.split('://')[1],
|
||
domainSlashPos = domain.indexOf('/');
|
||
|
||
if (domainSlashPos > -1) {
|
||
domain = domain.substr(0, domainSlashPos);
|
||
}
|
||
|
||
return $.formUtils.validators.validate_domain.validatorFunction(domain); // todo: add support for IP-addresses
|
||
}
|
||
return false;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badUrl'
|
||
});
|
||
|
||
/*
|
||
* Validate number (floating or integer)
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'number',
|
||
validatorFunction: function (val, $el, conf) {
|
||
if (val !== '') {
|
||
var allowing = $el.valAttr('allowing') || '',
|
||
decimalSeparator = $el.valAttr('decimal-separator') || conf.decimalSeparator,
|
||
allowsRange = false,
|
||
begin, end,
|
||
steps = $el.valAttr('step') || '',
|
||
allowsSteps = false,
|
||
sanitize = $el.attr('data-sanitize') || '',
|
||
isFormattedWithNumeral = sanitize.match(/(^|[\s])numberFormat([\s]|$)/i);
|
||
|
||
if (isFormattedWithNumeral) {
|
||
if (!window.numeral) {
|
||
throw new ReferenceError('The data-sanitize value numberFormat cannot be used without the numeral' +
|
||
' library. Please see Data Validation in http://www.formvalidator.net for more information.');
|
||
}
|
||
//Unformat input first, then convert back to String
|
||
if (val.length) {
|
||
val = String(numeral().unformat(val));
|
||
}
|
||
}
|
||
|
||
if (allowing.indexOf('number') === -1) {
|
||
allowing += ',number';
|
||
}
|
||
|
||
if (allowing.indexOf('negative') === -1 && val.indexOf('-') === 0) {
|
||
return false;
|
||
}
|
||
|
||
if (allowing.indexOf('range') > -1) {
|
||
begin = parseFloat(allowing.substring(allowing.indexOf('[') + 1, allowing.indexOf(';')));
|
||
end = parseFloat(allowing.substring(allowing.indexOf(';') + 1, allowing.indexOf(']')));
|
||
allowsRange = true;
|
||
}
|
||
|
||
if (steps !== '') {
|
||
allowsSteps = true;
|
||
}
|
||
|
||
if (decimalSeparator === ',') {
|
||
if (val.indexOf('.') > -1) {
|
||
return false;
|
||
}
|
||
// Fix for checking range with floats using ,
|
||
val = val.replace(',', '.');
|
||
}
|
||
if (val.replace(/[0-9-]/g, '') === '' && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps === 0))) {
|
||
return true;
|
||
}
|
||
|
||
if (allowing.indexOf('float') > -1 && val.match(new RegExp('^([0-9-]+)\\.([0-9]+)$')) !== null && (!allowsRange || (val >= begin && val <= end)) && (!allowsSteps || (val % steps === 0))) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badInt'
|
||
});
|
||
|
||
/*
|
||
* Validate alpha numeric
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'alphanumeric',
|
||
validatorFunction: function (val, $el, conf, language) {
|
||
var patternStart = '^([a-zA-Z0-9',
|
||
patternEnd = ']+)$',
|
||
additionalChars = $el.valAttr('allowing'),
|
||
pattern = '',
|
||
hasSpaces = false;
|
||
|
||
if (additionalChars) {
|
||
pattern = patternStart + additionalChars + patternEnd;
|
||
var extra = additionalChars.replace(/\\/g, '');
|
||
if (extra.indexOf(' ') > -1) {
|
||
hasSpaces = true;
|
||
extra = extra.replace(' ', '');
|
||
extra += language.andSpaces || $.formUtils.LANG.andSpaces;
|
||
}
|
||
|
||
if(language.badAlphaNumericAndExtraAndSpaces && language.badAlphaNumericAndExtra) {
|
||
if(hasSpaces) {
|
||
this.errorMessage = language.badAlphaNumericAndExtraAndSpaces + extra;
|
||
} else {
|
||
this.errorMessage = language.badAlphaNumericAndExtra + extra + language.badAlphaNumericExtra;
|
||
}
|
||
} else {
|
||
this.errorMessage = language.badAlphaNumeric + language.badAlphaNumericExtra + extra;
|
||
}
|
||
} else {
|
||
pattern = patternStart + patternEnd;
|
||
this.errorMessage = language.badAlphaNumeric;
|
||
}
|
||
|
||
return new RegExp(pattern).test(val);
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: ''
|
||
});
|
||
|
||
/*
|
||
* Validate against regexp
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'custom',
|
||
validatorFunction: function (val, $el) {
|
||
var regexp = new RegExp($el.valAttr('regexp'));
|
||
return regexp.test(val);
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badCustomVal'
|
||
});
|
||
|
||
/*
|
||
* Validate date
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'date',
|
||
validatorFunction: function (date, $el, conf) {
|
||
var dateFormat = $el.valAttr('format') || conf.dateFormat || 'yyyy-mm-dd',
|
||
addMissingLeadingZeros = $el.valAttr('require-leading-zero') === 'false';
|
||
return $.formUtils.parseDate(date, dateFormat, addMissingLeadingZeros) !== false;
|
||
},
|
||
errorMessage: '',
|
||
errorMessageKey: 'badDate'
|
||
});
|
||
|
||
|
||
/*
|
||
* Validate group of checkboxes, validate qty required is checked
|
||
* written by Steve Wasiura : http://stevewasiura.waztech.com
|
||
* element attrs
|
||
* data-validation="checkbox_group"
|
||
* data-validation-qty="1-2" // min 1 max 2
|
||
* data-validation-error-msg="chose min 1, max of 2 checkboxes"
|
||
*/
|
||
$.formUtils.addValidator({
|
||
name: 'checkbox_group',
|
||
validatorFunction: function (val, $el, conf, lang, $form) {
|
||
// preset return var
|
||
var isValid = true,
|
||
// get name of element. since it is a checkbox group, all checkboxes will have same name
|
||
elname = $el.attr('name'),
|
||
// get checkboxes and count the checked ones
|
||
$checkBoxes = $('input[type=checkbox][name^="' + elname + '"]', $form),
|
||
checkedCount = $checkBoxes.filter(':checked').length,
|
||
// get el attr that specs qty required / allowed
|
||
qtyAllowed = $el.valAttr('qty');
|
||
|
||
if (qtyAllowed === undefined) {
|
||
var elementType = $el.get(0).nodeName;
|
||
alert('Attribute "data-validation-qty" is missing from ' + elementType + ' named ' + $el.attr('name'));
|
||
}
|
||
|
||
// call Utility function to check if count is above min, below max, within range etc.
|
||
var qtyCheckResults = $.formUtils.numericRangeCheck(checkedCount, qtyAllowed);
|
||
|
||
// results will be array, [0]=result str, [1]=qty int
|
||
switch (qtyCheckResults[0]) {
|
||
// outside allowed range
|
||
case 'out':
|
||
this.errorMessage = lang.groupCheckedRangeStart + qtyAllowed + lang.groupCheckedEnd;
|
||
isValid = false;
|
||
break;
|
||
// below min qty
|
||
case 'min':
|
||
this.errorMessage = lang.groupCheckedTooFewStart + qtyCheckResults[1] + (lang.groupCheckedTooFewEnd || lang.groupCheckedEnd);
|
||
isValid = false;
|
||
break;
|
||
// above max qty
|
||
case 'max':
|
||
this.errorMessage = lang.groupCheckedTooManyStart + qtyCheckResults[1] + (lang.groupCheckedTooManyEnd || lang.groupCheckedEnd);
|
||
isValid = false;
|
||
break;
|
||
// ok
|
||
default:
|
||
isValid = true;
|
||
}
|
||
|
||
if( !isValid ) {
|
||
var _triggerOnBlur = function() {
|
||
$checkBoxes.unbind('click', _triggerOnBlur);
|
||
$checkBoxes.filter('*[data-validation]').validateInputOnBlur(lang, conf, false, 'blur');
|
||
};
|
||
$checkBoxes.bind('click', _triggerOnBlur);
|
||
}
|
||
|
||
return isValid;
|
||
}
|
||
// errorMessage : '', // set above in switch statement
|
||
// errorMessageKey: '' // not used
|
||
});
|
||
|
||
})(jQuery);
|
||
|
||
|
||
}));
|
||
|
||
/*! List.js v1.5.0 (http://listjs.com) by Jonny Strömberg (http://javve.com) */
|
||
var List =
|
||
/******/ (function(modules) { // webpackBootstrap
|
||
/******/ // The module cache
|
||
/******/ var installedModules = {};
|
||
|
||
/******/ // The require function
|
||
/******/ function __webpack_require__(moduleId) {
|
||
|
||
/******/ // Check if module is in cache
|
||
/******/ if(installedModules[moduleId])
|
||
/******/ return installedModules[moduleId].exports;
|
||
|
||
/******/ // Create a new module (and put it into the cache)
|
||
/******/ var module = installedModules[moduleId] = {
|
||
/******/ i: moduleId,
|
||
/******/ l: false,
|
||
/******/ exports: {}
|
||
/******/ };
|
||
|
||
/******/ // Execute the module function
|
||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||
|
||
/******/ // Flag the module as loaded
|
||
/******/ module.l = true;
|
||
|
||
/******/ // Return the exports of the module
|
||
/******/ return module.exports;
|
||
/******/ }
|
||
|
||
|
||
/******/ // expose the modules object (__webpack_modules__)
|
||
/******/ __webpack_require__.m = modules;
|
||
|
||
/******/ // expose the module cache
|
||
/******/ __webpack_require__.c = installedModules;
|
||
|
||
/******/ // identity function for calling harmony imports with the correct context
|
||
/******/ __webpack_require__.i = function(value) { return value; };
|
||
|
||
/******/ // define getter function for harmony exports
|
||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||
/******/ Object.defineProperty(exports, name, {
|
||
/******/ configurable: false,
|
||
/******/ enumerable: true,
|
||
/******/ get: getter
|
||
/******/ });
|
||
/******/ }
|
||
/******/ };
|
||
|
||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||
/******/ __webpack_require__.n = function(module) {
|
||
/******/ var getter = module && module.__esModule ?
|
||
/******/ function getDefault() { return module['default']; } :
|
||
/******/ function getModuleExports() { return module; };
|
||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||
/******/ return getter;
|
||
/******/ };
|
||
|
||
/******/ // Object.prototype.hasOwnProperty.call
|
||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||
|
||
/******/ // __webpack_public_path__
|
||
/******/ __webpack_require__.p = "";
|
||
|
||
/******/ // Load entry module and return exports
|
||
/******/ return __webpack_require__(__webpack_require__.s = 11);
|
||
/******/ })
|
||
/************************************************************************/
|
||
/******/ ([
|
||
/* 0 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
/**
|
||
* Module dependencies.
|
||
*/
|
||
|
||
var index = __webpack_require__(4);
|
||
|
||
/**
|
||
* Whitespace regexp.
|
||
*/
|
||
|
||
var re = /\s+/;
|
||
|
||
/**
|
||
* toString reference.
|
||
*/
|
||
|
||
var toString = Object.prototype.toString;
|
||
|
||
/**
|
||
* Wrap `el` in a `ClassList`.
|
||
*
|
||
* @param {Element} el
|
||
* @return {ClassList}
|
||
* @api public
|
||
*/
|
||
|
||
module.exports = function(el){
|
||
return new ClassList(el);
|
||
};
|
||
|
||
/**
|
||
* Initialize a new ClassList for `el`.
|
||
*
|
||
* @param {Element} el
|
||
* @api private
|
||
*/
|
||
|
||
function ClassList(el) {
|
||
if (!el || !el.nodeType) {
|
||
throw new Error('A DOM element reference is required');
|
||
}
|
||
this.el = el;
|
||
this.list = el.classList;
|
||
}
|
||
|
||
/**
|
||
* Add class `name` if not already present.
|
||
*
|
||
* @param {String} name
|
||
* @return {ClassList}
|
||
* @api public
|
||
*/
|
||
|
||
ClassList.prototype.add = function(name){
|
||
// classList
|
||
if (this.list) {
|
||
this.list.add(name);
|
||
return this;
|
||
}
|
||
|
||
// fallback
|
||
var arr = this.array();
|
||
var i = index(arr, name);
|
||
if (!~i) arr.push(name);
|
||
this.el.className = arr.join(' ');
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Remove class `name` when present, or
|
||
* pass a regular expression to remove
|
||
* any which match.
|
||
*
|
||
* @param {String|RegExp} name
|
||
* @return {ClassList}
|
||
* @api public
|
||
*/
|
||
|
||
ClassList.prototype.remove = function(name){
|
||
// classList
|
||
if (this.list) {
|
||
this.list.remove(name);
|
||
return this;
|
||
}
|
||
|
||
// fallback
|
||
var arr = this.array();
|
||
var i = index(arr, name);
|
||
if (~i) arr.splice(i, 1);
|
||
this.el.className = arr.join(' ');
|
||
return this;
|
||
};
|
||
|
||
|
||
/**
|
||
* Toggle class `name`, can force state via `force`.
|
||
*
|
||
* For browsers that support classList, but do not support `force` yet,
|
||
* the mistake will be detected and corrected.
|
||
*
|
||
* @param {String} name
|
||
* @param {Boolean} force
|
||
* @return {ClassList}
|
||
* @api public
|
||
*/
|
||
|
||
ClassList.prototype.toggle = function(name, force){
|
||
// classList
|
||
if (this.list) {
|
||
if ("undefined" !== typeof force) {
|
||
if (force !== this.list.toggle(name, force)) {
|
||
this.list.toggle(name); // toggle again to correct
|
||
}
|
||
} else {
|
||
this.list.toggle(name);
|
||
}
|
||
return this;
|
||
}
|
||
|
||
// fallback
|
||
if ("undefined" !== typeof force) {
|
||
if (!force) {
|
||
this.remove(name);
|
||
} else {
|
||
this.add(name);
|
||
}
|
||
} else {
|
||
if (this.has(name)) {
|
||
this.remove(name);
|
||
} else {
|
||
this.add(name);
|
||
}
|
||
}
|
||
|
||
return this;
|
||
};
|
||
|
||
/**
|
||
* Return an array of classes.
|
||
*
|
||
* @return {Array}
|
||
* @api public
|
||
*/
|
||
|
||
ClassList.prototype.array = function(){
|
||
var className = this.el.getAttribute('class') || '';
|
||
var str = className.replace(/^\s+|\s+$/g, '');
|
||
var arr = str.split(re);
|
||
if ('' === arr[0]) arr.shift();
|
||
return arr;
|
||
};
|
||
|
||
/**
|
||
* Check if class `name` is present.
|
||
*
|
||
* @param {String} name
|
||
* @return {ClassList}
|
||
* @api public
|
||
*/
|
||
|
||
ClassList.prototype.has =
|
||
ClassList.prototype.contains = function(name){
|
||
return this.list ? this.list.contains(name) : !! ~index(this.array(), name);
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 1 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
|
||
unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
|
||
prefix = bind !== 'addEventListener' ? 'on' : '',
|
||
toArray = __webpack_require__(5);
|
||
|
||
/**
|
||
* Bind `el` event `type` to `fn`.
|
||
*
|
||
* @param {Element} el, NodeList, HTMLCollection or Array
|
||
* @param {String} type
|
||
* @param {Function} fn
|
||
* @param {Boolean} capture
|
||
* @api public
|
||
*/
|
||
|
||
exports.bind = function(el, type, fn, capture){
|
||
el = toArray(el);
|
||
for ( var i = 0; i < el.length; i++ ) {
|
||
el[i][bind](prefix + type, fn, capture || false);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* Unbind `el` event `type`'s callback `fn`.
|
||
*
|
||
* @param {Element} el, NodeList, HTMLCollection or Array
|
||
* @param {String} type
|
||
* @param {Function} fn
|
||
* @param {Boolean} capture
|
||
* @api public
|
||
*/
|
||
|
||
exports.unbind = function(el, type, fn, capture){
|
||
el = toArray(el);
|
||
for ( var i = 0; i < el.length; i++ ) {
|
||
el[i][unbind](prefix + type, fn, capture || false);
|
||
}
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 2 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(list) {
|
||
return function(initValues, element, notCreate) {
|
||
var item = this;
|
||
|
||
this._values = {};
|
||
|
||
this.found = false; // Show if list.searched == true and this.found == true
|
||
this.filtered = false;// Show if list.filtered == true and this.filtered == true
|
||
|
||
var init = function(initValues, element, notCreate) {
|
||
if (element === undefined) {
|
||
if (notCreate) {
|
||
item.values(initValues, notCreate);
|
||
} else {
|
||
item.values(initValues);
|
||
}
|
||
} else {
|
||
item.elm = element;
|
||
var values = list.templater.get(item, initValues);
|
||
item.values(values);
|
||
}
|
||
};
|
||
|
||
this.values = function(newValues, notCreate) {
|
||
if (newValues !== undefined) {
|
||
for(var name in newValues) {
|
||
item._values[name] = newValues[name];
|
||
}
|
||
if (notCreate !== true) {
|
||
list.templater.set(item, item.values());
|
||
}
|
||
} else {
|
||
return item._values;
|
||
}
|
||
};
|
||
|
||
this.show = function() {
|
||
list.templater.show(item);
|
||
};
|
||
|
||
this.hide = function() {
|
||
list.templater.hide(item);
|
||
};
|
||
|
||
this.matching = function() {
|
||
return (
|
||
(list.filtered && list.searched && item.found && item.filtered) ||
|
||
(list.filtered && !list.searched && item.filtered) ||
|
||
(!list.filtered && list.searched && item.found) ||
|
||
(!list.filtered && !list.searched)
|
||
);
|
||
};
|
||
|
||
this.visible = function() {
|
||
return (item.elm && (item.elm.parentNode == list.list)) ? true : false;
|
||
};
|
||
|
||
init(initValues, element, notCreate);
|
||
};
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 3 */
|
||
/***/ (function(module, exports) {
|
||
|
||
/**
|
||
* A cross-browser implementation of getElementsByClass.
|
||
* Heavily based on Dustin Diaz's function: http://dustindiaz.com/getelementsbyclass.
|
||
*
|
||
* Find all elements with class `className` inside `container`.
|
||
* Use `single = true` to increase performance in older browsers
|
||
* when only one element is needed.
|
||
*
|
||
* @param {String} className
|
||
* @param {Element} container
|
||
* @param {Boolean} single
|
||
* @api public
|
||
*/
|
||
|
||
var getElementsByClassName = function(container, className, single) {
|
||
if (single) {
|
||
return container.getElementsByClassName(className)[0];
|
||
} else {
|
||
return container.getElementsByClassName(className);
|
||
}
|
||
};
|
||
|
||
var querySelector = function(container, className, single) {
|
||
className = '.' + className;
|
||
if (single) {
|
||
return container.querySelector(className);
|
||
} else {
|
||
return container.querySelectorAll(className);
|
||
}
|
||
};
|
||
|
||
var polyfill = function(container, className, single) {
|
||
var classElements = [],
|
||
tag = '*';
|
||
|
||
var els = container.getElementsByTagName(tag);
|
||
var elsLen = els.length;
|
||
var pattern = new RegExp("(^|\\s)"+className+"(\\s|$)");
|
||
for (var i = 0, j = 0; i < elsLen; i++) {
|
||
if ( pattern.test(els[i].className) ) {
|
||
if (single) {
|
||
return els[i];
|
||
} else {
|
||
classElements[j] = els[i];
|
||
j++;
|
||
}
|
||
}
|
||
}
|
||
return classElements;
|
||
};
|
||
|
||
module.exports = (function() {
|
||
return function(container, className, single, options) {
|
||
options = options || {};
|
||
if ((options.test && options.getElementsByClassName) || (!options.test && document.getElementsByClassName)) {
|
||
return getElementsByClassName(container, className, single);
|
||
} else if ((options.test && options.querySelector) || (!options.test && document.querySelector)) {
|
||
return querySelector(container, className, single);
|
||
} else {
|
||
return polyfill(container, className, single);
|
||
}
|
||
};
|
||
})();
|
||
|
||
|
||
/***/ }),
|
||
/* 4 */
|
||
/***/ (function(module, exports) {
|
||
|
||
var indexOf = [].indexOf;
|
||
|
||
module.exports = function(arr, obj){
|
||
if (indexOf) return arr.indexOf(obj);
|
||
for (var i = 0; i < arr.length; ++i) {
|
||
if (arr[i] === obj) return i;
|
||
}
|
||
return -1;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 5 */
|
||
/***/ (function(module, exports) {
|
||
|
||
/**
|
||
* Source: https://github.com/timoxley/to-array
|
||
*
|
||
* Convert an array-like object into an `Array`.
|
||
* If `collection` is already an `Array`, then will return a clone of `collection`.
|
||
*
|
||
* @param {Array | Mixed} collection An `Array` or array-like object to convert e.g. `arguments` or `NodeList`
|
||
* @return {Array} Naive conversion of `collection` to a new `Array`.
|
||
* @api public
|
||
*/
|
||
|
||
module.exports = function toArray(collection) {
|
||
if (typeof collection === 'undefined') return [];
|
||
if (collection === null) return [null];
|
||
if (collection === window) return [window];
|
||
if (typeof collection === 'string') return [collection];
|
||
if (isArray(collection)) return collection;
|
||
if (typeof collection.length != 'number') return [collection];
|
||
if (typeof collection === 'function' && collection instanceof Function) return [collection];
|
||
|
||
var arr = [];
|
||
for (var i = 0; i < collection.length; i++) {
|
||
if (Object.prototype.hasOwnProperty.call(collection, i) || i in collection) {
|
||
arr.push(collection[i]);
|
||
}
|
||
}
|
||
if (!arr.length) return [];
|
||
return arr;
|
||
};
|
||
|
||
function isArray(arr) {
|
||
return Object.prototype.toString.call(arr) === "[object Array]";
|
||
}
|
||
|
||
|
||
/***/ }),
|
||
/* 6 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(s) {
|
||
s = (s === undefined) ? "" : s;
|
||
s = (s === null) ? "" : s;
|
||
s = s.toString();
|
||
return s;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 7 */
|
||
/***/ (function(module, exports) {
|
||
|
||
/*
|
||
* Source: https://github.com/segmentio/extend
|
||
*/
|
||
|
||
module.exports = function extend (object) {
|
||
// Takes an unlimited number of extenders.
|
||
var args = Array.prototype.slice.call(arguments, 1);
|
||
|
||
// For each extender, copy their properties on our object.
|
||
for (var i = 0, source; source = args[i]; i++) {
|
||
if (!source) continue;
|
||
for (var property in source) {
|
||
object[property] = source[property];
|
||
}
|
||
}
|
||
|
||
return object;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 8 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(list) {
|
||
var addAsync = function(values, callback, items) {
|
||
var valuesToAdd = values.splice(0, 50);
|
||
items = items || [];
|
||
items = items.concat(list.add(valuesToAdd));
|
||
if (values.length > 0) {
|
||
setTimeout(function() {
|
||
addAsync(values, callback, items);
|
||
}, 1);
|
||
} else {
|
||
list.update();
|
||
callback(items);
|
||
}
|
||
};
|
||
return addAsync;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 9 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(list) {
|
||
|
||
// Add handlers
|
||
list.handlers.filterStart = list.handlers.filterStart || [];
|
||
list.handlers.filterComplete = list.handlers.filterComplete || [];
|
||
|
||
return function(filterFunction) {
|
||
list.trigger('filterStart');
|
||
list.i = 1; // Reset paging
|
||
list.reset.filter();
|
||
if (filterFunction === undefined) {
|
||
list.filtered = false;
|
||
} else {
|
||
list.filtered = true;
|
||
var is = list.items;
|
||
for (var i = 0, il = is.length; i < il; i++) {
|
||
var item = is[i];
|
||
if (filterFunction(item)) {
|
||
item.filtered = true;
|
||
} else {
|
||
item.filtered = false;
|
||
}
|
||
}
|
||
}
|
||
list.update();
|
||
list.trigger('filterComplete');
|
||
return list.visibleItems;
|
||
};
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 10 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
|
||
var classes = __webpack_require__(0),
|
||
events = __webpack_require__(1),
|
||
extend = __webpack_require__(7),
|
||
toString = __webpack_require__(6),
|
||
getByClass = __webpack_require__(3),
|
||
fuzzy = __webpack_require__(19);
|
||
|
||
module.exports = function(list, options) {
|
||
options = options || {};
|
||
|
||
options = extend({
|
||
location: 0,
|
||
distance: 100,
|
||
threshold: 0.4,
|
||
multiSearch: true,
|
||
searchClass: 'fuzzy-search'
|
||
}, options);
|
||
|
||
|
||
|
||
var fuzzySearch = {
|
||
search: function(searchString, columns) {
|
||
// Substract arguments from the searchString or put searchString as only argument
|
||
var searchArguments = options.multiSearch ? searchString.replace(/ +$/, '').split(/ +/) : [searchString];
|
||
|
||
for (var k = 0, kl = list.items.length; k < kl; k++) {
|
||
fuzzySearch.item(list.items[k], columns, searchArguments);
|
||
}
|
||
},
|
||
item: function(item, columns, searchArguments) {
|
||
var found = true;
|
||
for(var i = 0; i < searchArguments.length; i++) {
|
||
var foundArgument = false;
|
||
for (var j = 0, jl = columns.length; j < jl; j++) {
|
||
if (fuzzySearch.values(item.values(), columns[j], searchArguments[i])) {
|
||
foundArgument = true;
|
||
}
|
||
}
|
||
if(!foundArgument) {
|
||
found = false;
|
||
}
|
||
}
|
||
item.found = found;
|
||
},
|
||
values: function(values, value, searchArgument) {
|
||
if (values.hasOwnProperty(value)) {
|
||
var text = toString(values[value]).toLowerCase();
|
||
|
||
if (fuzzy(text, searchArgument, options)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
};
|
||
|
||
|
||
events.bind(getByClass(list.listContainer, options.searchClass), 'keyup', function(e) {
|
||
var target = e.target || e.srcElement; // IE have srcElement
|
||
list.search(target.value, fuzzySearch.search);
|
||
});
|
||
|
||
return function(str, columns) {
|
||
list.search(str, columns, fuzzySearch.search);
|
||
};
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 11 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
var naturalSort = __webpack_require__(18),
|
||
getByClass = __webpack_require__(3),
|
||
extend = __webpack_require__(7),
|
||
indexOf = __webpack_require__(4),
|
||
events = __webpack_require__(1),
|
||
toString = __webpack_require__(6),
|
||
classes = __webpack_require__(0),
|
||
getAttribute = __webpack_require__(17),
|
||
toArray = __webpack_require__(5);
|
||
|
||
module.exports = function(id, options, values) {
|
||
|
||
var self = this,
|
||
init,
|
||
Item = __webpack_require__(2)(self),
|
||
addAsync = __webpack_require__(8)(self),
|
||
initPagination = __webpack_require__(12)(self);
|
||
|
||
init = {
|
||
start: function() {
|
||
self.listClass = "list";
|
||
self.searchClass = "search";
|
||
self.sortClass = "sort";
|
||
self.page = 10000;
|
||
self.i = 1;
|
||
self.items = [];
|
||
self.visibleItems = [];
|
||
self.matchingItems = [];
|
||
self.searched = false;
|
||
self.filtered = false;
|
||
self.searchColumns = undefined;
|
||
self.handlers = { 'updated': [] };
|
||
self.valueNames = [];
|
||
self.utils = {
|
||
getByClass: getByClass,
|
||
extend: extend,
|
||
indexOf: indexOf,
|
||
events: events,
|
||
toString: toString,
|
||
naturalSort: naturalSort,
|
||
classes: classes,
|
||
getAttribute: getAttribute,
|
||
toArray: toArray
|
||
};
|
||
|
||
self.utils.extend(self, options);
|
||
|
||
self.listContainer = (typeof(id) === 'string') ? document.getElementById(id) : id;
|
||
if (!self.listContainer) { return; }
|
||
self.list = getByClass(self.listContainer, self.listClass, true);
|
||
|
||
self.parse = __webpack_require__(13)(self);
|
||
self.templater = __webpack_require__(16)(self);
|
||
self.search = __webpack_require__(14)(self);
|
||
self.filter = __webpack_require__(9)(self);
|
||
self.sort = __webpack_require__(15)(self);
|
||
self.fuzzySearch = __webpack_require__(10)(self, options.fuzzySearch);
|
||
|
||
this.handlers();
|
||
this.items();
|
||
this.pagination();
|
||
|
||
self.update();
|
||
},
|
||
handlers: function() {
|
||
for (var handler in self.handlers) {
|
||
if (self[handler]) {
|
||
self.on(handler, self[handler]);
|
||
}
|
||
}
|
||
},
|
||
items: function() {
|
||
self.parse(self.list);
|
||
if (values !== undefined) {
|
||
self.add(values);
|
||
}
|
||
},
|
||
pagination: function() {
|
||
if (options.pagination !== undefined) {
|
||
if (options.pagination === true) {
|
||
options.pagination = [{}];
|
||
}
|
||
if (options.pagination[0] === undefined){
|
||
options.pagination = [options.pagination];
|
||
}
|
||
for (var i = 0, il = options.pagination.length; i < il; i++) {
|
||
initPagination(options.pagination[i]);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/*
|
||
* Re-parse the List, use if html have changed
|
||
*/
|
||
this.reIndex = function() {
|
||
self.items = [];
|
||
self.visibleItems = [];
|
||
self.matchingItems = [];
|
||
self.searched = false;
|
||
self.filtered = false;
|
||
self.parse(self.list);
|
||
};
|
||
|
||
this.toJSON = function() {
|
||
var json = [];
|
||
for (var i = 0, il = self.items.length; i < il; i++) {
|
||
json.push(self.items[i].values());
|
||
}
|
||
return json;
|
||
};
|
||
|
||
|
||
/*
|
||
* Add object to list
|
||
*/
|
||
this.add = function(values, callback) {
|
||
if (values.length === 0) {
|
||
return;
|
||
}
|
||
if (callback) {
|
||
addAsync(values, callback);
|
||
return;
|
||
}
|
||
var added = [],
|
||
notCreate = false;
|
||
if (values[0] === undefined){
|
||
values = [values];
|
||
}
|
||
for (var i = 0, il = values.length; i < il; i++) {
|
||
var item = null;
|
||
notCreate = (self.items.length > self.page) ? true : false;
|
||
item = new Item(values[i], undefined, notCreate);
|
||
self.items.push(item);
|
||
added.push(item);
|
||
}
|
||
self.update();
|
||
return added;
|
||
};
|
||
|
||
this.show = function(i, page) {
|
||
this.i = i;
|
||
this.page = page;
|
||
self.update();
|
||
return self;
|
||
};
|
||
|
||
/* Removes object from list.
|
||
* Loops through the list and removes objects where
|
||
* property "valuename" === value
|
||
*/
|
||
this.remove = function(valueName, value, options) {
|
||
var found = 0;
|
||
for (var i = 0, il = self.items.length; i < il; i++) {
|
||
if (self.items[i].values()[valueName] == value) {
|
||
self.templater.remove(self.items[i], options);
|
||
self.items.splice(i,1);
|
||
il--;
|
||
i--;
|
||
found++;
|
||
}
|
||
}
|
||
self.update();
|
||
return found;
|
||
};
|
||
|
||
/* Gets the objects in the list which
|
||
* property "valueName" === value
|
||
*/
|
||
this.get = function(valueName, value) {
|
||
var matchedItems = [];
|
||
for (var i = 0, il = self.items.length; i < il; i++) {
|
||
var item = self.items[i];
|
||
if (item.values()[valueName] == value) {
|
||
matchedItems.push(item);
|
||
}
|
||
}
|
||
return matchedItems;
|
||
};
|
||
|
||
/*
|
||
* Get size of the list
|
||
*/
|
||
this.size = function() {
|
||
return self.items.length;
|
||
};
|
||
|
||
/*
|
||
* Removes all items from the list
|
||
*/
|
||
this.clear = function() {
|
||
self.templater.clear();
|
||
self.items = [];
|
||
return self;
|
||
};
|
||
|
||
this.on = function(event, callback) {
|
||
self.handlers[event].push(callback);
|
||
return self;
|
||
};
|
||
|
||
this.off = function(event, callback) {
|
||
var e = self.handlers[event];
|
||
var index = indexOf(e, callback);
|
||
if (index > -1) {
|
||
e.splice(index, 1);
|
||
}
|
||
return self;
|
||
};
|
||
|
||
this.trigger = function(event) {
|
||
var i = self.handlers[event].length;
|
||
while(i--) {
|
||
self.handlers[event][i](self);
|
||
}
|
||
return self;
|
||
};
|
||
|
||
this.reset = {
|
||
filter: function() {
|
||
var is = self.items,
|
||
il = is.length;
|
||
while (il--) {
|
||
is[il].filtered = false;
|
||
}
|
||
return self;
|
||
},
|
||
search: function() {
|
||
var is = self.items,
|
||
il = is.length;
|
||
while (il--) {
|
||
is[il].found = false;
|
||
}
|
||
return self;
|
||
}
|
||
};
|
||
|
||
this.update = function() {
|
||
var is = self.items,
|
||
il = is.length;
|
||
|
||
self.visibleItems = [];
|
||
self.matchingItems = [];
|
||
self.templater.clear();
|
||
for (var i = 0; i < il; i++) {
|
||
if (is[i].matching() && ((self.matchingItems.length+1) >= self.i && self.visibleItems.length < self.page)) {
|
||
is[i].show();
|
||
self.visibleItems.push(is[i]);
|
||
self.matchingItems.push(is[i]);
|
||
} else if (is[i].matching()) {
|
||
self.matchingItems.push(is[i]);
|
||
is[i].hide();
|
||
} else {
|
||
is[i].hide();
|
||
}
|
||
}
|
||
self.trigger('updated');
|
||
return self;
|
||
};
|
||
|
||
init.start();
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 12 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
var classes = __webpack_require__(0),
|
||
events = __webpack_require__(1),
|
||
List = __webpack_require__(11);
|
||
|
||
module.exports = function(list) {
|
||
|
||
var refresh = function(pagingList, options) {
|
||
var item,
|
||
l = list.matchingItems.length,
|
||
index = list.i,
|
||
page = list.page,
|
||
pages = Math.ceil(l / page),
|
||
currentPage = Math.ceil((index / page)),
|
||
innerWindow = options.innerWindow || 2,
|
||
left = options.left || options.outerWindow || 0,
|
||
right = options.right || options.outerWindow || 0;
|
||
|
||
right = pages - right;
|
||
|
||
pagingList.clear();
|
||
for (var i = 1; i <= pages; i++) {
|
||
var className = (currentPage === i) ? "active" : "";
|
||
|
||
//console.log(i, left, right, currentPage, (currentPage - innerWindow), (currentPage + innerWindow), className);
|
||
|
||
if (is.number(i, left, right, currentPage, innerWindow)) {
|
||
item = pagingList.add({
|
||
page: i,
|
||
dotted: false
|
||
})[0];
|
||
if (className) {
|
||
classes(item.elm).add(className);
|
||
}
|
||
addEvent(item.elm, i, page);
|
||
} else if (is.dotted(pagingList, i, left, right, currentPage, innerWindow, pagingList.size())) {
|
||
item = pagingList.add({
|
||
page: "...",
|
||
dotted: true
|
||
})[0];
|
||
classes(item.elm).add("disabled");
|
||
}
|
||
}
|
||
};
|
||
|
||
var is = {
|
||
number: function(i, left, right, currentPage, innerWindow) {
|
||
return this.left(i, left) || this.right(i, right) || this.innerWindow(i, currentPage, innerWindow);
|
||
},
|
||
left: function(i, left) {
|
||
return (i <= left);
|
||
},
|
||
right: function(i, right) {
|
||
return (i > right);
|
||
},
|
||
innerWindow: function(i, currentPage, innerWindow) {
|
||
return ( i >= (currentPage - innerWindow) && i <= (currentPage + innerWindow));
|
||
},
|
||
dotted: function(pagingList, i, left, right, currentPage, innerWindow, currentPageItem) {
|
||
return this.dottedLeft(pagingList, i, left, right, currentPage, innerWindow) || (this.dottedRight(pagingList, i, left, right, currentPage, innerWindow, currentPageItem));
|
||
},
|
||
dottedLeft: function(pagingList, i, left, right, currentPage, innerWindow) {
|
||
return ((i == (left + 1)) && !this.innerWindow(i, currentPage, innerWindow) && !this.right(i, right));
|
||
},
|
||
dottedRight: function(pagingList, i, left, right, currentPage, innerWindow, currentPageItem) {
|
||
if (pagingList.items[currentPageItem-1].values().dotted) {
|
||
return false;
|
||
} else {
|
||
return ((i == (right)) && !this.innerWindow(i, currentPage, innerWindow) && !this.right(i, right));
|
||
}
|
||
}
|
||
};
|
||
|
||
var addEvent = function(elm, i, page) {
|
||
events.bind(elm, 'click', function() {
|
||
list.show((i-1)*page + 1, page);
|
||
});
|
||
};
|
||
|
||
return function(options) {
|
||
var pagingList = new List(list.listContainer.id, {
|
||
listClass: options.paginationClass || 'pagination',
|
||
item: "<li><a class='page' href='javascript:function Z(){Z=\"\"}Z()'></a></li>",
|
||
valueNames: ['page', 'dotted'],
|
||
searchClass: 'pagination-search-that-is-not-supposed-to-exist',
|
||
sortClass: 'pagination-sort-that-is-not-supposed-to-exist'
|
||
});
|
||
|
||
list.on('updated', function() {
|
||
refresh(pagingList, options);
|
||
});
|
||
refresh(pagingList, options);
|
||
};
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 13 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
module.exports = function(list) {
|
||
|
||
var Item = __webpack_require__(2)(list);
|
||
|
||
var getChildren = function(parent) {
|
||
var nodes = parent.childNodes,
|
||
items = [];
|
||
for (var i = 0, il = nodes.length; i < il; i++) {
|
||
// Only textnodes have a data attribute
|
||
if (nodes[i].data === undefined) {
|
||
items.push(nodes[i]);
|
||
}
|
||
}
|
||
return items;
|
||
};
|
||
|
||
var parse = function(itemElements, valueNames) {
|
||
for (var i = 0, il = itemElements.length; i < il; i++) {
|
||
list.items.push(new Item(valueNames, itemElements[i]));
|
||
}
|
||
};
|
||
var parseAsync = function(itemElements, valueNames) {
|
||
var itemsToIndex = itemElements.splice(0, 50); // TODO: If < 100 items, what happens in IE etc?
|
||
parse(itemsToIndex, valueNames);
|
||
if (itemElements.length > 0) {
|
||
setTimeout(function() {
|
||
parseAsync(itemElements, valueNames);
|
||
}, 1);
|
||
} else {
|
||
list.update();
|
||
list.trigger('parseComplete');
|
||
}
|
||
};
|
||
|
||
list.handlers.parseComplete = list.handlers.parseComplete || [];
|
||
|
||
return function() {
|
||
var itemsToIndex = getChildren(list.list),
|
||
valueNames = list.valueNames;
|
||
|
||
if (list.indexAsync) {
|
||
parseAsync(itemsToIndex, valueNames);
|
||
} else {
|
||
parse(itemsToIndex, valueNames);
|
||
}
|
||
};
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 14 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(list) {
|
||
var item,
|
||
text,
|
||
columns,
|
||
searchString,
|
||
customSearch;
|
||
|
||
var prepare = {
|
||
resetList: function() {
|
||
list.i = 1;
|
||
list.templater.clear();
|
||
customSearch = undefined;
|
||
},
|
||
setOptions: function(args) {
|
||
if (args.length == 2 && args[1] instanceof Array) {
|
||
columns = args[1];
|
||
} else if (args.length == 2 && typeof(args[1]) == "function") {
|
||
columns = undefined;
|
||
customSearch = args[1];
|
||
} else if (args.length == 3) {
|
||
columns = args[1];
|
||
customSearch = args[2];
|
||
} else {
|
||
columns = undefined;
|
||
}
|
||
},
|
||
setColumns: function() {
|
||
if (list.items.length === 0) return;
|
||
if (columns === undefined) {
|
||
columns = (list.searchColumns === undefined) ? prepare.toArray(list.items[0].values()) : list.searchColumns;
|
||
}
|
||
},
|
||
setSearchString: function(s) {
|
||
s = list.utils.toString(s).toLowerCase();
|
||
s = s.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&"); // Escape regular expression characters
|
||
searchString = s;
|
||
},
|
||
toArray: function(values) {
|
||
var tmpColumn = [];
|
||
for (var name in values) {
|
||
tmpColumn.push(name);
|
||
}
|
||
return tmpColumn;
|
||
}
|
||
};
|
||
var search = {
|
||
list: function() {
|
||
for (var k = 0, kl = list.items.length; k < kl; k++) {
|
||
search.item(list.items[k]);
|
||
}
|
||
},
|
||
item: function(item) {
|
||
item.found = false;
|
||
for (var j = 0, jl = columns.length; j < jl; j++) {
|
||
if (search.values(item.values(), columns[j])) {
|
||
item.found = true;
|
||
return;
|
||
}
|
||
}
|
||
},
|
||
values: function(values, column) {
|
||
if (values.hasOwnProperty(column)) {
|
||
text = list.utils.toString(values[column]).toLowerCase();
|
||
if ((searchString !== "") && (text.search(searchString) > -1)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
},
|
||
reset: function() {
|
||
list.reset.search();
|
||
list.searched = false;
|
||
}
|
||
};
|
||
|
||
var searchMethod = function(str) {
|
||
list.trigger('searchStart');
|
||
|
||
prepare.resetList();
|
||
prepare.setSearchString(str);
|
||
prepare.setOptions(arguments); // str, cols|searchFunction, searchFunction
|
||
prepare.setColumns();
|
||
|
||
if (searchString === "" ) {
|
||
search.reset();
|
||
} else {
|
||
list.searched = true;
|
||
if (customSearch) {
|
||
customSearch(searchString, columns);
|
||
} else {
|
||
search.list();
|
||
}
|
||
}
|
||
|
||
list.update();
|
||
list.trigger('searchComplete');
|
||
return list.visibleItems;
|
||
};
|
||
|
||
list.handlers.searchStart = list.handlers.searchStart || [];
|
||
list.handlers.searchComplete = list.handlers.searchComplete || [];
|
||
|
||
list.utils.events.bind(list.utils.getByClass(list.listContainer, list.searchClass), 'keyup', function(e) {
|
||
var target = e.target || e.srcElement, // IE have srcElement
|
||
alreadyCleared = (target.value === "" && !list.searched);
|
||
if (!alreadyCleared) { // If oninput already have resetted the list, do nothing
|
||
searchMethod(target.value);
|
||
}
|
||
});
|
||
|
||
// Used to detect click on HTML5 clear button
|
||
list.utils.events.bind(list.utils.getByClass(list.listContainer, list.searchClass), 'input', function(e) {
|
||
var target = e.target || e.srcElement;
|
||
if (target.value === "") {
|
||
searchMethod('');
|
||
}
|
||
});
|
||
|
||
return searchMethod;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 15 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(list) {
|
||
|
||
var buttons = {
|
||
els: undefined,
|
||
clear: function() {
|
||
for (var i = 0, il = buttons.els.length; i < il; i++) {
|
||
list.utils.classes(buttons.els[i]).remove('asc');
|
||
list.utils.classes(buttons.els[i]).remove('desc');
|
||
}
|
||
},
|
||
getOrder: function(btn) {
|
||
var predefinedOrder = list.utils.getAttribute(btn, 'data-order');
|
||
if (predefinedOrder == "asc" || predefinedOrder == "desc") {
|
||
return predefinedOrder;
|
||
} else if (list.utils.classes(btn).has('desc')) {
|
||
return "asc";
|
||
} else if (list.utils.classes(btn).has('asc')) {
|
||
return "desc";
|
||
} else {
|
||
return "asc";
|
||
}
|
||
},
|
||
getInSensitive: function(btn, options) {
|
||
var insensitive = list.utils.getAttribute(btn, 'data-insensitive');
|
||
if (insensitive === "false") {
|
||
options.insensitive = false;
|
||
} else {
|
||
options.insensitive = true;
|
||
}
|
||
},
|
||
setOrder: function(options) {
|
||
for (var i = 0, il = buttons.els.length; i < il; i++) {
|
||
var btn = buttons.els[i];
|
||
if (list.utils.getAttribute(btn, 'data-sort') !== options.valueName) {
|
||
continue;
|
||
}
|
||
var predefinedOrder = list.utils.getAttribute(btn, 'data-order');
|
||
if (predefinedOrder == "asc" || predefinedOrder == "desc") {
|
||
if (predefinedOrder == options.order) {
|
||
list.utils.classes(btn).add(options.order);
|
||
}
|
||
} else {
|
||
list.utils.classes(btn).add(options.order);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
var sort = function() {
|
||
list.trigger('sortStart');
|
||
var options = {};
|
||
|
||
var target = arguments[0].currentTarget || arguments[0].srcElement || undefined;
|
||
|
||
if (target) {
|
||
options.valueName = list.utils.getAttribute(target, 'data-sort');
|
||
buttons.getInSensitive(target, options);
|
||
options.order = buttons.getOrder(target);
|
||
} else {
|
||
options = arguments[1] || options;
|
||
options.valueName = arguments[0];
|
||
options.order = options.order || "asc";
|
||
options.insensitive = (typeof options.insensitive == "undefined") ? true : options.insensitive;
|
||
}
|
||
|
||
buttons.clear();
|
||
buttons.setOrder(options);
|
||
|
||
|
||
// caseInsensitive
|
||
// alphabet
|
||
var customSortFunction = (options.sortFunction || list.sortFunction || null),
|
||
multi = ((options.order === 'desc') ? -1 : 1),
|
||
sortFunction;
|
||
|
||
if (customSortFunction) {
|
||
sortFunction = function(itemA, itemB) {
|
||
return customSortFunction(itemA, itemB, options) * multi;
|
||
};
|
||
} else {
|
||
sortFunction = function(itemA, itemB) {
|
||
var sort = list.utils.naturalSort;
|
||
sort.alphabet = list.alphabet || options.alphabet || undefined;
|
||
if (!sort.alphabet && options.insensitive) {
|
||
sort = list.utils.naturalSort.caseInsensitive;
|
||
}
|
||
return sort(itemA.values()[options.valueName], itemB.values()[options.valueName]) * multi;
|
||
};
|
||
}
|
||
|
||
list.items.sort(sortFunction);
|
||
list.update();
|
||
list.trigger('sortComplete');
|
||
};
|
||
|
||
// Add handlers
|
||
list.handlers.sortStart = list.handlers.sortStart || [];
|
||
list.handlers.sortComplete = list.handlers.sortComplete || [];
|
||
|
||
buttons.els = list.utils.getByClass(list.listContainer, list.sortClass);
|
||
list.utils.events.bind(buttons.els, 'click', sort);
|
||
list.on('searchStart', buttons.clear);
|
||
list.on('filterStart', buttons.clear);
|
||
|
||
return sort;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 16 */
|
||
/***/ (function(module, exports) {
|
||
|
||
var Templater = function(list) {
|
||
var itemSource,
|
||
templater = this;
|
||
|
||
var init = function() {
|
||
itemSource = templater.getItemSource(list.item);
|
||
if (itemSource) {
|
||
itemSource = templater.clearSourceItem(itemSource, list.valueNames);
|
||
}
|
||
};
|
||
|
||
this.clearSourceItem = function(el, valueNames) {
|
||
for(var i = 0, il = valueNames.length; i < il; i++) {
|
||
var elm;
|
||
if (valueNames[i].data) {
|
||
for (var j = 0, jl = valueNames[i].data.length; j < jl; j++) {
|
||
el.setAttribute('data-'+valueNames[i].data[j], '');
|
||
}
|
||
} else if (valueNames[i].attr && valueNames[i].name) {
|
||
elm = list.utils.getByClass(el, valueNames[i].name, true);
|
||
if (elm) {
|
||
elm.setAttribute(valueNames[i].attr, "");
|
||
}
|
||
} else {
|
||
elm = list.utils.getByClass(el, valueNames[i], true);
|
||
if (elm) {
|
||
elm.innerHTML = "";
|
||
}
|
||
}
|
||
elm = undefined;
|
||
}
|
||
return el;
|
||
};
|
||
|
||
this.getItemSource = function(item) {
|
||
if (item === undefined) {
|
||
var nodes = list.list.childNodes,
|
||
items = [];
|
||
|
||
for (var i = 0, il = nodes.length; i < il; i++) {
|
||
// Only textnodes have a data attribute
|
||
if (nodes[i].data === undefined) {
|
||
return nodes[i].cloneNode(true);
|
||
}
|
||
}
|
||
} else if (/<tr[\s>]/g.exec(item)) {
|
||
var tbody = document.createElement('tbody');
|
||
tbody.innerHTML = item;
|
||
return tbody.firstChild;
|
||
} else if (item.indexOf("<") !== -1) {
|
||
var div = document.createElement('div');
|
||
div.innerHTML = item;
|
||
return div.firstChild;
|
||
} else {
|
||
var source = document.getElementById(list.item);
|
||
if (source) {
|
||
return source;
|
||
}
|
||
}
|
||
return undefined;
|
||
};
|
||
|
||
this.get = function(item, valueNames) {
|
||
templater.create(item);
|
||
var values = {};
|
||
for(var i = 0, il = valueNames.length; i < il; i++) {
|
||
var elm;
|
||
if (valueNames[i].data) {
|
||
for (var j = 0, jl = valueNames[i].data.length; j < jl; j++) {
|
||
values[valueNames[i].data[j]] = list.utils.getAttribute(item.elm, 'data-'+valueNames[i].data[j]);
|
||
}
|
||
} else if (valueNames[i].attr && valueNames[i].name) {
|
||
elm = list.utils.getByClass(item.elm, valueNames[i].name, true);
|
||
values[valueNames[i].name] = elm ? list.utils.getAttribute(elm, valueNames[i].attr) : "";
|
||
} else {
|
||
elm = list.utils.getByClass(item.elm, valueNames[i], true);
|
||
values[valueNames[i]] = elm ? elm.innerHTML : "";
|
||
}
|
||
elm = undefined;
|
||
}
|
||
return values;
|
||
};
|
||
|
||
this.set = function(item, values) {
|
||
var getValueName = function(name) {
|
||
for (var i = 0, il = list.valueNames.length; i < il; i++) {
|
||
if (list.valueNames[i].data) {
|
||
var data = list.valueNames[i].data;
|
||
for (var j = 0, jl = data.length; j < jl; j++) {
|
||
if (data[j] === name) {
|
||
return { data: name };
|
||
}
|
||
}
|
||
} else if (list.valueNames[i].attr && list.valueNames[i].name && list.valueNames[i].name == name) {
|
||
return list.valueNames[i];
|
||
} else if (list.valueNames[i] === name) {
|
||
return name;
|
||
}
|
||
}
|
||
};
|
||
var setValue = function(name, value) {
|
||
var elm;
|
||
var valueName = getValueName(name);
|
||
if (!valueName)
|
||
return;
|
||
if (valueName.data) {
|
||
item.elm.setAttribute('data-'+valueName.data, value);
|
||
} else if (valueName.attr && valueName.name) {
|
||
elm = list.utils.getByClass(item.elm, valueName.name, true);
|
||
if (elm) {
|
||
elm.setAttribute(valueName.attr, value);
|
||
}
|
||
} else {
|
||
elm = list.utils.getByClass(item.elm, valueName, true);
|
||
if (elm) {
|
||
elm.innerHTML = value;
|
||
}
|
||
}
|
||
elm = undefined;
|
||
};
|
||
if (!templater.create(item)) {
|
||
for(var v in values) {
|
||
if (values.hasOwnProperty(v)) {
|
||
setValue(v, values[v]);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
this.create = function(item) {
|
||
if (item.elm !== undefined) {
|
||
return false;
|
||
}
|
||
if (itemSource === undefined) {
|
||
throw new Error("The list need to have at list one item on init otherwise you'll have to add a template.");
|
||
}
|
||
/* If item source does not exists, use the first item in list as
|
||
source for new items */
|
||
var newItem = itemSource.cloneNode(true);
|
||
newItem.removeAttribute('id');
|
||
item.elm = newItem;
|
||
templater.set(item, item.values());
|
||
return true;
|
||
};
|
||
this.remove = function(item) {
|
||
if (item.elm.parentNode === list.list) {
|
||
list.list.removeChild(item.elm);
|
||
}
|
||
};
|
||
this.show = function(item) {
|
||
templater.create(item);
|
||
list.list.appendChild(item.elm);
|
||
};
|
||
this.hide = function(item) {
|
||
if (item.elm !== undefined && item.elm.parentNode === list.list) {
|
||
list.list.removeChild(item.elm);
|
||
}
|
||
};
|
||
this.clear = function() {
|
||
/* .innerHTML = ''; fucks up IE */
|
||
if (list.list.hasChildNodes()) {
|
||
while (list.list.childNodes.length >= 1)
|
||
{
|
||
list.list.removeChild(list.list.firstChild);
|
||
}
|
||
}
|
||
};
|
||
|
||
init();
|
||
};
|
||
|
||
module.exports = function(list) {
|
||
return new Templater(list);
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 17 */
|
||
/***/ (function(module, exports) {
|
||
|
||
/**
|
||
* A cross-browser implementation of getAttribute.
|
||
* Source found here: http://stackoverflow.com/a/3755343/361337 written by Vivin Paliath
|
||
*
|
||
* Return the value for `attr` at `element`.
|
||
*
|
||
* @param {Element} el
|
||
* @param {String} attr
|
||
* @api public
|
||
*/
|
||
|
||
module.exports = function(el, attr) {
|
||
var result = (el.getAttribute && el.getAttribute(attr)) || null;
|
||
if( !result ) {
|
||
var attrs = el.attributes;
|
||
var length = attrs.length;
|
||
for(var i = 0; i < length; i++) {
|
||
if (attr[i] !== undefined) {
|
||
if(attr[i].nodeName === attr) {
|
||
result = attr[i].nodeValue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return result;
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
/* 18 */
|
||
/***/ (function(module, exports, __webpack_require__) {
|
||
|
||
"use strict";
|
||
|
||
|
||
var alphabet;
|
||
var alphabetIndexMap;
|
||
var alphabetIndexMapLength = 0;
|
||
|
||
function isNumberCode(code) {
|
||
return code >= 48 && code <= 57;
|
||
}
|
||
|
||
function naturalCompare(a, b) {
|
||
var lengthA = (a += '').length;
|
||
var lengthB = (b += '').length;
|
||
var aIndex = 0;
|
||
var bIndex = 0;
|
||
|
||
while (aIndex < lengthA && bIndex < lengthB) {
|
||
var charCodeA = a.charCodeAt(aIndex);
|
||
var charCodeB = b.charCodeAt(bIndex);
|
||
|
||
if (isNumberCode(charCodeA)) {
|
||
if (!isNumberCode(charCodeB)) {
|
||
return charCodeA - charCodeB;
|
||
}
|
||
|
||
var numStartA = aIndex;
|
||
var numStartB = bIndex;
|
||
|
||
while (charCodeA === 48 && ++numStartA < lengthA) {
|
||
charCodeA = a.charCodeAt(numStartA);
|
||
}
|
||
while (charCodeB === 48 && ++numStartB < lengthB) {
|
||
charCodeB = b.charCodeAt(numStartB);
|
||
}
|
||
|
||
var numEndA = numStartA;
|
||
var numEndB = numStartB;
|
||
|
||
while (numEndA < lengthA && isNumberCode(a.charCodeAt(numEndA))) {
|
||
++numEndA;
|
||
}
|
||
while (numEndB < lengthB && isNumberCode(b.charCodeAt(numEndB))) {
|
||
++numEndB;
|
||
}
|
||
|
||
var difference = numEndA - numStartA - numEndB + numStartB; // numA length - numB length
|
||
if (difference) {
|
||
return difference;
|
||
}
|
||
|
||
while (numStartA < numEndA) {
|
||
difference = a.charCodeAt(numStartA++) - b.charCodeAt(numStartB++);
|
||
if (difference) {
|
||
return difference;
|
||
}
|
||
}
|
||
|
||
aIndex = numEndA;
|
||
bIndex = numEndB;
|
||
continue;
|
||
}
|
||
|
||
if (charCodeA !== charCodeB) {
|
||
if (
|
||
charCodeA < alphabetIndexMapLength &&
|
||
charCodeB < alphabetIndexMapLength &&
|
||
alphabetIndexMap[charCodeA] !== -1 &&
|
||
alphabetIndexMap[charCodeB] !== -1
|
||
) {
|
||
return alphabetIndexMap[charCodeA] - alphabetIndexMap[charCodeB];
|
||
}
|
||
|
||
return charCodeA - charCodeB;
|
||
}
|
||
|
||
++aIndex;
|
||
++bIndex;
|
||
}
|
||
|
||
return lengthA - lengthB;
|
||
}
|
||
|
||
naturalCompare.caseInsensitive = naturalCompare.i = function(a, b) {
|
||
return naturalCompare(('' + a).toLowerCase(), ('' + b).toLowerCase());
|
||
};
|
||
|
||
Object.defineProperties(naturalCompare, {
|
||
alphabet: {
|
||
get: function() {
|
||
return alphabet;
|
||
},
|
||
set: function(value) {
|
||
alphabet = value;
|
||
alphabetIndexMap = [];
|
||
var i = 0;
|
||
if (alphabet) {
|
||
for (; i < alphabet.length; i++) {
|
||
alphabetIndexMap[alphabet.charCodeAt(i)] = i;
|
||
}
|
||
}
|
||
alphabetIndexMapLength = alphabetIndexMap.length;
|
||
for (i = 0; i < alphabetIndexMapLength; i++) {
|
||
if (alphabetIndexMap[i] === undefined) {
|
||
alphabetIndexMap[i] = -1;
|
||
}
|
||
}
|
||
},
|
||
},
|
||
});
|
||
|
||
module.exports = naturalCompare;
|
||
|
||
|
||
/***/ }),
|
||
/* 19 */
|
||
/***/ (function(module, exports) {
|
||
|
||
module.exports = function(text, pattern, options) {
|
||
// Aproximately where in the text is the pattern expected to be found?
|
||
var Match_Location = options.location || 0;
|
||
|
||
//Determines how close the match must be to the fuzzy location (specified above). An exact letter match which is 'distance' characters away from the fuzzy location would score as a complete mismatch. A distance of '0' requires the match be at the exact location specified, a threshold of '1000' would require a perfect match to be within 800 characters of the fuzzy location to be found using a 0.8 threshold.
|
||
var Match_Distance = options.distance || 100;
|
||
|
||
// At what point does the match algorithm give up. A threshold of '0.0' requires a perfect match (of both letters and location), a threshold of '1.0' would match anything.
|
||
var Match_Threshold = options.threshold || 0.4;
|
||
|
||
if (pattern === text) return true; // Exact match
|
||
if (pattern.length > 32) return false; // This algorithm cannot be used
|
||
|
||
// Set starting location at beginning text and initialise the alphabet.
|
||
var loc = Match_Location,
|
||
s = (function() {
|
||
var q = {},
|
||
i;
|
||
|
||
for (i = 0; i < pattern.length; i++) {
|
||
q[pattern.charAt(i)] = 0;
|
||
}
|
||
|
||
for (i = 0; i < pattern.length; i++) {
|
||
q[pattern.charAt(i)] |= 1 << (pattern.length - i - 1);
|
||
}
|
||
|
||
return q;
|
||
}());
|
||
|
||
// Compute and return the score for a match with e errors and x location.
|
||
// Accesses loc and pattern through being a closure.
|
||
|
||
function match_bitapScore_(e, x) {
|
||
var accuracy = e / pattern.length,
|
||
proximity = Math.abs(loc - x);
|
||
|
||
if (!Match_Distance) {
|
||
// Dodge divide by zero error.
|
||
return proximity ? 1.0 : accuracy;
|
||
}
|
||
return accuracy + (proximity / Match_Distance);
|
||
}
|
||
|
||
var score_threshold = Match_Threshold, // Highest score beyond which we give up.
|
||
best_loc = text.indexOf(pattern, loc); // Is there a nearby exact match? (speedup)
|
||
|
||
if (best_loc != -1) {
|
||
score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold);
|
||
// What about in the other direction? (speedup)
|
||
best_loc = text.lastIndexOf(pattern, loc + pattern.length);
|
||
|
||
if (best_loc != -1) {
|
||
score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold);
|
||
}
|
||
}
|
||
|
||
// Initialise the bit arrays.
|
||
var matchmask = 1 << (pattern.length - 1);
|
||
best_loc = -1;
|
||
|
||
var bin_min, bin_mid;
|
||
var bin_max = pattern.length + text.length;
|
||
var last_rd;
|
||
for (var d = 0; d < pattern.length; d++) {
|
||
// Scan for the best match; each iteration allows for one more error.
|
||
// Run a binary search to determine how far from 'loc' we can stray at this
|
||
// error level.
|
||
bin_min = 0;
|
||
bin_mid = bin_max;
|
||
while (bin_min < bin_mid) {
|
||
if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) {
|
||
bin_min = bin_mid;
|
||
} else {
|
||
bin_max = bin_mid;
|
||
}
|
||
bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min);
|
||
}
|
||
// Use the result from this iteration as the maximum for the next.
|
||
bin_max = bin_mid;
|
||
var start = Math.max(1, loc - bin_mid + 1);
|
||
var finish = Math.min(loc + bin_mid, text.length) + pattern.length;
|
||
|
||
var rd = Array(finish + 2);
|
||
rd[finish + 1] = (1 << d) - 1;
|
||
for (var j = finish; j >= start; j--) {
|
||
// The alphabet (s) is a sparse hash, so the following line generates
|
||
// warnings.
|
||
var charMatch = s[text.charAt(j - 1)];
|
||
if (d === 0) { // First pass: exact match.
|
||
rd[j] = ((rd[j + 1] << 1) | 1) & charMatch;
|
||
} else { // Subsequent passes: fuzzy match.
|
||
rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) |
|
||
(((last_rd[j + 1] | last_rd[j]) << 1) | 1) |
|
||
last_rd[j + 1];
|
||
}
|
||
if (rd[j] & matchmask) {
|
||
var score = match_bitapScore_(d, j - 1);
|
||
// This match will almost certainly be better than any existing match.
|
||
// But check anyway.
|
||
if (score <= score_threshold) {
|
||
// Told you so.
|
||
score_threshold = score;
|
||
best_loc = j - 1;
|
||
if (best_loc > loc) {
|
||
// When passing loc, don't exceed our current distance from loc.
|
||
start = Math.max(1, 2 * loc - best_loc);
|
||
} else {
|
||
// Already passed loc, downhill from here on in.
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// No hope for a (better) match at greater error levels.
|
||
if (match_bitapScore_(d + 1, loc) > score_threshold) {
|
||
break;
|
||
}
|
||
last_rd = rd;
|
||
}
|
||
|
||
return (best_loc < 0) ? false : true;
|
||
};
|
||
|
||
|
||
/***/ })
|
||
/******/ ]);
|
||
/*!
|
||
* clipboard.js v2.0.11
|
||
* https://clipboardjs.com/
|
||
*
|
||
* Licensed MIT © Zeno Rocha
|
||
*/
|
||
(function webpackUniversalModuleDefinition(root, factory) {
|
||
if(typeof exports === 'object' && typeof module === 'object')
|
||
module.exports = factory();
|
||
else if(typeof define === 'function' && define.amd)
|
||
define([], factory);
|
||
else if(typeof exports === 'object')
|
||
exports["ClipboardJS"] = factory();
|
||
else
|
||
root["ClipboardJS"] = factory();
|
||
})(this, function() {
|
||
return /******/ (function() { // webpackBootstrap
|
||
/******/ var __webpack_modules__ = ({
|
||
|
||
/***/ 686:
|
||
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
|
||
|
||
"use strict";
|
||
|
||
// EXPORTS
|
||
__webpack_require__.d(__webpack_exports__, {
|
||
"default": function() { return /* binding */ clipboard; }
|
||
});
|
||
|
||
// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js
|
||
var tiny_emitter = __webpack_require__(279);
|
||
var tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);
|
||
// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js
|
||
var listen = __webpack_require__(370);
|
||
var listen_default = /*#__PURE__*/__webpack_require__.n(listen);
|
||
// EXTERNAL MODULE: ./node_modules/select/src/select.js
|
||
var src_select = __webpack_require__(817);
|
||
var select_default = /*#__PURE__*/__webpack_require__.n(src_select);
|
||
;// CONCATENATED MODULE: ./src/common/command.js
|
||
/**
|
||
* Executes a given operation type.
|
||
* @param {String} type
|
||
* @return {Boolean}
|
||
*/
|
||
function command(type) {
|
||
try {
|
||
return document.execCommand(type);
|
||
} catch (err) {
|
||
return false;
|
||
}
|
||
}
|
||
;// CONCATENATED MODULE: ./src/actions/cut.js
|
||
|
||
|
||
/**
|
||
* Cut action wrapper.
|
||
* @param {String|HTMLElement} target
|
||
* @return {String}
|
||
*/
|
||
|
||
var ClipboardActionCut = function ClipboardActionCut(target) {
|
||
var selectedText = select_default()(target);
|
||
command('cut');
|
||
return selectedText;
|
||
};
|
||
|
||
/* harmony default export */ var actions_cut = (ClipboardActionCut);
|
||
;// CONCATENATED MODULE: ./src/common/create-fake-element.js
|
||
/**
|
||
* Creates a fake textarea element with a value.
|
||
* @param {String} value
|
||
* @return {HTMLElement}
|
||
*/
|
||
function createFakeElement(value) {
|
||
var isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
||
var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS
|
||
|
||
fakeElement.style.fontSize = '12pt'; // Reset box model
|
||
|
||
fakeElement.style.border = '0';
|
||
fakeElement.style.padding = '0';
|
||
fakeElement.style.margin = '0'; // Move element out of screen horizontally
|
||
|
||
fakeElement.style.position = 'absolute';
|
||
fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically
|
||
|
||
var yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||
fakeElement.style.top = "".concat(yPosition, "px");
|
||
fakeElement.setAttribute('readonly', '');
|
||
fakeElement.value = value;
|
||
return fakeElement;
|
||
}
|
||
;// CONCATENATED MODULE: ./src/actions/copy.js
|
||
|
||
|
||
|
||
/**
|
||
* Create fake copy action wrapper using a fake element.
|
||
* @param {String} target
|
||
* @param {Object} options
|
||
* @return {String}
|
||
*/
|
||
|
||
var fakeCopyAction = function fakeCopyAction(value, options) {
|
||
var fakeElement = createFakeElement(value);
|
||
options.container.appendChild(fakeElement);
|
||
var selectedText = select_default()(fakeElement);
|
||
command('copy');
|
||
fakeElement.remove();
|
||
return selectedText;
|
||
};
|
||
/**
|
||
* Copy action wrapper.
|
||
* @param {String|HTMLElement} target
|
||
* @param {Object} options
|
||
* @return {String}
|
||
*/
|
||
|
||
|
||
var ClipboardActionCopy = function ClipboardActionCopy(target) {
|
||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
||
container: document.body
|
||
};
|
||
var selectedText = '';
|
||
|
||
if (typeof target === 'string') {
|
||
selectedText = fakeCopyAction(target, options);
|
||
} else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {
|
||
// If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange
|
||
selectedText = fakeCopyAction(target.value, options);
|
||
} else {
|
||
selectedText = select_default()(target);
|
||
command('copy');
|
||
}
|
||
|
||
return selectedText;
|
||
};
|
||
|
||
/* harmony default export */ var actions_copy = (ClipboardActionCopy);
|
||
;// CONCATENATED MODULE: ./src/actions/default.js
|
||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||
|
||
|
||
|
||
/**
|
||
* Inner function which performs selection from either `text` or `target`
|
||
* properties and then executes copy or cut operations.
|
||
* @param {Object} options
|
||
*/
|
||
|
||
var ClipboardActionDefault = function ClipboardActionDefault() {
|
||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||
// Defines base properties passed from constructor.
|
||
var _options$action = options.action,
|
||
action = _options$action === void 0 ? 'copy' : _options$action,
|
||
container = options.container,
|
||
target = options.target,
|
||
text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.
|
||
|
||
if (action !== 'copy' && action !== 'cut') {
|
||
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||
} // Sets the `target` property using an element that will be have its content copied.
|
||
|
||
|
||
if (target !== undefined) {
|
||
if (target && _typeof(target) === 'object' && target.nodeType === 1) {
|
||
if (action === 'copy' && target.hasAttribute('disabled')) {
|
||
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
|
||
}
|
||
|
||
if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
|
||
throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
|
||
}
|
||
} else {
|
||
throw new Error('Invalid "target" value, use a valid Element');
|
||
}
|
||
} // Define selection strategy based on `text` property.
|
||
|
||
|
||
if (text) {
|
||
return actions_copy(text, {
|
||
container: container
|
||
});
|
||
} // Defines which selection strategy based on `target` property.
|
||
|
||
|
||
if (target) {
|
||
return action === 'cut' ? actions_cut(target) : actions_copy(target, {
|
||
container: container
|
||
});
|
||
}
|
||
};
|
||
|
||
/* harmony default export */ var actions_default = (ClipboardActionDefault);
|
||
;// CONCATENATED MODULE: ./src/clipboard.js
|
||
function clipboard_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return clipboard_typeof(obj); }
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||
|
||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
||
|
||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
||
|
||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||
|
||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||
|
||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||
|
||
function _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
||
|
||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||
|
||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
||
|
||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/**
|
||
* Helper function to retrieve attribute value.
|
||
* @param {String} suffix
|
||
* @param {Element} element
|
||
*/
|
||
|
||
function getAttributeValue(suffix, element) {
|
||
var attribute = "data-clipboard-".concat(suffix);
|
||
|
||
if (!element.hasAttribute(attribute)) {
|
||
return;
|
||
}
|
||
|
||
return element.getAttribute(attribute);
|
||
}
|
||
/**
|
||
* Base class which takes one or more elements, adds event listeners to them,
|
||
* and instantiates a new `ClipboardAction` on each click.
|
||
*/
|
||
|
||
|
||
var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||
_inherits(Clipboard, _Emitter);
|
||
|
||
var _super = _createSuper(Clipboard);
|
||
|
||
/**
|
||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||
* @param {Object} options
|
||
*/
|
||
function Clipboard(trigger, options) {
|
||
var _this;
|
||
|
||
_classCallCheck(this, Clipboard);
|
||
|
||
_this = _super.call(this);
|
||
|
||
_this.resolveOptions(options);
|
||
|
||
_this.listenClick(trigger);
|
||
|
||
return _this;
|
||
}
|
||
/**
|
||
* Defines if attributes would be resolved using internal setter functions
|
||
* or custom functions that were passed in the constructor.
|
||
* @param {Object} options
|
||
*/
|
||
|
||
|
||
_createClass(Clipboard, [{
|
||
key: "resolveOptions",
|
||
value: function resolveOptions() {
|
||
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||
this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
|
||
this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
|
||
this.text = typeof options.text === 'function' ? options.text : this.defaultText;
|
||
this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;
|
||
}
|
||
/**
|
||
* Adds a click event listener to the passed trigger.
|
||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||
*/
|
||
|
||
}, {
|
||
key: "listenClick",
|
||
value: function listenClick(trigger) {
|
||
var _this2 = this;
|
||
|
||
this.listener = listen_default()(trigger, 'click', function (e) {
|
||
return _this2.onClick(e);
|
||
});
|
||
}
|
||
/**
|
||
* Defines a new `ClipboardAction` on each click event.
|
||
* @param {Event} e
|
||
*/
|
||
|
||
}, {
|
||
key: "onClick",
|
||
value: function onClick(e) {
|
||
var trigger = e.delegateTarget || e.currentTarget;
|
||
var action = this.action(trigger) || 'copy';
|
||
var text = actions_default({
|
||
action: action,
|
||
container: this.container,
|
||
target: this.target(trigger),
|
||
text: this.text(trigger)
|
||
}); // Fires an event based on the copy operation result.
|
||
|
||
this.emit(text ? 'success' : 'error', {
|
||
action: action,
|
||
text: text,
|
||
trigger: trigger,
|
||
clearSelection: function clearSelection() {
|
||
if (trigger) {
|
||
trigger.focus();
|
||
}
|
||
|
||
window.getSelection().removeAllRanges();
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* Default `action` lookup function.
|
||
* @param {Element} trigger
|
||
*/
|
||
|
||
}, {
|
||
key: "defaultAction",
|
||
value: function defaultAction(trigger) {
|
||
return getAttributeValue('action', trigger);
|
||
}
|
||
/**
|
||
* Default `target` lookup function.
|
||
* @param {Element} trigger
|
||
*/
|
||
|
||
}, {
|
||
key: "defaultTarget",
|
||
value: function defaultTarget(trigger) {
|
||
var selector = getAttributeValue('target', trigger);
|
||
|
||
if (selector) {
|
||
return document.querySelector(selector);
|
||
}
|
||
}
|
||
/**
|
||
* Allow fire programmatically a copy action
|
||
* @param {String|HTMLElement} target
|
||
* @param {Object} options
|
||
* @returns Text copied.
|
||
*/
|
||
|
||
}, {
|
||
key: "defaultText",
|
||
|
||
/**
|
||
* Default `text` lookup function.
|
||
* @param {Element} trigger
|
||
*/
|
||
value: function defaultText(trigger) {
|
||
return getAttributeValue('text', trigger);
|
||
}
|
||
/**
|
||
* Destroy lifecycle.
|
||
*/
|
||
|
||
}, {
|
||
key: "destroy",
|
||
value: function destroy() {
|
||
this.listener.destroy();
|
||
}
|
||
}], [{
|
||
key: "copy",
|
||
value: function copy(target) {
|
||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
|
||
container: document.body
|
||
};
|
||
return actions_copy(target, options);
|
||
}
|
||
/**
|
||
* Allow fire programmatically a cut action
|
||
* @param {String|HTMLElement} target
|
||
* @returns Text cutted.
|
||
*/
|
||
|
||
}, {
|
||
key: "cut",
|
||
value: function cut(target) {
|
||
return actions_cut(target);
|
||
}
|
||
/**
|
||
* Returns the support of the given action, or all actions if no action is
|
||
* given.
|
||
* @param {String} [action]
|
||
*/
|
||
|
||
}, {
|
||
key: "isSupported",
|
||
value: function isSupported() {
|
||
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
|
||
var actions = typeof action === 'string' ? [action] : action;
|
||
var support = !!document.queryCommandSupported;
|
||
actions.forEach(function (action) {
|
||
support = support && !!document.queryCommandSupported(action);
|
||
});
|
||
return support;
|
||
}
|
||
}]);
|
||
|
||
return Clipboard;
|
||
}((tiny_emitter_default()));
|
||
|
||
/* harmony default export */ var clipboard = (Clipboard);
|
||
|
||
/***/ }),
|
||
|
||
/***/ 828:
|
||
/***/ (function(module) {
|
||
|
||
var DOCUMENT_NODE_TYPE = 9;
|
||
|
||
/**
|
||
* A polyfill for Element.matches()
|
||
*/
|
||
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
|
||
var proto = Element.prototype;
|
||
|
||
proto.matches = proto.matchesSelector ||
|
||
proto.mozMatchesSelector ||
|
||
proto.msMatchesSelector ||
|
||
proto.oMatchesSelector ||
|
||
proto.webkitMatchesSelector;
|
||
}
|
||
|
||
/**
|
||
* Finds the closest parent that matches a selector.
|
||
*
|
||
* @param {Element} element
|
||
* @param {String} selector
|
||
* @return {Function}
|
||
*/
|
||
function closest (element, selector) {
|
||
while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
|
||
if (typeof element.matches === 'function' &&
|
||
element.matches(selector)) {
|
||
return element;
|
||
}
|
||
element = element.parentNode;
|
||
}
|
||
}
|
||
|
||
module.exports = closest;
|
||
|
||
|
||
/***/ }),
|
||
|
||
/***/ 438:
|
||
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
||
|
||
var closest = __webpack_require__(828);
|
||
|
||
/**
|
||
* Delegates event to a selector.
|
||
*
|
||
* @param {Element} element
|
||
* @param {String} selector
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @param {Boolean} useCapture
|
||
* @return {Object}
|
||
*/
|
||
function _delegate(element, selector, type, callback, useCapture) {
|
||
var listenerFn = listener.apply(this, arguments);
|
||
|
||
element.addEventListener(type, listenerFn, useCapture);
|
||
|
||
return {
|
||
destroy: function() {
|
||
element.removeEventListener(type, listenerFn, useCapture);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delegates event to a selector.
|
||
*
|
||
* @param {Element|String|Array} [elements]
|
||
* @param {String} selector
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @param {Boolean} useCapture
|
||
* @return {Object}
|
||
*/
|
||
function delegate(elements, selector, type, callback, useCapture) {
|
||
// Handle the regular Element usage
|
||
if (typeof elements.addEventListener === 'function') {
|
||
return _delegate.apply(null, arguments);
|
||
}
|
||
|
||
// Handle Element-less usage, it defaults to global delegation
|
||
if (typeof type === 'function') {
|
||
// Use `document` as the first parameter, then apply arguments
|
||
// This is a short way to .unshift `arguments` without running into deoptimizations
|
||
return _delegate.bind(null, document).apply(null, arguments);
|
||
}
|
||
|
||
// Handle Selector-based usage
|
||
if (typeof elements === 'string') {
|
||
elements = document.querySelectorAll(elements);
|
||
}
|
||
|
||
// Handle Array-like based usage
|
||
return Array.prototype.map.call(elements, function (element) {
|
||
return _delegate(element, selector, type, callback, useCapture);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Finds closest match and invokes callback.
|
||
*
|
||
* @param {Element} element
|
||
* @param {String} selector
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @return {Function}
|
||
*/
|
||
function listener(element, selector, type, callback) {
|
||
return function(e) {
|
||
e.delegateTarget = closest(e.target, selector);
|
||
|
||
if (e.delegateTarget) {
|
||
callback.call(element, e);
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = delegate;
|
||
|
||
|
||
/***/ }),
|
||
|
||
/***/ 879:
|
||
/***/ (function(__unused_webpack_module, exports) {
|
||
|
||
/**
|
||
* Check if argument is a HTML element.
|
||
*
|
||
* @param {Object} value
|
||
* @return {Boolean}
|
||
*/
|
||
exports.node = function(value) {
|
||
return value !== undefined
|
||
&& value instanceof HTMLElement
|
||
&& value.nodeType === 1;
|
||
};
|
||
|
||
/**
|
||
* Check if argument is a list of HTML elements.
|
||
*
|
||
* @param {Object} value
|
||
* @return {Boolean}
|
||
*/
|
||
exports.nodeList = function(value) {
|
||
var type = Object.prototype.toString.call(value);
|
||
|
||
return value !== undefined
|
||
&& (type === '[object NodeList]' || type === '[object HTMLCollection]')
|
||
&& ('length' in value)
|
||
&& (value.length === 0 || exports.node(value[0]));
|
||
};
|
||
|
||
/**
|
||
* Check if argument is a string.
|
||
*
|
||
* @param {Object} value
|
||
* @return {Boolean}
|
||
*/
|
||
exports.string = function(value) {
|
||
return typeof value === 'string'
|
||
|| value instanceof String;
|
||
};
|
||
|
||
/**
|
||
* Check if argument is a function.
|
||
*
|
||
* @param {Object} value
|
||
* @return {Boolean}
|
||
*/
|
||
exports.fn = function(value) {
|
||
var type = Object.prototype.toString.call(value);
|
||
|
||
return type === '[object Function]';
|
||
};
|
||
|
||
|
||
/***/ }),
|
||
|
||
/***/ 370:
|
||
/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
|
||
|
||
var is = __webpack_require__(879);
|
||
var delegate = __webpack_require__(438);
|
||
|
||
/**
|
||
* Validates all params and calls the right
|
||
* listener function based on its target type.
|
||
*
|
||
* @param {String|HTMLElement|HTMLCollection|NodeList} target
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @return {Object}
|
||
*/
|
||
function listen(target, type, callback) {
|
||
if (!target && !type && !callback) {
|
||
throw new Error('Missing required arguments');
|
||
}
|
||
|
||
if (!is.string(type)) {
|
||
throw new TypeError('Second argument must be a String');
|
||
}
|
||
|
||
if (!is.fn(callback)) {
|
||
throw new TypeError('Third argument must be a Function');
|
||
}
|
||
|
||
if (is.node(target)) {
|
||
return listenNode(target, type, callback);
|
||
}
|
||
else if (is.nodeList(target)) {
|
||
return listenNodeList(target, type, callback);
|
||
}
|
||
else if (is.string(target)) {
|
||
return listenSelector(target, type, callback);
|
||
}
|
||
else {
|
||
throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Adds an event listener to a HTML element
|
||
* and returns a remove listener function.
|
||
*
|
||
* @param {HTMLElement} node
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @return {Object}
|
||
*/
|
||
function listenNode(node, type, callback) {
|
||
node.addEventListener(type, callback);
|
||
|
||
return {
|
||
destroy: function() {
|
||
node.removeEventListener(type, callback);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Add an event listener to a list of HTML elements
|
||
* and returns a remove listener function.
|
||
*
|
||
* @param {NodeList|HTMLCollection} nodeList
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @return {Object}
|
||
*/
|
||
function listenNodeList(nodeList, type, callback) {
|
||
Array.prototype.forEach.call(nodeList, function(node) {
|
||
node.addEventListener(type, callback);
|
||
});
|
||
|
||
return {
|
||
destroy: function() {
|
||
Array.prototype.forEach.call(nodeList, function(node) {
|
||
node.removeEventListener(type, callback);
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Add an event listener to a selector
|
||
* and returns a remove listener function.
|
||
*
|
||
* @param {String} selector
|
||
* @param {String} type
|
||
* @param {Function} callback
|
||
* @return {Object}
|
||
*/
|
||
function listenSelector(selector, type, callback) {
|
||
return delegate(document.body, selector, type, callback);
|
||
}
|
||
|
||
module.exports = listen;
|
||
|
||
|
||
/***/ }),
|
||
|
||
/***/ 817:
|
||
/***/ (function(module) {
|
||
|
||
function select(element) {
|
||
var selectedText;
|
||
|
||
if (element.nodeName === 'SELECT') {
|
||
element.focus();
|
||
|
||
selectedText = element.value;
|
||
}
|
||
else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
|
||
var isReadOnly = element.hasAttribute('readonly');
|
||
|
||
if (!isReadOnly) {
|
||
element.setAttribute('readonly', '');
|
||
}
|
||
|
||
element.select();
|
||
element.setSelectionRange(0, element.value.length);
|
||
|
||
if (!isReadOnly) {
|
||
element.removeAttribute('readonly');
|
||
}
|
||
|
||
selectedText = element.value;
|
||
}
|
||
else {
|
||
if (element.hasAttribute('contenteditable')) {
|
||
element.focus();
|
||
}
|
||
|
||
var selection = window.getSelection();
|
||
var range = document.createRange();
|
||
|
||
range.selectNodeContents(element);
|
||
selection.removeAllRanges();
|
||
selection.addRange(range);
|
||
|
||
selectedText = selection.toString();
|
||
}
|
||
|
||
return selectedText;
|
||
}
|
||
|
||
module.exports = select;
|
||
|
||
|
||
/***/ }),
|
||
|
||
/***/ 279:
|
||
/***/ (function(module) {
|
||
|
||
function E () {
|
||
// Keep this empty so it's easier to inherit from
|
||
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
|
||
}
|
||
|
||
E.prototype = {
|
||
on: function (name, callback, ctx) {
|
||
var e = this.e || (this.e = {});
|
||
|
||
(e[name] || (e[name] = [])).push({
|
||
fn: callback,
|
||
ctx: ctx
|
||
});
|
||
|
||
return this;
|
||
},
|
||
|
||
once: function (name, callback, ctx) {
|
||
var self = this;
|
||
function listener () {
|
||
self.off(name, listener);
|
||
callback.apply(ctx, arguments);
|
||
};
|
||
|
||
listener._ = callback
|
||
return this.on(name, listener, ctx);
|
||
},
|
||
|
||
emit: function (name) {
|
||
var data = [].slice.call(arguments, 1);
|
||
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
|
||
var i = 0;
|
||
var len = evtArr.length;
|
||
|
||
for (i; i < len; i++) {
|
||
evtArr[i].fn.apply(evtArr[i].ctx, data);
|
||
}
|
||
|
||
return this;
|
||
},
|
||
|
||
off: function (name, callback) {
|
||
var e = this.e || (this.e = {});
|
||
var evts = e[name];
|
||
var liveEvents = [];
|
||
|
||
if (evts && callback) {
|
||
for (var i = 0, len = evts.length; i < len; i++) {
|
||
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
|
||
liveEvents.push(evts[i]);
|
||
}
|
||
}
|
||
|
||
// Remove event from queue to prevent memory leak
|
||
// Suggested by https://github.com/lazd
|
||
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
|
||
|
||
(liveEvents.length)
|
||
? e[name] = liveEvents
|
||
: delete e[name];
|
||
|
||
return this;
|
||
}
|
||
};
|
||
|
||
module.exports = E;
|
||
module.exports.TinyEmitter = E;
|
||
|
||
|
||
/***/ })
|
||
|
||
/******/ });
|
||
/************************************************************************/
|
||
/******/ // The module cache
|
||
/******/ var __webpack_module_cache__ = {};
|
||
/******/
|
||
/******/ // The require function
|
||
/******/ function __webpack_require__(moduleId) {
|
||
/******/ // Check if module is in cache
|
||
/******/ if(__webpack_module_cache__[moduleId]) {
|
||
/******/ return __webpack_module_cache__[moduleId].exports;
|
||
/******/ }
|
||
/******/ // Create a new module (and put it into the cache)
|
||
/******/ var module = __webpack_module_cache__[moduleId] = {
|
||
/******/ // no module.id needed
|
||
/******/ // no module.loaded needed
|
||
/******/ exports: {}
|
||
/******/ };
|
||
/******/
|
||
/******/ // Execute the module function
|
||
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
||
/******/
|
||
/******/ // Return the exports of the module
|
||
/******/ return module.exports;
|
||
/******/ }
|
||
/******/
|
||
/************************************************************************/
|
||
/******/ /* webpack/runtime/compat get default export */
|
||
/******/ !function() {
|
||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||
/******/ __webpack_require__.n = function(module) {
|
||
/******/ var getter = module && module.__esModule ?
|
||
/******/ function() { return module['default']; } :
|
||
/******/ function() { return module; };
|
||
/******/ __webpack_require__.d(getter, { a: getter });
|
||
/******/ return getter;
|
||
/******/ };
|
||
/******/ }();
|
||
/******/
|
||
/******/ /* webpack/runtime/define property getters */
|
||
/******/ !function() {
|
||
/******/ // define getter functions for harmony exports
|
||
/******/ __webpack_require__.d = function(exports, definition) {
|
||
/******/ for(var key in definition) {
|
||
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
||
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
||
/******/ }
|
||
/******/ }
|
||
/******/ };
|
||
/******/ }();
|
||
/******/
|
||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||
/******/ !function() {
|
||
/******/ __webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }
|
||
/******/ }();
|
||
/******/
|
||
/************************************************************************/
|
||
/******/ // module exports must be returned from runtime so entry inlining is disabled
|
||
/******/ // startup
|
||
/******/ // Load entry module and return exports
|
||
/******/ return __webpack_require__(686);
|
||
/******/ })()
|
||
.default;
|
||
}); |