· 8 years ago · Apr 14, 2017, 08:40 PM
1<?xml version="1.0" encoding="utf-8"?>
2<html xmlns="http://www.w3.org/1999/xhtml">
3 <head>
4 <meta http-equiv="content-type" content="application/xhtml+xml; charset=utf-8" />
5 <title>Repository statistics for 'Aligot'</title>
6 <script type="application/javascript">/*!
7 * jQuery JavaScript Library v1.9.1
8 * http://jquery.com/
9 *
10 * Includes Sizzle.js
11 * http://sizzlejs.com/
12 *
13 * Copyright 2005, 2012 jQuery Foundation, Inc. and other contributors
14 * Released under the MIT license
15 * http://jquery.org/license
16 *
17 * Date: 2013-2-4
18 */
19(function( window, undefined ) {
20
21// Can't do this because several apps including ASP.NET trace
22// the stack via arguments.caller.callee and Firefox dies if
23// you try to trace through "use strict" call chains. (#13335)
24// Support: Firefox 18+
25//"use strict";
26var
27 // The deferred used on DOM ready
28 readyList,
29
30 // A central reference to the root jQuery(document)
31 rootjQuery,
32
33 // Support: IE<9
34 // For `typeof node.method` instead of `node.method !== undefined`
35 core_strundefined = typeof undefined,
36
37 // Use the correct document accordingly with window argument (sandbox)
38 document = window.document,
39 location = window.location,
40
41 // Map over jQuery in case of overwrite
42 _jQuery = window.jQuery,
43
44 // Map over the $ in case of overwrite
45 _$ = window.$,
46
47 // [[Class]] -> type pairs
48 class2type = {},
49
50 // List of deleted data cache ids, so we can reuse them
51 core_deletedIds = [],
52
53 core_version = "1.9.1",
54
55 // Save a reference to some core methods
56 core_concat = core_deletedIds.concat,
57 core_push = core_deletedIds.push,
58 core_slice = core_deletedIds.slice,
59 core_indexOf = core_deletedIds.indexOf,
60 core_toString = class2type.toString,
61 core_hasOwn = class2type.hasOwnProperty,
62 core_trim = core_version.trim,
63
64 // Define a local copy of jQuery
65 jQuery = function( selector, context ) {
66 // The jQuery object is actually just the init constructor 'enhanced'
67 return new jQuery.fn.init( selector, context, rootjQuery );
68 },
69
70 // Used for matching numbers
71 core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,
72
73 // Used for splitting on whitespace
74 core_rnotwhite = /\S+/g,
75
76 // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
77 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
78
79 // A simple way to check for HTML strings
80 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
81 // Strict HTML recognition (#11290: must start with <)
82 rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,
83
84 // Match a standalone tag
85 rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
86
87 // JSON RegExp
88 rvalidchars = /^[\],:{}\s]*$/,
89 rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
90 rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
91 rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,
92
93 // Matches dashed string for camelizing
94 rmsPrefix = /^-ms-/,
95 rdashAlpha = /-([\da-z])/gi,
96
97 // Used by jQuery.camelCase as callback to replace()
98 fcamelCase = function( all, letter ) {
99 return letter.toUpperCase();
100 },
101
102 // The ready event handler
103 completed = function( event ) {
104
105 // readyState === "complete" is good enough for us to call the dom ready in oldIE
106 if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
107 detach();
108 jQuery.ready();
109 }
110 },
111 // Clean-up method for dom ready events
112 detach = function() {
113 if ( document.addEventListener ) {
114 document.removeEventListener( "DOMContentLoaded", completed, false );
115 window.removeEventListener( "load", completed, false );
116
117 } else {
118 document.detachEvent( "onreadystatechange", completed );
119 window.detachEvent( "onload", completed );
120 }
121 };
122
123jQuery.fn = jQuery.prototype = {
124 // The current version of jQuery being used
125 jquery: core_version,
126
127 constructor: jQuery,
128 init: function( selector, context, rootjQuery ) {
129 var match, elem;
130
131 // HANDLE: $(""), $(null), $(undefined), $(false)
132 if ( !selector ) {
133 return this;
134 }
135
136 // Handle HTML strings
137 if ( typeof selector === "string" ) {
138 if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
139 // Assume that strings that start and end with <> are HTML and skip the regex check
140 match = [ null, selector, null ];
141
142 } else {
143 match = rquickExpr.exec( selector );
144 }
145
146 // Match html or make sure no context is specified for #id
147 if ( match && (match[1] || !context) ) {
148
149 // HANDLE: $(html) -> $(array)
150 if ( match[1] ) {
151 context = context instanceof jQuery ? context[0] : context;
152
153 // scripts is true for back-compat
154 jQuery.merge( this, jQuery.parseHTML(
155 match[1],
156 context && context.nodeType ? context.ownerDocument || context : document,
157 true
158 ) );
159
160 // HANDLE: $(html, props)
161 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
162 for ( match in context ) {
163 // Properties of context are called as methods if possible
164 if ( jQuery.isFunction( this[ match ] ) ) {
165 this[ match ]( context[ match ] );
166
167 // ...and otherwise set as attributes
168 } else {
169 this.attr( match, context[ match ] );
170 }
171 }
172 }
173
174 return this;
175
176 // HANDLE: $(#id)
177 } else {
178 elem = document.getElementById( match[2] );
179
180 // Check parentNode to catch when Blackberry 4.6 returns
181 // nodes that are no longer in the document #6963
182 if ( elem && elem.parentNode ) {
183 // Handle the case where IE and Opera return items
184 // by name instead of ID
185 if ( elem.id !== match[2] ) {
186 return rootjQuery.find( selector );
187 }
188
189 // Otherwise, we inject the element directly into the jQuery object
190 this.length = 1;
191 this[0] = elem;
192 }
193
194 this.context = document;
195 this.selector = selector;
196 return this;
197 }
198
199 // HANDLE: $(expr, $(...))
200 } else if ( !context || context.jquery ) {
201 return ( context || rootjQuery ).find( selector );
202
203 // HANDLE: $(expr, context)
204 // (which is just equivalent to: $(context).find(expr)
205 } else {
206 return this.constructor( context ).find( selector );
207 }
208
209 // HANDLE: $(DOMElement)
210 } else if ( selector.nodeType ) {
211 this.context = this[0] = selector;
212 this.length = 1;
213 return this;
214
215 // HANDLE: $(function)
216 // Shortcut for document ready
217 } else if ( jQuery.isFunction( selector ) ) {
218 return rootjQuery.ready( selector );
219 }
220
221 if ( selector.selector !== undefined ) {
222 this.selector = selector.selector;
223 this.context = selector.context;
224 }
225
226 return jQuery.makeArray( selector, this );
227 },
228
229 // Start with an empty selector
230 selector: "",
231
232 // The default length of a jQuery object is 0
233 length: 0,
234
235 // The number of elements contained in the matched element set
236 size: function() {
237 return this.length;
238 },
239
240 toArray: function() {
241 return core_slice.call( this );
242 },
243
244 // Get the Nth element in the matched element set OR
245 // Get the whole matched element set as a clean array
246 get: function( num ) {
247 return num == null ?
248
249 // Return a 'clean' array
250 this.toArray() :
251
252 // Return just the object
253 ( num < 0 ? this[ this.length + num ] : this[ num ] );
254 },
255
256 // Take an array of elements and push it onto the stack
257 // (returning the new matched element set)
258 pushStack: function( elems ) {
259
260 // Build a new jQuery matched element set
261 var ret = jQuery.merge( this.constructor(), elems );
262
263 // Add the old object onto the stack (as a reference)
264 ret.prevObject = this;
265 ret.context = this.context;
266
267 // Return the newly-formed element set
268 return ret;
269 },
270
271 // Execute a callback for every element in the matched set.
272 // (You can seed the arguments with an array of args, but this is
273 // only used internally.)
274 each: function( callback, args ) {
275 return jQuery.each( this, callback, args );
276 },
277
278 ready: function( fn ) {
279 // Add the callback
280 jQuery.ready.promise().done( fn );
281
282 return this;
283 },
284
285 slice: function() {
286 return this.pushStack( core_slice.apply( this, arguments ) );
287 },
288
289 first: function() {
290 return this.eq( 0 );
291 },
292
293 last: function() {
294 return this.eq( -1 );
295 },
296
297 eq: function( i ) {
298 var len = this.length,
299 j = +i + ( i < 0 ? len : 0 );
300 return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
301 },
302
303 map: function( callback ) {
304 return this.pushStack( jQuery.map(this, function( elem, i ) {
305 return callback.call( elem, i, elem );
306 }));
307 },
308
309 end: function() {
310 return this.prevObject || this.constructor(null);
311 },
312
313 // For internal use only.
314 // Behaves like an Array's method, not like a jQuery method.
315 push: core_push,
316 sort: [].sort,
317 splice: [].splice
318};
319
320// Give the init function the jQuery prototype for later instantiation
321jQuery.fn.init.prototype = jQuery.fn;
322
323jQuery.extend = jQuery.fn.extend = function() {
324 var src, copyIsArray, copy, name, options, clone,
325 target = arguments[0] || {},
326 i = 1,
327 length = arguments.length,
328 deep = false;
329
330 // Handle a deep copy situation
331 if ( typeof target === "boolean" ) {
332 deep = target;
333 target = arguments[1] || {};
334 // skip the boolean and the target
335 i = 2;
336 }
337
338 // Handle case when target is a string or something (possible in deep copy)
339 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
340 target = {};
341 }
342
343 // extend jQuery itself if only one argument is passed
344 if ( length === i ) {
345 target = this;
346 --i;
347 }
348
349 for ( ; i < length; i++ ) {
350 // Only deal with non-null/undefined values
351 if ( (options = arguments[ i ]) != null ) {
352 // Extend the base object
353 for ( name in options ) {
354 src = target[ name ];
355 copy = options[ name ];
356
357 // Prevent never-ending loop
358 if ( target === copy ) {
359 continue;
360 }
361
362 // Recurse if we're merging plain objects or arrays
363 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
364 if ( copyIsArray ) {
365 copyIsArray = false;
366 clone = src && jQuery.isArray(src) ? src : [];
367
368 } else {
369 clone = src && jQuery.isPlainObject(src) ? src : {};
370 }
371
372 // Never move original objects, clone them
373 target[ name ] = jQuery.extend( deep, clone, copy );
374
375 // Don't bring in undefined values
376 } else if ( copy !== undefined ) {
377 target[ name ] = copy;
378 }
379 }
380 }
381 }
382
383 // Return the modified object
384 return target;
385};
386
387jQuery.extend({
388 noConflict: function( deep ) {
389 if ( window.$ === jQuery ) {
390 window.$ = _$;
391 }
392
393 if ( deep && window.jQuery === jQuery ) {
394 window.jQuery = _jQuery;
395 }
396
397 return jQuery;
398 },
399
400 // Is the DOM ready to be used? Set to true once it occurs.
401 isReady: false,
402
403 // A counter to track how many items to wait for before
404 // the ready event fires. See #6781
405 readyWait: 1,
406
407 // Hold (or release) the ready event
408 holdReady: function( hold ) {
409 if ( hold ) {
410 jQuery.readyWait++;
411 } else {
412 jQuery.ready( true );
413 }
414 },
415
416 // Handle when the DOM is ready
417 ready: function( wait ) {
418
419 // Abort if there are pending holds or we're already ready
420 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
421 return;
422 }
423
424 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
425 if ( !document.body ) {
426 return setTimeout( jQuery.ready );
427 }
428
429 // Remember that the DOM is ready
430 jQuery.isReady = true;
431
432 // If a normal DOM Ready event fired, decrement, and wait if need be
433 if ( wait !== true && --jQuery.readyWait > 0 ) {
434 return;
435 }
436
437 // If there are functions bound, to execute
438 readyList.resolveWith( document, [ jQuery ] );
439
440 // Trigger any bound ready events
441 if ( jQuery.fn.trigger ) {
442 jQuery( document ).trigger("ready").off("ready");
443 }
444 },
445
446 // See test/unit/core.js for details concerning isFunction.
447 // Since version 1.3, DOM methods and functions like alert
448 // aren't supported. They return false on IE (#2968).
449 isFunction: function( obj ) {
450 return jQuery.type(obj) === "function";
451 },
452
453 isArray: Array.isArray || function( obj ) {
454 return jQuery.type(obj) === "array";
455 },
456
457 isWindow: function( obj ) {
458 return obj != null && obj == obj.window;
459 },
460
461 isNumeric: function( obj ) {
462 return !isNaN( parseFloat(obj) ) && isFinite( obj );
463 },
464
465 type: function( obj ) {
466 if ( obj == null ) {
467 return String( obj );
468 }
469 return typeof obj === "object" || typeof obj === "function" ?
470 class2type[ core_toString.call(obj) ] || "object" :
471 typeof obj;
472 },
473
474 isPlainObject: function( obj ) {
475 // Must be an Object.
476 // Because of IE, we also have to check the presence of the constructor property.
477 // Make sure that DOM nodes and window objects don't pass through, as well
478 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
479 return false;
480 }
481
482 try {
483 // Not own constructor property must be Object
484 if ( obj.constructor &&
485 !core_hasOwn.call(obj, "constructor") &&
486 !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
487 return false;
488 }
489 } catch ( e ) {
490 // IE8,9 Will throw exceptions on certain host objects #9897
491 return false;
492 }
493
494 // Own properties are enumerated firstly, so to speed up,
495 // if last one is own, then all properties are own.
496
497 var key;
498 for ( key in obj ) {}
499
500 return key === undefined || core_hasOwn.call( obj, key );
501 },
502
503 isEmptyObject: function( obj ) {
504 var name;
505 for ( name in obj ) {
506 return false;
507 }
508 return true;
509 },
510
511 error: function( msg ) {
512 throw new Error( msg );
513 },
514
515 // data: string of html
516 // context (optional): If specified, the fragment will be created in this context, defaults to document
517 // keepScripts (optional): If true, will include scripts passed in the html string
518 parseHTML: function( data, context, keepScripts ) {
519 if ( !data || typeof data !== "string" ) {
520 return null;
521 }
522 if ( typeof context === "boolean" ) {
523 keepScripts = context;
524 context = false;
525 }
526 context = context || document;
527
528 var parsed = rsingleTag.exec( data ),
529 scripts = !keepScripts && [];
530
531 // Single tag
532 if ( parsed ) {
533 return [ context.createElement( parsed[1] ) ];
534 }
535
536 parsed = jQuery.buildFragment( [ data ], context, scripts );
537 if ( scripts ) {
538 jQuery( scripts ).remove();
539 }
540 return jQuery.merge( [], parsed.childNodes );
541 },
542
543 parseJSON: function( data ) {
544 // Attempt to parse using the native JSON parser first
545 if ( window.JSON && window.JSON.parse ) {
546 return window.JSON.parse( data );
547 }
548
549 if ( data === null ) {
550 return data;
551 }
552
553 if ( typeof data === "string" ) {
554
555 // Make sure leading/trailing whitespace is removed (IE can't handle it)
556 data = jQuery.trim( data );
557
558 if ( data ) {
559 // Make sure the incoming data is actual JSON
560 // Logic borrowed from http://json.org/json2.js
561 if ( rvalidchars.test( data.replace( rvalidescape, "@" )
562 .replace( rvalidtokens, "]" )
563 .replace( rvalidbraces, "")) ) {
564
565 return ( new Function( "return " + data ) )();
566 }
567 }
568 }
569
570 jQuery.error( "Invalid JSON: " + data );
571 },
572
573 // Cross-browser xml parsing
574 parseXML: function( data ) {
575 var xml, tmp;
576 if ( !data || typeof data !== "string" ) {
577 return null;
578 }
579 try {
580 if ( window.DOMParser ) { // Standard
581 tmp = new DOMParser();
582 xml = tmp.parseFromString( data , "text/xml" );
583 } else { // IE
584 xml = new ActiveXObject( "Microsoft.XMLDOM" );
585 xml.async = "false";
586 xml.loadXML( data );
587 }
588 } catch( e ) {
589 xml = undefined;
590 }
591 if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
592 jQuery.error( "Invalid XML: " + data );
593 }
594 return xml;
595 },
596
597 noop: function() {},
598
599 // Evaluates a script in a global context
600 // Workarounds based on findings by Jim Driscoll
601 // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
602 globalEval: function( data ) {
603 if ( data && jQuery.trim( data ) ) {
604 // We use execScript on Internet Explorer
605 // We use an anonymous function so that context is window
606 // rather than jQuery in Firefox
607 ( window.execScript || function( data ) {
608 window[ "eval" ].call( window, data );
609 } )( data );
610 }
611 },
612
613 // Convert dashed to camelCase; used by the css and data modules
614 // Microsoft forgot to hump their vendor prefix (#9572)
615 camelCase: function( string ) {
616 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
617 },
618
619 nodeName: function( elem, name ) {
620 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
621 },
622
623 // args is for internal usage only
624 each: function( obj, callback, args ) {
625 var value,
626 i = 0,
627 length = obj.length,
628 isArray = isArraylike( obj );
629
630 if ( args ) {
631 if ( isArray ) {
632 for ( ; i < length; i++ ) {
633 value = callback.apply( obj[ i ], args );
634
635 if ( value === false ) {
636 break;
637 }
638 }
639 } else {
640 for ( i in obj ) {
641 value = callback.apply( obj[ i ], args );
642
643 if ( value === false ) {
644 break;
645 }
646 }
647 }
648
649 // A special, fast, case for the most common use of each
650 } else {
651 if ( isArray ) {
652 for ( ; i < length; i++ ) {
653 value = callback.call( obj[ i ], i, obj[ i ] );
654
655 if ( value === false ) {
656 break;
657 }
658 }
659 } else {
660 for ( i in obj ) {
661 value = callback.call( obj[ i ], i, obj[ i ] );
662
663 if ( value === false ) {
664 break;
665 }
666 }
667 }
668 }
669
670 return obj;
671 },
672
673 // Use native String.trim function wherever possible
674 trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
675 function( text ) {
676 return text == null ?
677 "" :
678 core_trim.call( text );
679 } :
680
681 // Otherwise use our own trimming functionality
682 function( text ) {
683 return text == null ?
684 "" :
685 ( text + "" ).replace( rtrim, "" );
686 },
687
688 // results is for internal usage only
689 makeArray: function( arr, results ) {
690 var ret = results || [];
691
692 if ( arr != null ) {
693 if ( isArraylike( Object(arr) ) ) {
694 jQuery.merge( ret,
695 typeof arr === "string" ?
696 [ arr ] : arr
697 );
698 } else {
699 core_push.call( ret, arr );
700 }
701 }
702
703 return ret;
704 },
705
706 inArray: function( elem, arr, i ) {
707 var len;
708
709 if ( arr ) {
710 if ( core_indexOf ) {
711 return core_indexOf.call( arr, elem, i );
712 }
713
714 len = arr.length;
715 i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
716
717 for ( ; i < len; i++ ) {
718 // Skip accessing in sparse arrays
719 if ( i in arr && arr[ i ] === elem ) {
720 return i;
721 }
722 }
723 }
724
725 return -1;
726 },
727
728 merge: function( first, second ) {
729 var l = second.length,
730 i = first.length,
731 j = 0;
732
733 if ( typeof l === "number" ) {
734 for ( ; j < l; j++ ) {
735 first[ i++ ] = second[ j ];
736 }
737 } else {
738 while ( second[j] !== undefined ) {
739 first[ i++ ] = second[ j++ ];
740 }
741 }
742
743 first.length = i;
744
745 return first;
746 },
747
748 grep: function( elems, callback, inv ) {
749 var retVal,
750 ret = [],
751 i = 0,
752 length = elems.length;
753 inv = !!inv;
754
755 // Go through the array, only saving the items
756 // that pass the validator function
757 for ( ; i < length; i++ ) {
758 retVal = !!callback( elems[ i ], i );
759 if ( inv !== retVal ) {
760 ret.push( elems[ i ] );
761 }
762 }
763
764 return ret;
765 },
766
767 // arg is for internal usage only
768 map: function( elems, callback, arg ) {
769 var value,
770 i = 0,
771 length = elems.length,
772 isArray = isArraylike( elems ),
773 ret = [];
774
775 // Go through the array, translating each of the items to their
776 if ( isArray ) {
777 for ( ; i < length; i++ ) {
778 value = callback( elems[ i ], i, arg );
779
780 if ( value != null ) {
781 ret[ ret.length ] = value;
782 }
783 }
784
785 // Go through every key on the object,
786 } else {
787 for ( i in elems ) {
788 value = callback( elems[ i ], i, arg );
789
790 if ( value != null ) {
791 ret[ ret.length ] = value;
792 }
793 }
794 }
795
796 // Flatten any nested arrays
797 return core_concat.apply( [], ret );
798 },
799
800 // A global GUID counter for objects
801 guid: 1,
802
803 // Bind a function to a context, optionally partially applying any
804 // arguments.
805 proxy: function( fn, context ) {
806 var args, proxy, tmp;
807
808 if ( typeof context === "string" ) {
809 tmp = fn[ context ];
810 context = fn;
811 fn = tmp;
812 }
813
814 // Quick check to determine if target is callable, in the spec
815 // this throws a TypeError, but we will just return undefined.
816 if ( !jQuery.isFunction( fn ) ) {
817 return undefined;
818 }
819
820 // Simulated bind
821 args = core_slice.call( arguments, 2 );
822 proxy = function() {
823 return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );
824 };
825
826 // Set the guid of unique handler to the same of original handler, so it can be removed
827 proxy.guid = fn.guid = fn.guid || jQuery.guid++;
828
829 return proxy;
830 },
831
832 // Multifunctional method to get and set values of a collection
833 // The value/s can optionally be executed if it's a function
834 access: function( elems, fn, key, value, chainable, emptyGet, raw ) {
835 var i = 0,
836 length = elems.length,
837 bulk = key == null;
838
839 // Sets many values
840 if ( jQuery.type( key ) === "object" ) {
841 chainable = true;
842 for ( i in key ) {
843 jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
844 }
845
846 // Sets one value
847 } else if ( value !== undefined ) {
848 chainable = true;
849
850 if ( !jQuery.isFunction( value ) ) {
851 raw = true;
852 }
853
854 if ( bulk ) {
855 // Bulk operations run against the entire set
856 if ( raw ) {
857 fn.call( elems, value );
858 fn = null;
859
860 // ...except when executing function values
861 } else {
862 bulk = fn;
863 fn = function( elem, key, value ) {
864 return bulk.call( jQuery( elem ), value );
865 };
866 }
867 }
868
869 if ( fn ) {
870 for ( ; i < length; i++ ) {
871 fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
872 }
873 }
874 }
875
876 return chainable ?
877 elems :
878
879 // Gets
880 bulk ?
881 fn.call( elems ) :
882 length ? fn( elems[0], key ) : emptyGet;
883 },
884
885 now: function() {
886 return ( new Date() ).getTime();
887 }
888});
889
890jQuery.ready.promise = function( obj ) {
891 if ( !readyList ) {
892
893 readyList = jQuery.Deferred();
894
895 // Catch cases where $(document).ready() is called after the browser event has already occurred.
896 // we once tried to use readyState "interactive" here, but it caused issues like the one
897 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
898 if ( document.readyState === "complete" ) {
899 // Handle it asynchronously to allow scripts the opportunity to delay ready
900 setTimeout( jQuery.ready );
901
902 // Standards-based browsers support DOMContentLoaded
903 } else if ( document.addEventListener ) {
904 // Use the handy event callback
905 document.addEventListener( "DOMContentLoaded", completed, false );
906
907 // A fallback to window.onload, that will always work
908 window.addEventListener( "load", completed, false );
909
910 // If IE event model is used
911 } else {
912 // Ensure firing before onload, maybe late but safe also for iframes
913 document.attachEvent( "onreadystatechange", completed );
914
915 // A fallback to window.onload, that will always work
916 window.attachEvent( "onload", completed );
917
918 // If IE and not a frame
919 // continually check to see if the document is ready
920 var top = false;
921
922 try {
923 top = window.frameElement == null && document.documentElement;
924 } catch(e) {}
925
926 if ( top && top.doScroll ) {
927 (function doScrollCheck() {
928 if ( !jQuery.isReady ) {
929
930 try {
931 // Use the trick by Diego Perini
932 // http://javascript.nwbox.com/IEContentLoaded/
933 top.doScroll("left");
934 } catch(e) {
935 return setTimeout( doScrollCheck, 50 );
936 }
937
938 // detach all dom ready events
939 detach();
940
941 // and execute any waiting functions
942 jQuery.ready();
943 }
944 })();
945 }
946 }
947 }
948 return readyList.promise( obj );
949};
950
951// Populate the class2type map
952jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
953 class2type[ "[object " + name + "]" ] = name.toLowerCase();
954});
955
956function isArraylike( obj ) {
957 var length = obj.length,
958 type = jQuery.type( obj );
959
960 if ( jQuery.isWindow( obj ) ) {
961 return false;
962 }
963
964 if ( obj.nodeType === 1 && length ) {
965 return true;
966 }
967
968 return type === "array" || type !== "function" &&
969 ( length === 0 ||
970 typeof length === "number" && length > 0 && ( length - 1 ) in obj );
971}
972
973// All jQuery objects should point back to these
974rootjQuery = jQuery(document);
975// String to Object options format cache
976var optionsCache = {};
977
978// Convert String-formatted options into Object-formatted ones and store in cache
979function createOptions( options ) {
980 var object = optionsCache[ options ] = {};
981 jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) {
982 object[ flag ] = true;
983 });
984 return object;
985}
986
987/*
988 * Create a callback list using the following parameters:
989 *
990 * options: an optional list of space-separated options that will change how
991 * the callback list behaves or a more traditional option object
992 *
993 * By default a callback list will act like an event callback list and can be
994 * "fired" multiple times.
995 *
996 * Possible options:
997 *
998 * once: will ensure the callback list can only be fired once (like a Deferred)
999 *
1000 * memory: will keep track of previous values and will call any callback added
1001 * after the list has been fired right away with the latest "memorized"
1002 * values (like a Deferred)
1003 *
1004 * unique: will ensure a callback can only be added once (no duplicate in the list)
1005 *
1006 * stopOnFalse: interrupt callings when a callback returns false
1007 *
1008 */
1009jQuery.Callbacks = function( options ) {
1010
1011 // Convert options from String-formatted to Object-formatted if needed
1012 // (we check in cache first)
1013 options = typeof options === "string" ?
1014 ( optionsCache[ options ] || createOptions( options ) ) :
1015 jQuery.extend( {}, options );
1016
1017 var // Flag to know if list is currently firing
1018 firing,
1019 // Last fire value (for non-forgettable lists)
1020 memory,
1021 // Flag to know if list was already fired
1022 fired,
1023 // End of the loop when firing
1024 firingLength,
1025 // Index of currently firing callback (modified by remove if needed)
1026 firingIndex,
1027 // First callback to fire (used internally by add and fireWith)
1028 firingStart,
1029 // Actual callback list
1030 list = [],
1031 // Stack of fire calls for repeatable lists
1032 stack = !options.once && [],
1033 // Fire callbacks
1034 fire = function( data ) {
1035 memory = options.memory && data;
1036 fired = true;
1037 firingIndex = firingStart || 0;
1038 firingStart = 0;
1039 firingLength = list.length;
1040 firing = true;
1041 for ( ; list && firingIndex < firingLength; firingIndex++ ) {
1042 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
1043 memory = false; // To prevent further calls using add
1044 break;
1045 }
1046 }
1047 firing = false;
1048 if ( list ) {
1049 if ( stack ) {
1050 if ( stack.length ) {
1051 fire( stack.shift() );
1052 }
1053 } else if ( memory ) {
1054 list = [];
1055 } else {
1056 self.disable();
1057 }
1058 }
1059 },
1060 // Actual Callbacks object
1061 self = {
1062 // Add a callback or a collection of callbacks to the list
1063 add: function() {
1064 if ( list ) {
1065 // First, we save the current length
1066 var start = list.length;
1067 (function add( args ) {
1068 jQuery.each( args, function( _, arg ) {
1069 var type = jQuery.type( arg );
1070 if ( type === "function" ) {
1071 if ( !options.unique || !self.has( arg ) ) {
1072 list.push( arg );
1073 }
1074 } else if ( arg && arg.length && type !== "string" ) {
1075 // Inspect recursively
1076 add( arg );
1077 }
1078 });
1079 })( arguments );
1080 // Do we need to add the callbacks to the
1081 // current firing batch?
1082 if ( firing ) {
1083 firingLength = list.length;
1084 // With memory, if we're not firing then
1085 // we should call right away
1086 } else if ( memory ) {
1087 firingStart = start;
1088 fire( memory );
1089 }
1090 }
1091 return this;
1092 },
1093 // Remove a callback from the list
1094 remove: function() {
1095 if ( list ) {
1096 jQuery.each( arguments, function( _, arg ) {
1097 var index;
1098 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
1099 list.splice( index, 1 );
1100 // Handle firing indexes
1101 if ( firing ) {
1102 if ( index <= firingLength ) {
1103 firingLength--;
1104 }
1105 if ( index <= firingIndex ) {
1106 firingIndex--;
1107 }
1108 }
1109 }
1110 });
1111 }
1112 return this;
1113 },
1114 // Check if a given callback is in the list.
1115 // If no argument is given, return whether or not list has callbacks attached.
1116 has: function( fn ) {
1117 return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
1118 },
1119 // Remove all callbacks from the list
1120 empty: function() {
1121 list = [];
1122 return this;
1123 },
1124 // Have the list do nothing anymore
1125 disable: function() {
1126 list = stack = memory = undefined;
1127 return this;
1128 },
1129 // Is it disabled?
1130 disabled: function() {
1131 return !list;
1132 },
1133 // Lock the list in its current state
1134 lock: function() {
1135 stack = undefined;
1136 if ( !memory ) {
1137 self.disable();
1138 }
1139 return this;
1140 },
1141 // Is it locked?
1142 locked: function() {
1143 return !stack;
1144 },
1145 // Call all callbacks with the given context and arguments
1146 fireWith: function( context, args ) {
1147 args = args || [];
1148 args = [ context, args.slice ? args.slice() : args ];
1149 if ( list && ( !fired || stack ) ) {
1150 if ( firing ) {
1151 stack.push( args );
1152 } else {
1153 fire( args );
1154 }
1155 }
1156 return this;
1157 },
1158 // Call all the callbacks with the given arguments
1159 fire: function() {
1160 self.fireWith( this, arguments );
1161 return this;
1162 },
1163 // To know if the callbacks have already been called at least once
1164 fired: function() {
1165 return !!fired;
1166 }
1167 };
1168
1169 return self;
1170};
1171jQuery.extend({
1172
1173 Deferred: function( func ) {
1174 var tuples = [
1175 // action, add listener, listener list, final state
1176 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
1177 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
1178 [ "notify", "progress", jQuery.Callbacks("memory") ]
1179 ],
1180 state = "pending",
1181 promise = {
1182 state: function() {
1183 return state;
1184 },
1185 always: function() {
1186 deferred.done( arguments ).fail( arguments );
1187 return this;
1188 },
1189 then: function( /* fnDone, fnFail, fnProgress */ ) {
1190 var fns = arguments;
1191 return jQuery.Deferred(function( newDefer ) {
1192 jQuery.each( tuples, function( i, tuple ) {
1193 var action = tuple[ 0 ],
1194 fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
1195 // deferred[ done | fail | progress ] for forwarding actions to newDefer
1196 deferred[ tuple[1] ](function() {
1197 var returned = fn && fn.apply( this, arguments );
1198 if ( returned && jQuery.isFunction( returned.promise ) ) {
1199 returned.promise()
1200 .done( newDefer.resolve )
1201 .fail( newDefer.reject )
1202 .progress( newDefer.notify );
1203 } else {
1204 newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
1205 }
1206 });
1207 });
1208 fns = null;
1209 }).promise();
1210 },
1211 // Get a promise for this deferred
1212 // If obj is provided, the promise aspect is added to the object
1213 promise: function( obj ) {
1214 return obj != null ? jQuery.extend( obj, promise ) : promise;
1215 }
1216 },
1217 deferred = {};
1218
1219 // Keep pipe for back-compat
1220 promise.pipe = promise.then;
1221
1222 // Add list-specific methods
1223 jQuery.each( tuples, function( i, tuple ) {
1224 var list = tuple[ 2 ],
1225 stateString = tuple[ 3 ];
1226
1227 // promise[ done | fail | progress ] = list.add
1228 promise[ tuple[1] ] = list.add;
1229
1230 // Handle state
1231 if ( stateString ) {
1232 list.add(function() {
1233 // state = [ resolved | rejected ]
1234 state = stateString;
1235
1236 // [ reject_list | resolve_list ].disable; progress_list.lock
1237 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
1238 }
1239
1240 // deferred[ resolve | reject | notify ]
1241 deferred[ tuple[0] ] = function() {
1242 deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
1243 return this;
1244 };
1245 deferred[ tuple[0] + "With" ] = list.fireWith;
1246 });
1247
1248 // Make the deferred a promise
1249 promise.promise( deferred );
1250
1251 // Call given func if any
1252 if ( func ) {
1253 func.call( deferred, deferred );
1254 }
1255
1256 // All done!
1257 return deferred;
1258 },
1259
1260 // Deferred helper
1261 when: function( subordinate /* , ..., subordinateN */ ) {
1262 var i = 0,
1263 resolveValues = core_slice.call( arguments ),
1264 length = resolveValues.length,
1265
1266 // the count of uncompleted subordinates
1267 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
1268
1269 // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
1270 deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
1271
1272 // Update function for both resolve and progress values
1273 updateFunc = function( i, contexts, values ) {
1274 return function( value ) {
1275 contexts[ i ] = this;
1276 values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
1277 if( values === progressValues ) {
1278 deferred.notifyWith( contexts, values );
1279 } else if ( !( --remaining ) ) {
1280 deferred.resolveWith( contexts, values );
1281 }
1282 };
1283 },
1284
1285 progressValues, progressContexts, resolveContexts;
1286
1287 // add listeners to Deferred subordinates; treat others as resolved
1288 if ( length > 1 ) {
1289 progressValues = new Array( length );
1290 progressContexts = new Array( length );
1291 resolveContexts = new Array( length );
1292 for ( ; i < length; i++ ) {
1293 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
1294 resolveValues[ i ].promise()
1295 .done( updateFunc( i, resolveContexts, resolveValues ) )
1296 .fail( deferred.reject )
1297 .progress( updateFunc( i, progressContexts, progressValues ) );
1298 } else {
1299 --remaining;
1300 }
1301 }
1302 }
1303
1304 // if we're not waiting on anything, resolve the master
1305 if ( !remaining ) {
1306 deferred.resolveWith( resolveContexts, resolveValues );
1307 }
1308
1309 return deferred.promise();
1310 }
1311});
1312jQuery.support = (function() {
1313
1314 var support, all, a,
1315 input, select, fragment,
1316 opt, eventName, isSupported, i,
1317 div = document.createElement("div");
1318
1319 // Setup
1320 div.setAttribute( "className", "t" );
1321 div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
1322
1323 // Support tests won't run in some limited or non-browser environments
1324 all = div.getElementsByTagName("*");
1325 a = div.getElementsByTagName("a")[ 0 ];
1326 if ( !all || !a || !all.length ) {
1327 return {};
1328 }
1329
1330 // First batch of tests
1331 select = document.createElement("select");
1332 opt = select.appendChild( document.createElement("option") );
1333 input = div.getElementsByTagName("input")[ 0 ];
1334
1335 a.style.cssText = "top:1px;float:left;opacity:.5";
1336 support = {
1337 // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
1338 getSetAttribute: div.className !== "t",
1339
1340 // IE strips leading whitespace when .innerHTML is used
1341 leadingWhitespace: div.firstChild.nodeType === 3,
1342
1343 // Make sure that tbody elements aren't automatically inserted
1344 // IE will insert them into empty tables
1345 tbody: !div.getElementsByTagName("tbody").length,
1346
1347 // Make sure that link elements get serialized correctly by innerHTML
1348 // This requires a wrapper element in IE
1349 htmlSerialize: !!div.getElementsByTagName("link").length,
1350
1351 // Get the style information from getAttribute
1352 // (IE uses .cssText instead)
1353 style: /top/.test( a.getAttribute("style") ),
1354
1355 // Make sure that URLs aren't manipulated
1356 // (IE normalizes it by default)
1357 hrefNormalized: a.getAttribute("href") === "/a",
1358
1359 // Make sure that element opacity exists
1360 // (IE uses filter instead)
1361 // Use a regex to work around a WebKit issue. See #5145
1362 opacity: /^0.5/.test( a.style.opacity ),
1363
1364 // Verify style float existence
1365 // (IE uses styleFloat instead of cssFloat)
1366 cssFloat: !!a.style.cssFloat,
1367
1368 // Check the default checkbox/radio value ("" on WebKit; "on" elsewhere)
1369 checkOn: !!input.value,
1370
1371 // Make sure that a selected-by-default option has a working selected property.
1372 // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
1373 optSelected: opt.selected,
1374
1375 // Tests for enctype support on a form (#6743)
1376 enctype: !!document.createElement("form").enctype,
1377
1378 // Makes sure cloning an html5 element does not cause problems
1379 // Where outerHTML is undefined, this still works
1380 html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
1381
1382 // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
1383 boxModel: document.compatMode === "CSS1Compat",
1384
1385 // Will be defined later
1386 deleteExpando: true,
1387 noCloneEvent: true,
1388 inlineBlockNeedsLayout: false,
1389 shrinkWrapBlocks: false,
1390 reliableMarginRight: true,
1391 boxSizingReliable: true,
1392 pixelPosition: false
1393 };
1394
1395 // Make sure checked status is properly cloned
1396 input.checked = true;
1397 support.noCloneChecked = input.cloneNode( true ).checked;
1398
1399 // Make sure that the options inside disabled selects aren't marked as disabled
1400 // (WebKit marks them as disabled)
1401 select.disabled = true;
1402 support.optDisabled = !opt.disabled;
1403
1404 // Support: IE<9
1405 try {
1406 delete div.test;
1407 } catch( e ) {
1408 support.deleteExpando = false;
1409 }
1410
1411 // Check if we can trust getAttribute("value")
1412 input = document.createElement("input");
1413 input.setAttribute( "value", "" );
1414 support.input = input.getAttribute( "value" ) === "";
1415
1416 // Check if an input maintains its value after becoming a radio
1417 input.value = "t";
1418 input.setAttribute( "type", "radio" );
1419 support.radioValue = input.value === "t";
1420
1421 // #11217 - WebKit loses check when the name is after the checked attribute
1422 input.setAttribute( "checked", "t" );
1423 input.setAttribute( "name", "t" );
1424
1425 fragment = document.createDocumentFragment();
1426 fragment.appendChild( input );
1427
1428 // Check if a disconnected checkbox will retain its checked
1429 // value of true after appended to the DOM (IE6/7)
1430 support.appendChecked = input.checked;
1431
1432 // WebKit doesn't clone checked state correctly in fragments
1433 support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
1434
1435 // Support: IE<9
1436 // Opera does not clone events (and typeof div.attachEvent === undefined).
1437 // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
1438 if ( div.attachEvent ) {
1439 div.attachEvent( "onclick", function() {
1440 support.noCloneEvent = false;
1441 });
1442
1443 div.cloneNode( true ).click();
1444 }
1445
1446 // Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
1447 // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
1448 for ( i in { submit: true, change: true, focusin: true }) {
1449 div.setAttribute( eventName = "on" + i, "t" );
1450
1451 support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
1452 }
1453
1454 div.style.backgroundClip = "content-box";
1455 div.cloneNode( true ).style.backgroundClip = "";
1456 support.clearCloneStyle = div.style.backgroundClip === "content-box";
1457
1458 // Run tests that need a body at doc ready
1459 jQuery(function() {
1460 var container, marginDiv, tds,
1461 divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
1462 body = document.getElementsByTagName("body")[0];
1463
1464 if ( !body ) {
1465 // Return for frameset docs that don't have a body
1466 return;
1467 }
1468
1469 container = document.createElement("div");
1470 container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";
1471
1472 body.appendChild( container ).appendChild( div );
1473
1474 // Support: IE8
1475 // Check if table cells still have offsetWidth/Height when they are set
1476 // to display:none and there are still other visible table cells in a
1477 // table row; if so, offsetWidth/Height are not reliable for use when
1478 // determining if an element has been hidden directly using
1479 // display:none (it is still safe to use offsets if a parent element is
1480 // hidden; don safety goggles and see bug #4512 for more information).
1481 div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
1482 tds = div.getElementsByTagName("td");
1483 tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
1484 isSupported = ( tds[ 0 ].offsetHeight === 0 );
1485
1486 tds[ 0 ].style.display = "";
1487 tds[ 1 ].style.display = "none";
1488
1489 // Support: IE8
1490 // Check if empty table cells still have offsetWidth/Height
1491 support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
1492
1493 // Check box-sizing and margin behavior
1494 div.innerHTML = "";
1495 div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
1496 support.boxSizing = ( div.offsetWidth === 4 );
1497 support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
1498
1499 // Use window.getComputedStyle because jsdom on node.js will break without it.
1500 if ( window.getComputedStyle ) {
1501 support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
1502 support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
1503
1504 // Check if div with explicit width and no margin-right incorrectly
1505 // gets computed margin-right based on width of container. (#3333)
1506 // Fails in WebKit before Feb 2011 nightlies
1507 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
1508 marginDiv = div.appendChild( document.createElement("div") );
1509 marginDiv.style.cssText = div.style.cssText = divReset;
1510 marginDiv.style.marginRight = marginDiv.style.width = "0";
1511 div.style.width = "1px";
1512
1513 support.reliableMarginRight =
1514 !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
1515 }
1516
1517 if ( typeof div.style.zoom !== core_strundefined ) {
1518 // Support: IE<8
1519 // Check if natively block-level elements act like inline-block
1520 // elements when setting their display to 'inline' and giving
1521 // them layout
1522 div.innerHTML = "";
1523 div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
1524 support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
1525
1526 // Support: IE6
1527 // Check if elements with layout shrink-wrap their children
1528 div.style.display = "block";
1529 div.innerHTML = "<div></div>";
1530 div.firstChild.style.width = "5px";
1531 support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
1532
1533 if ( support.inlineBlockNeedsLayout ) {
1534 // Prevent IE 6 from affecting layout for positioned elements #11048
1535 // Prevent IE from shrinking the body in IE 7 mode #12869
1536 // Support: IE<8
1537 body.style.zoom = 1;
1538 }
1539 }
1540
1541 body.removeChild( container );
1542
1543 // Null elements to avoid leaks in IE
1544 container = div = tds = marginDiv = null;
1545 });
1546
1547 // Null elements to avoid leaks in IE
1548 all = select = fragment = opt = a = input = null;
1549
1550 return support;
1551})();
1552
1553var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
1554 rmultiDash = /([A-Z])/g;
1555
1556function internalData( elem, name, data, pvt /* Internal Use Only */ ){
1557 if ( !jQuery.acceptData( elem ) ) {
1558 return;
1559 }
1560
1561 var thisCache, ret,
1562 internalKey = jQuery.expando,
1563 getByName = typeof name === "string",
1564
1565 // We have to handle DOM nodes and JS objects differently because IE6-7
1566 // can't GC object references properly across the DOM-JS boundary
1567 isNode = elem.nodeType,
1568
1569 // Only DOM nodes need the global jQuery cache; JS object data is
1570 // attached directly to the object so GC can occur automatically
1571 cache = isNode ? jQuery.cache : elem,
1572
1573 // Only defining an ID for JS objects if its cache already exists allows
1574 // the code to shortcut on the same path as a DOM node with no cache
1575 id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
1576
1577 // Avoid doing any more work than we need to when trying to get data on an
1578 // object that has no data at all
1579 if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
1580 return;
1581 }
1582
1583 if ( !id ) {
1584 // Only DOM nodes need a new unique ID for each element since their data
1585 // ends up in the global cache
1586 if ( isNode ) {
1587 elem[ internalKey ] = id = core_deletedIds.pop() || jQuery.guid++;
1588 } else {
1589 id = internalKey;
1590 }
1591 }
1592
1593 if ( !cache[ id ] ) {
1594 cache[ id ] = {};
1595
1596 // Avoids exposing jQuery metadata on plain JS objects when the object
1597 // is serialized using JSON.stringify
1598 if ( !isNode ) {
1599 cache[ id ].toJSON = jQuery.noop;
1600 }
1601 }
1602
1603 // An object can be passed to jQuery.data instead of a key/value pair; this gets
1604 // shallow copied over onto the existing cache
1605 if ( typeof name === "object" || typeof name === "function" ) {
1606 if ( pvt ) {
1607 cache[ id ] = jQuery.extend( cache[ id ], name );
1608 } else {
1609 cache[ id ].data = jQuery.extend( cache[ id ].data, name );
1610 }
1611 }
1612
1613 thisCache = cache[ id ];
1614
1615 // jQuery data() is stored in a separate object inside the object's internal data
1616 // cache in order to avoid key collisions between internal data and user-defined
1617 // data.
1618 if ( !pvt ) {
1619 if ( !thisCache.data ) {
1620 thisCache.data = {};
1621 }
1622
1623 thisCache = thisCache.data;
1624 }
1625
1626 if ( data !== undefined ) {
1627 thisCache[ jQuery.camelCase( name ) ] = data;
1628 }
1629
1630 // Check for both converted-to-camel and non-converted data property names
1631 // If a data property was specified
1632 if ( getByName ) {
1633
1634 // First Try to find as-is property data
1635 ret = thisCache[ name ];
1636
1637 // Test for null|undefined property data
1638 if ( ret == null ) {
1639
1640 // Try to find the camelCased property
1641 ret = thisCache[ jQuery.camelCase( name ) ];
1642 }
1643 } else {
1644 ret = thisCache;
1645 }
1646
1647 return ret;
1648}
1649
1650function internalRemoveData( elem, name, pvt ) {
1651 if ( !jQuery.acceptData( elem ) ) {
1652 return;
1653 }
1654
1655 var i, l, thisCache,
1656 isNode = elem.nodeType,
1657
1658 // See jQuery.data for more information
1659 cache = isNode ? jQuery.cache : elem,
1660 id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
1661
1662 // If there is already no cache entry for this object, there is no
1663 // purpose in continuing
1664 if ( !cache[ id ] ) {
1665 return;
1666 }
1667
1668 if ( name ) {
1669
1670 thisCache = pvt ? cache[ id ] : cache[ id ].data;
1671
1672 if ( thisCache ) {
1673
1674 // Support array or space separated string names for data keys
1675 if ( !jQuery.isArray( name ) ) {
1676
1677 // try the string as a key before any manipulation
1678 if ( name in thisCache ) {
1679 name = [ name ];
1680 } else {
1681
1682 // split the camel cased version by spaces unless a key with the spaces exists
1683 name = jQuery.camelCase( name );
1684 if ( name in thisCache ) {
1685 name = [ name ];
1686 } else {
1687 name = name.split(" ");
1688 }
1689 }
1690 } else {
1691 // If "name" is an array of keys...
1692 // When data is initially created, via ("key", "val") signature,
1693 // keys will be converted to camelCase.
1694 // Since there is no way to tell _how_ a key was added, remove
1695 // both plain key and camelCase key. #12786
1696 // This will only penalize the array argument path.
1697 name = name.concat( jQuery.map( name, jQuery.camelCase ) );
1698 }
1699
1700 for ( i = 0, l = name.length; i < l; i++ ) {
1701 delete thisCache[ name[i] ];
1702 }
1703
1704 // If there is no data left in the cache, we want to continue
1705 // and let the cache object itself get destroyed
1706 if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
1707 return;
1708 }
1709 }
1710 }
1711
1712 // See jQuery.data for more information
1713 if ( !pvt ) {
1714 delete cache[ id ].data;
1715
1716 // Don't destroy the parent cache unless the internal data object
1717 // had been the only thing left in it
1718 if ( !isEmptyDataObject( cache[ id ] ) ) {
1719 return;
1720 }
1721 }
1722
1723 // Destroy the cache
1724 if ( isNode ) {
1725 jQuery.cleanData( [ elem ], true );
1726
1727 // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
1728 } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
1729 delete cache[ id ];
1730
1731 // When all else fails, null
1732 } else {
1733 cache[ id ] = null;
1734 }
1735}
1736
1737jQuery.extend({
1738 cache: {},
1739
1740 // Unique for each copy of jQuery on the page
1741 // Non-digits removed to match rinlinejQuery
1742 expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ),
1743
1744 // The following elements throw uncatchable exceptions if you
1745 // attempt to add expando properties to them.
1746 noData: {
1747 "embed": true,
1748 // Ban all objects except for Flash (which handle expandos)
1749 "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
1750 "applet": true
1751 },
1752
1753 hasData: function( elem ) {
1754 elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
1755 return !!elem && !isEmptyDataObject( elem );
1756 },
1757
1758 data: function( elem, name, data ) {
1759 return internalData( elem, name, data );
1760 },
1761
1762 removeData: function( elem, name ) {
1763 return internalRemoveData( elem, name );
1764 },
1765
1766 // For internal use only.
1767 _data: function( elem, name, data ) {
1768 return internalData( elem, name, data, true );
1769 },
1770
1771 _removeData: function( elem, name ) {
1772 return internalRemoveData( elem, name, true );
1773 },
1774
1775 // A method for determining if a DOM node can handle the data expando
1776 acceptData: function( elem ) {
1777 // Do not set data on non-element because it will not be cleared (#8335).
1778 if ( elem.nodeType && elem.nodeType !== 1 && elem.nodeType !== 9 ) {
1779 return false;
1780 }
1781
1782 var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
1783
1784 // nodes accept data unless otherwise specified; rejection can be conditional
1785 return !noData || noData !== true && elem.getAttribute("classid") === noData;
1786 }
1787});
1788
1789jQuery.fn.extend({
1790 data: function( key, value ) {
1791 var attrs, name,
1792 elem = this[0],
1793 i = 0,
1794 data = null;
1795
1796 // Gets all values
1797 if ( key === undefined ) {
1798 if ( this.length ) {
1799 data = jQuery.data( elem );
1800
1801 if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
1802 attrs = elem.attributes;
1803 for ( ; i < attrs.length; i++ ) {
1804 name = attrs[i].name;
1805
1806 if ( !name.indexOf( "data-" ) ) {
1807 name = jQuery.camelCase( name.slice(5) );
1808
1809 dataAttr( elem, name, data[ name ] );
1810 }
1811 }
1812 jQuery._data( elem, "parsedAttrs", true );
1813 }
1814 }
1815
1816 return data;
1817 }
1818
1819 // Sets multiple values
1820 if ( typeof key === "object" ) {
1821 return this.each(function() {
1822 jQuery.data( this, key );
1823 });
1824 }
1825
1826 return jQuery.access( this, function( value ) {
1827
1828 if ( value === undefined ) {
1829 // Try to fetch any internally stored data first
1830 return elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : null;
1831 }
1832
1833 this.each(function() {
1834 jQuery.data( this, key, value );
1835 });
1836 }, null, value, arguments.length > 1, null, true );
1837 },
1838
1839 removeData: function( key ) {
1840 return this.each(function() {
1841 jQuery.removeData( this, key );
1842 });
1843 }
1844});
1845
1846function dataAttr( elem, key, data ) {
1847 // If nothing was found internally, try to fetch any
1848 // data from the HTML5 data-* attribute
1849 if ( data === undefined && elem.nodeType === 1 ) {
1850
1851 var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
1852
1853 data = elem.getAttribute( name );
1854
1855 if ( typeof data === "string" ) {
1856 try {
1857 data = data === "true" ? true :
1858 data === "false" ? false :
1859 data === "null" ? null :
1860 // Only convert to a number if it doesn't change the string
1861 +data + "" === data ? +data :
1862 rbrace.test( data ) ? jQuery.parseJSON( data ) :
1863 data;
1864 } catch( e ) {}
1865
1866 // Make sure we set the data so it isn't changed later
1867 jQuery.data( elem, key, data );
1868
1869 } else {
1870 data = undefined;
1871 }
1872 }
1873
1874 return data;
1875}
1876
1877// checks a cache object for emptiness
1878function isEmptyDataObject( obj ) {
1879 var name;
1880 for ( name in obj ) {
1881
1882 // if the public data object is empty, the private is still empty
1883 if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
1884 continue;
1885 }
1886 if ( name !== "toJSON" ) {
1887 return false;
1888 }
1889 }
1890
1891 return true;
1892}
1893jQuery.extend({
1894 queue: function( elem, type, data ) {
1895 var queue;
1896
1897 if ( elem ) {
1898 type = ( type || "fx" ) + "queue";
1899 queue = jQuery._data( elem, type );
1900
1901 // Speed up dequeue by getting out quickly if this is just a lookup
1902 if ( data ) {
1903 if ( !queue || jQuery.isArray(data) ) {
1904 queue = jQuery._data( elem, type, jQuery.makeArray(data) );
1905 } else {
1906 queue.push( data );
1907 }
1908 }
1909 return queue || [];
1910 }
1911 },
1912
1913 dequeue: function( elem, type ) {
1914 type = type || "fx";
1915
1916 var queue = jQuery.queue( elem, type ),
1917 startLength = queue.length,
1918 fn = queue.shift(),
1919 hooks = jQuery._queueHooks( elem, type ),
1920 next = function() {
1921 jQuery.dequeue( elem, type );
1922 };
1923
1924 // If the fx queue is dequeued, always remove the progress sentinel
1925 if ( fn === "inprogress" ) {
1926 fn = queue.shift();
1927 startLength--;
1928 }
1929
1930 hooks.cur = fn;
1931 if ( fn ) {
1932
1933 // Add a progress sentinel to prevent the fx queue from being
1934 // automatically dequeued
1935 if ( type === "fx" ) {
1936 queue.unshift( "inprogress" );
1937 }
1938
1939 // clear up the last queue stop function
1940 delete hooks.stop;
1941 fn.call( elem, next, hooks );
1942 }
1943
1944 if ( !startLength && hooks ) {
1945 hooks.empty.fire();
1946 }
1947 },
1948
1949 // not intended for public consumption - generates a queueHooks object, or returns the current one
1950 _queueHooks: function( elem, type ) {
1951 var key = type + "queueHooks";
1952 return jQuery._data( elem, key ) || jQuery._data( elem, key, {
1953 empty: jQuery.Callbacks("once memory").add(function() {
1954 jQuery._removeData( elem, type + "queue" );
1955 jQuery._removeData( elem, key );
1956 })
1957 });
1958 }
1959});
1960
1961jQuery.fn.extend({
1962 queue: function( type, data ) {
1963 var setter = 2;
1964
1965 if ( typeof type !== "string" ) {
1966 data = type;
1967 type = "fx";
1968 setter--;
1969 }
1970
1971 if ( arguments.length < setter ) {
1972 return jQuery.queue( this[0], type );
1973 }
1974
1975 return data === undefined ?
1976 this :
1977 this.each(function() {
1978 var queue = jQuery.queue( this, type, data );
1979
1980 // ensure a hooks for this queue
1981 jQuery._queueHooks( this, type );
1982
1983 if ( type === "fx" && queue[0] !== "inprogress" ) {
1984 jQuery.dequeue( this, type );
1985 }
1986 });
1987 },
1988 dequeue: function( type ) {
1989 return this.each(function() {
1990 jQuery.dequeue( this, type );
1991 });
1992 },
1993 // Based off of the plugin by Clint Helfers, with permission.
1994 // http://blindsignals.com/index.php/2009/07/jquery-delay/
1995 delay: function( time, type ) {
1996 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
1997 type = type || "fx";
1998
1999 return this.queue( type, function( next, hooks ) {
2000 var timeout = setTimeout( next, time );
2001 hooks.stop = function() {
2002 clearTimeout( timeout );
2003 };
2004 });
2005 },
2006 clearQueue: function( type ) {
2007 return this.queue( type || "fx", [] );
2008 },
2009 // Get a promise resolved when queues of a certain type
2010 // are emptied (fx is the type by default)
2011 promise: function( type, obj ) {
2012 var tmp,
2013 count = 1,
2014 defer = jQuery.Deferred(),
2015 elements = this,
2016 i = this.length,
2017 resolve = function() {
2018 if ( !( --count ) ) {
2019 defer.resolveWith( elements, [ elements ] );
2020 }
2021 };
2022
2023 if ( typeof type !== "string" ) {
2024 obj = type;
2025 type = undefined;
2026 }
2027 type = type || "fx";
2028
2029 while( i-- ) {
2030 tmp = jQuery._data( elements[ i ], type + "queueHooks" );
2031 if ( tmp && tmp.empty ) {
2032 count++;
2033 tmp.empty.add( resolve );
2034 }
2035 }
2036 resolve();
2037 return defer.promise( obj );
2038 }
2039});
2040var nodeHook, boolHook,
2041 rclass = /[\t\r\n]/g,
2042 rreturn = /\r/g,
2043 rfocusable = /^(?:input|select|textarea|button|object)$/i,
2044 rclickable = /^(?:a|area)$/i,
2045 rboolean = /^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,
2046 ruseDefault = /^(?:checked|selected)$/i,
2047 getSetAttribute = jQuery.support.getSetAttribute,
2048 getSetInput = jQuery.support.input;
2049
2050jQuery.fn.extend({
2051 attr: function( name, value ) {
2052 return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
2053 },
2054
2055 removeAttr: function( name ) {
2056 return this.each(function() {
2057 jQuery.removeAttr( this, name );
2058 });
2059 },
2060
2061 prop: function( name, value ) {
2062 return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
2063 },
2064
2065 removeProp: function( name ) {
2066 name = jQuery.propFix[ name ] || name;
2067 return this.each(function() {
2068 // try/catch handles cases where IE balks (such as removing a property on window)
2069 try {
2070 this[ name ] = undefined;
2071 delete this[ name ];
2072 } catch( e ) {}
2073 });
2074 },
2075
2076 addClass: function( value ) {
2077 var classes, elem, cur, clazz, j,
2078 i = 0,
2079 len = this.length,
2080 proceed = typeof value === "string" && value;
2081
2082 if ( jQuery.isFunction( value ) ) {
2083 return this.each(function( j ) {
2084 jQuery( this ).addClass( value.call( this, j, this.className ) );
2085 });
2086 }
2087
2088 if ( proceed ) {
2089 // The disjunction here is for better compressibility (see removeClass)
2090 classes = ( value || "" ).match( core_rnotwhite ) || [];
2091
2092 for ( ; i < len; i++ ) {
2093 elem = this[ i ];
2094 cur = elem.nodeType === 1 && ( elem.className ?
2095 ( " " + elem.className + " " ).replace( rclass, " " ) :
2096 " "
2097 );
2098
2099 if ( cur ) {
2100 j = 0;
2101 while ( (clazz = classes[j++]) ) {
2102 if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
2103 cur += clazz + " ";
2104 }
2105 }
2106 elem.className = jQuery.trim( cur );
2107
2108 }
2109 }
2110 }
2111
2112 return this;
2113 },
2114
2115 removeClass: function( value ) {
2116 var classes, elem, cur, clazz, j,
2117 i = 0,
2118 len = this.length,
2119 proceed = arguments.length === 0 || typeof value === "string" && value;
2120
2121 if ( jQuery.isFunction( value ) ) {
2122 return this.each(function( j ) {
2123 jQuery( this ).removeClass( value.call( this, j, this.className ) );
2124 });
2125 }
2126 if ( proceed ) {
2127 classes = ( value || "" ).match( core_rnotwhite ) || [];
2128
2129 for ( ; i < len; i++ ) {
2130 elem = this[ i ];
2131 // This expression is here for better compressibility (see addClass)
2132 cur = elem.nodeType === 1 && ( elem.className ?
2133 ( " " + elem.className + " " ).replace( rclass, " " ) :
2134 ""
2135 );
2136
2137 if ( cur ) {
2138 j = 0;
2139 while ( (clazz = classes[j++]) ) {
2140 // Remove *all* instances
2141 while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
2142 cur = cur.replace( " " + clazz + " ", " " );
2143 }
2144 }
2145 elem.className = value ? jQuery.trim( cur ) : "";
2146 }
2147 }
2148 }
2149
2150 return this;
2151 },
2152
2153 toggleClass: function( value, stateVal ) {
2154 var type = typeof value,
2155 isBool = typeof stateVal === "boolean";
2156
2157 if ( jQuery.isFunction( value ) ) {
2158 return this.each(function( i ) {
2159 jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
2160 });
2161 }
2162
2163 return this.each(function() {
2164 if ( type === "string" ) {
2165 // toggle individual class names
2166 var className,
2167 i = 0,
2168 self = jQuery( this ),
2169 state = stateVal,
2170 classNames = value.match( core_rnotwhite ) || [];
2171
2172 while ( (className = classNames[ i++ ]) ) {
2173 // check each className given, space separated list
2174 state = isBool ? state : !self.hasClass( className );
2175 self[ state ? "addClass" : "removeClass" ]( className );
2176 }
2177
2178 // Toggle whole class name
2179 } else if ( type === core_strundefined || type === "boolean" ) {
2180 if ( this.className ) {
2181 // store className if set
2182 jQuery._data( this, "__className__", this.className );
2183 }
2184
2185 // If the element has a class name or if we're passed "false",
2186 // then remove the whole classname (if there was one, the above saved it).
2187 // Otherwise bring back whatever was previously saved (if anything),
2188 // falling back to the empty string if nothing was stored.
2189 this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
2190 }
2191 });
2192 },
2193
2194 hasClass: function( selector ) {
2195 var className = " " + selector + " ",
2196 i = 0,
2197 l = this.length;
2198 for ( ; i < l; i++ ) {
2199 if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
2200 return true;
2201 }
2202 }
2203
2204 return false;
2205 },
2206
2207 val: function( value ) {
2208 var ret, hooks, isFunction,
2209 elem = this[0];
2210
2211 if ( !arguments.length ) {
2212 if ( elem ) {
2213 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
2214
2215 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
2216 return ret;
2217 }
2218
2219 ret = elem.value;
2220
2221 return typeof ret === "string" ?
2222 // handle most common string cases
2223 ret.replace(rreturn, "") :
2224 // handle cases where value is null/undef or number
2225 ret == null ? "" : ret;
2226 }
2227
2228 return;
2229 }
2230
2231 isFunction = jQuery.isFunction( value );
2232
2233 return this.each(function( i ) {
2234 var val,
2235 self = jQuery(this);
2236
2237 if ( this.nodeType !== 1 ) {
2238 return;
2239 }
2240
2241 if ( isFunction ) {
2242 val = value.call( this, i, self.val() );
2243 } else {
2244 val = value;
2245 }
2246
2247 // Treat null/undefined as ""; convert numbers to string
2248 if ( val == null ) {
2249 val = "";
2250 } else if ( typeof val === "number" ) {
2251 val += "";
2252 } else if ( jQuery.isArray( val ) ) {
2253 val = jQuery.map(val, function ( value ) {
2254 return value == null ? "" : value + "";
2255 });
2256 }
2257
2258 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
2259
2260 // If set returns undefined, fall back to normal setting
2261 if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
2262 this.value = val;
2263 }
2264 });
2265 }
2266});
2267
2268jQuery.extend({
2269 valHooks: {
2270 option: {
2271 get: function( elem ) {
2272 // attributes.value is undefined in Blackberry 4.7 but
2273 // uses .value. See #6932
2274 var val = elem.attributes.value;
2275 return !val || val.specified ? elem.value : elem.text;
2276 }
2277 },
2278 select: {
2279 get: function( elem ) {
2280 var value, option,
2281 options = elem.options,
2282 index = elem.selectedIndex,
2283 one = elem.type === "select-one" || index < 0,
2284 values = one ? null : [],
2285 max = one ? index + 1 : options.length,
2286 i = index < 0 ?
2287 max :
2288 one ? index : 0;
2289
2290 // Loop through all the selected options
2291 for ( ; i < max; i++ ) {
2292 option = options[ i ];
2293
2294 // oldIE doesn't update selected after form reset (#2551)
2295 if ( ( option.selected || i === index ) &&
2296 // Don't return options that are disabled or in a disabled optgroup
2297 ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
2298 ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
2299
2300 // Get the specific value for the option
2301 value = jQuery( option ).val();
2302
2303 // We don't need an array for one selects
2304 if ( one ) {
2305 return value;
2306 }
2307
2308 // Multi-Selects return an array
2309 values.push( value );
2310 }
2311 }
2312
2313 return values;
2314 },
2315
2316 set: function( elem, value ) {
2317 var values = jQuery.makeArray( value );
2318
2319 jQuery(elem).find("option").each(function() {
2320 this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
2321 });
2322
2323 if ( !values.length ) {
2324 elem.selectedIndex = -1;
2325 }
2326 return values;
2327 }
2328 }
2329 },
2330
2331 attr: function( elem, name, value ) {
2332 var hooks, notxml, ret,
2333 nType = elem.nodeType;
2334
2335 // don't get/set attributes on text, comment and attribute nodes
2336 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2337 return;
2338 }
2339
2340 // Fallback to prop when attributes are not supported
2341 if ( typeof elem.getAttribute === core_strundefined ) {
2342 return jQuery.prop( elem, name, value );
2343 }
2344
2345 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2346
2347 // All attributes are lowercase
2348 // Grab necessary hook if one is defined
2349 if ( notxml ) {
2350 name = name.toLowerCase();
2351 hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
2352 }
2353
2354 if ( value !== undefined ) {
2355
2356 if ( value === null ) {
2357 jQuery.removeAttr( elem, name );
2358
2359 } else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
2360 return ret;
2361
2362 } else {
2363 elem.setAttribute( name, value + "" );
2364 return value;
2365 }
2366
2367 } else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
2368 return ret;
2369
2370 } else {
2371
2372 // In IE9+, Flash objects don't have .getAttribute (#12945)
2373 // Support: IE9+
2374 if ( typeof elem.getAttribute !== core_strundefined ) {
2375 ret = elem.getAttribute( name );
2376 }
2377
2378 // Non-existent attributes return null, we normalize to undefined
2379 return ret == null ?
2380 undefined :
2381 ret;
2382 }
2383 },
2384
2385 removeAttr: function( elem, value ) {
2386 var name, propName,
2387 i = 0,
2388 attrNames = value && value.match( core_rnotwhite );
2389
2390 if ( attrNames && elem.nodeType === 1 ) {
2391 while ( (name = attrNames[i++]) ) {
2392 propName = jQuery.propFix[ name ] || name;
2393
2394 // Boolean attributes get special treatment (#10870)
2395 if ( rboolean.test( name ) ) {
2396 // Set corresponding property to false for boolean attributes
2397 // Also clear defaultChecked/defaultSelected (if appropriate) for IE<8
2398 if ( !getSetAttribute && ruseDefault.test( name ) ) {
2399 elem[ jQuery.camelCase( "default-" + name ) ] =
2400 elem[ propName ] = false;
2401 } else {
2402 elem[ propName ] = false;
2403 }
2404
2405 // See #9699 for explanation of this approach (setting first, then removal)
2406 } else {
2407 jQuery.attr( elem, name, "" );
2408 }
2409
2410 elem.removeAttribute( getSetAttribute ? name : propName );
2411 }
2412 }
2413 },
2414
2415 attrHooks: {
2416 type: {
2417 set: function( elem, value ) {
2418 if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
2419 // Setting the type on a radio button after the value resets the value in IE6-9
2420 // Reset value to default in case type is set after value during creation
2421 var val = elem.value;
2422 elem.setAttribute( "type", value );
2423 if ( val ) {
2424 elem.value = val;
2425 }
2426 return value;
2427 }
2428 }
2429 }
2430 },
2431
2432 propFix: {
2433 tabindex: "tabIndex",
2434 readonly: "readOnly",
2435 "for": "htmlFor",
2436 "class": "className",
2437 maxlength: "maxLength",
2438 cellspacing: "cellSpacing",
2439 cellpadding: "cellPadding",
2440 rowspan: "rowSpan",
2441 colspan: "colSpan",
2442 usemap: "useMap",
2443 frameborder: "frameBorder",
2444 contenteditable: "contentEditable"
2445 },
2446
2447 prop: function( elem, name, value ) {
2448 var ret, hooks, notxml,
2449 nType = elem.nodeType;
2450
2451 // don't get/set properties on text, comment and attribute nodes
2452 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2453 return;
2454 }
2455
2456 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2457
2458 if ( notxml ) {
2459 // Fix name and attach hooks
2460 name = jQuery.propFix[ name ] || name;
2461 hooks = jQuery.propHooks[ name ];
2462 }
2463
2464 if ( value !== undefined ) {
2465 if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
2466 return ret;
2467
2468 } else {
2469 return ( elem[ name ] = value );
2470 }
2471
2472 } else {
2473 if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
2474 return ret;
2475
2476 } else {
2477 return elem[ name ];
2478 }
2479 }
2480 },
2481
2482 propHooks: {
2483 tabIndex: {
2484 get: function( elem ) {
2485 // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
2486 // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
2487 var attributeNode = elem.getAttributeNode("tabindex");
2488
2489 return attributeNode && attributeNode.specified ?
2490 parseInt( attributeNode.value, 10 ) :
2491 rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
2492 0 :
2493 undefined;
2494 }
2495 }
2496 }
2497});
2498
2499// Hook for boolean attributes
2500boolHook = {
2501 get: function( elem, name ) {
2502 var
2503 // Use .prop to determine if this attribute is understood as boolean
2504 prop = jQuery.prop( elem, name ),
2505
2506 // Fetch it accordingly
2507 attr = typeof prop === "boolean" && elem.getAttribute( name ),
2508 detail = typeof prop === "boolean" ?
2509
2510 getSetInput && getSetAttribute ?
2511 attr != null :
2512 // oldIE fabricates an empty string for missing boolean attributes
2513 // and conflates checked/selected into attroperties
2514 ruseDefault.test( name ) ?
2515 elem[ jQuery.camelCase( "default-" + name ) ] :
2516 !!attr :
2517
2518 // fetch an attribute node for properties not recognized as boolean
2519 elem.getAttributeNode( name );
2520
2521 return detail && detail.value !== false ?
2522 name.toLowerCase() :
2523 undefined;
2524 },
2525 set: function( elem, value, name ) {
2526 if ( value === false ) {
2527 // Remove boolean attributes when set to false
2528 jQuery.removeAttr( elem, name );
2529 } else if ( getSetInput && getSetAttribute || !ruseDefault.test( name ) ) {
2530 // IE<8 needs the *property* name
2531 elem.setAttribute( !getSetAttribute && jQuery.propFix[ name ] || name, name );
2532
2533 // Use defaultChecked and defaultSelected for oldIE
2534 } else {
2535 elem[ jQuery.camelCase( "default-" + name ) ] = elem[ name ] = true;
2536 }
2537
2538 return name;
2539 }
2540};
2541
2542// fix oldIE value attroperty
2543if ( !getSetInput || !getSetAttribute ) {
2544 jQuery.attrHooks.value = {
2545 get: function( elem, name ) {
2546 var ret = elem.getAttributeNode( name );
2547 return jQuery.nodeName( elem, "input" ) ?
2548
2549 // Ignore the value *property* by using defaultValue
2550 elem.defaultValue :
2551
2552 ret && ret.specified ? ret.value : undefined;
2553 },
2554 set: function( elem, value, name ) {
2555 if ( jQuery.nodeName( elem, "input" ) ) {
2556 // Does not return so that setAttribute is also used
2557 elem.defaultValue = value;
2558 } else {
2559 // Use nodeHook if defined (#1954); otherwise setAttribute is fine
2560 return nodeHook && nodeHook.set( elem, value, name );
2561 }
2562 }
2563 };
2564}
2565
2566// IE6/7 do not support getting/setting some attributes with get/setAttribute
2567if ( !getSetAttribute ) {
2568
2569 // Use this for any attribute in IE6/7
2570 // This fixes almost every IE6/7 issue
2571 nodeHook = jQuery.valHooks.button = {
2572 get: function( elem, name ) {
2573 var ret = elem.getAttributeNode( name );
2574 return ret && ( name === "id" || name === "name" || name === "coords" ? ret.value !== "" : ret.specified ) ?
2575 ret.value :
2576 undefined;
2577 },
2578 set: function( elem, value, name ) {
2579 // Set the existing or create a new attribute node
2580 var ret = elem.getAttributeNode( name );
2581 if ( !ret ) {
2582 elem.setAttributeNode(
2583 (ret = elem.ownerDocument.createAttribute( name ))
2584 );
2585 }
2586
2587 ret.value = value += "";
2588
2589 // Break association with cloned elements by also using setAttribute (#9646)
2590 return name === "value" || value === elem.getAttribute( name ) ?
2591 value :
2592 undefined;
2593 }
2594 };
2595
2596 // Set contenteditable to false on removals(#10429)
2597 // Setting to empty string throws an error as an invalid value
2598 jQuery.attrHooks.contenteditable = {
2599 get: nodeHook.get,
2600 set: function( elem, value, name ) {
2601 nodeHook.set( elem, value === "" ? false : value, name );
2602 }
2603 };
2604
2605 // Set width and height to auto instead of 0 on empty string( Bug #8150 )
2606 // This is for removals
2607 jQuery.each([ "width", "height" ], function( i, name ) {
2608 jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2609 set: function( elem, value ) {
2610 if ( value === "" ) {
2611 elem.setAttribute( name, "auto" );
2612 return value;
2613 }
2614 }
2615 });
2616 });
2617}
2618
2619
2620// Some attributes require a special call on IE
2621// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
2622if ( !jQuery.support.hrefNormalized ) {
2623 jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
2624 jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2625 get: function( elem ) {
2626 var ret = elem.getAttribute( name, 2 );
2627 return ret == null ? undefined : ret;
2628 }
2629 });
2630 });
2631
2632 // href/src property should get the full normalized URL (#10299/#12915)
2633 jQuery.each([ "href", "src" ], function( i, name ) {
2634 jQuery.propHooks[ name ] = {
2635 get: function( elem ) {
2636 return elem.getAttribute( name, 4 );
2637 }
2638 };
2639 });
2640}
2641
2642if ( !jQuery.support.style ) {
2643 jQuery.attrHooks.style = {
2644 get: function( elem ) {
2645 // Return undefined in the case of empty string
2646 // Note: IE uppercases css property names, but if we were to .toLowerCase()
2647 // .cssText, that would destroy case senstitivity in URL's, like in "background"
2648 return elem.style.cssText || undefined;
2649 },
2650 set: function( elem, value ) {
2651 return ( elem.style.cssText = value + "" );
2652 }
2653 };
2654}
2655
2656// Safari mis-reports the default selected property of an option
2657// Accessing the parent's selectedIndex property fixes it
2658if ( !jQuery.support.optSelected ) {
2659 jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
2660 get: function( elem ) {
2661 var parent = elem.parentNode;
2662
2663 if ( parent ) {
2664 parent.selectedIndex;
2665
2666 // Make sure that it also works with optgroups, see #5701
2667 if ( parent.parentNode ) {
2668 parent.parentNode.selectedIndex;
2669 }
2670 }
2671 return null;
2672 }
2673 });
2674}
2675
2676// IE6/7 call enctype encoding
2677if ( !jQuery.support.enctype ) {
2678 jQuery.propFix.enctype = "encoding";
2679}
2680
2681// Radios and checkboxes getter/setter
2682if ( !jQuery.support.checkOn ) {
2683 jQuery.each([ "radio", "checkbox" ], function() {
2684 jQuery.valHooks[ this ] = {
2685 get: function( elem ) {
2686 // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
2687 return elem.getAttribute("value") === null ? "on" : elem.value;
2688 }
2689 };
2690 });
2691}
2692jQuery.each([ "radio", "checkbox" ], function() {
2693 jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
2694 set: function( elem, value ) {
2695 if ( jQuery.isArray( value ) ) {
2696 return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
2697 }
2698 }
2699 });
2700});
2701var rformElems = /^(?:input|select|textarea)$/i,
2702 rkeyEvent = /^key/,
2703 rmouseEvent = /^(?:mouse|contextmenu)|click/,
2704 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
2705 rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
2706
2707function returnTrue() {
2708 return true;
2709}
2710
2711function returnFalse() {
2712 return false;
2713}
2714
2715/*
2716 * Helper functions for managing events -- not part of the public interface.
2717 * Props to Dean Edwards' addEvent library for many of the ideas.
2718 */
2719jQuery.event = {
2720
2721 global: {},
2722
2723 add: function( elem, types, handler, data, selector ) {
2724 var tmp, events, t, handleObjIn,
2725 special, eventHandle, handleObj,
2726 handlers, type, namespaces, origType,
2727 elemData = jQuery._data( elem );
2728
2729 // Don't attach events to noData or text/comment nodes (but allow plain objects)
2730 if ( !elemData ) {
2731 return;
2732 }
2733
2734 // Caller can pass in an object of custom data in lieu of the handler
2735 if ( handler.handler ) {
2736 handleObjIn = handler;
2737 handler = handleObjIn.handler;
2738 selector = handleObjIn.selector;
2739 }
2740
2741 // Make sure that the handler has a unique ID, used to find/remove it later
2742 if ( !handler.guid ) {
2743 handler.guid = jQuery.guid++;
2744 }
2745
2746 // Init the element's event structure and main handler, if this is the first
2747 if ( !(events = elemData.events) ) {
2748 events = elemData.events = {};
2749 }
2750 if ( !(eventHandle = elemData.handle) ) {
2751 eventHandle = elemData.handle = function( e ) {
2752 // Discard the second event of a jQuery.event.trigger() and
2753 // when an event is called after a page has unloaded
2754 return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ?
2755 jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
2756 undefined;
2757 };
2758 // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
2759 eventHandle.elem = elem;
2760 }
2761
2762 // Handle multiple events separated by a space
2763 // jQuery(...).bind("mouseover mouseout", fn);
2764 types = ( types || "" ).match( core_rnotwhite ) || [""];
2765 t = types.length;
2766 while ( t-- ) {
2767 tmp = rtypenamespace.exec( types[t] ) || [];
2768 type = origType = tmp[1];
2769 namespaces = ( tmp[2] || "" ).split( "." ).sort();
2770
2771 // If event changes its type, use the special event handlers for the changed type
2772 special = jQuery.event.special[ type ] || {};
2773
2774 // If selector defined, determine special event api type, otherwise given type
2775 type = ( selector ? special.delegateType : special.bindType ) || type;
2776
2777 // Update special based on newly reset type
2778 special = jQuery.event.special[ type ] || {};
2779
2780 // handleObj is passed to all event handlers
2781 handleObj = jQuery.extend({
2782 type: type,
2783 origType: origType,
2784 data: data,
2785 handler: handler,
2786 guid: handler.guid,
2787 selector: selector,
2788 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
2789 namespace: namespaces.join(".")
2790 }, handleObjIn );
2791
2792 // Init the event handler queue if we're the first
2793 if ( !(handlers = events[ type ]) ) {
2794 handlers = events[ type ] = [];
2795 handlers.delegateCount = 0;
2796
2797 // Only use addEventListener/attachEvent if the special events handler returns false
2798 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
2799 // Bind the global event handler to the element
2800 if ( elem.addEventListener ) {
2801 elem.addEventListener( type, eventHandle, false );
2802
2803 } else if ( elem.attachEvent ) {
2804 elem.attachEvent( "on" + type, eventHandle );
2805 }
2806 }
2807 }
2808
2809 if ( special.add ) {
2810 special.add.call( elem, handleObj );
2811
2812 if ( !handleObj.handler.guid ) {
2813 handleObj.handler.guid = handler.guid;
2814 }
2815 }
2816
2817 // Add to the element's handler list, delegates in front
2818 if ( selector ) {
2819 handlers.splice( handlers.delegateCount++, 0, handleObj );
2820 } else {
2821 handlers.push( handleObj );
2822 }
2823
2824 // Keep track of which events have ever been used, for event optimization
2825 jQuery.event.global[ type ] = true;
2826 }
2827
2828 // Nullify elem to prevent memory leaks in IE
2829 elem = null;
2830 },
2831
2832 // Detach an event or set of events from an element
2833 remove: function( elem, types, handler, selector, mappedTypes ) {
2834 var j, handleObj, tmp,
2835 origCount, t, events,
2836 special, handlers, type,
2837 namespaces, origType,
2838 elemData = jQuery.hasData( elem ) && jQuery._data( elem );
2839
2840 if ( !elemData || !(events = elemData.events) ) {
2841 return;
2842 }
2843
2844 // Once for each type.namespace in types; type may be omitted
2845 types = ( types || "" ).match( core_rnotwhite ) || [""];
2846 t = types.length;
2847 while ( t-- ) {
2848 tmp = rtypenamespace.exec( types[t] ) || [];
2849 type = origType = tmp[1];
2850 namespaces = ( tmp[2] || "" ).split( "." ).sort();
2851
2852 // Unbind all events (on this namespace, if provided) for the element
2853 if ( !type ) {
2854 for ( type in events ) {
2855 jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
2856 }
2857 continue;
2858 }
2859
2860 special = jQuery.event.special[ type ] || {};
2861 type = ( selector ? special.delegateType : special.bindType ) || type;
2862 handlers = events[ type ] || [];
2863 tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
2864
2865 // Remove matching events
2866 origCount = j = handlers.length;
2867 while ( j-- ) {
2868 handleObj = handlers[ j ];
2869
2870 if ( ( mappedTypes || origType === handleObj.origType ) &&
2871 ( !handler || handler.guid === handleObj.guid ) &&
2872 ( !tmp || tmp.test( handleObj.namespace ) ) &&
2873 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
2874 handlers.splice( j, 1 );
2875
2876 if ( handleObj.selector ) {
2877 handlers.delegateCount--;
2878 }
2879 if ( special.remove ) {
2880 special.remove.call( elem, handleObj );
2881 }
2882 }
2883 }
2884
2885 // Remove generic event handler if we removed something and no more handlers exist
2886 // (avoids potential for endless recursion during removal of special event handlers)
2887 if ( origCount && !handlers.length ) {
2888 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
2889 jQuery.removeEvent( elem, type, elemData.handle );
2890 }
2891
2892 delete events[ type ];
2893 }
2894 }
2895
2896 // Remove the expando if it's no longer used
2897 if ( jQuery.isEmptyObject( events ) ) {
2898 delete elemData.handle;
2899
2900 // removeData also checks for emptiness and clears the expando if empty
2901 // so use it instead of delete
2902 jQuery._removeData( elem, "events" );
2903 }
2904 },
2905
2906 trigger: function( event, data, elem, onlyHandlers ) {
2907 var handle, ontype, cur,
2908 bubbleType, special, tmp, i,
2909 eventPath = [ elem || document ],
2910 type = core_hasOwn.call( event, "type" ) ? event.type : event,
2911 namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
2912
2913 cur = tmp = elem = elem || document;
2914
2915 // Don't do events on text and comment nodes
2916 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
2917 return;
2918 }
2919
2920 // focus/blur morphs to focusin/out; ensure we're not firing them right now
2921 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
2922 return;
2923 }
2924
2925 if ( type.indexOf(".") >= 0 ) {
2926 // Namespaced trigger; create a regexp to match event type in handle()
2927 namespaces = type.split(".");
2928 type = namespaces.shift();
2929 namespaces.sort();
2930 }
2931 ontype = type.indexOf(":") < 0 && "on" + type;
2932
2933 // Caller can pass in a jQuery.Event object, Object, or just an event type string
2934 event = event[ jQuery.expando ] ?
2935 event :
2936 new jQuery.Event( type, typeof event === "object" && event );
2937
2938 event.isTrigger = true;
2939 event.namespace = namespaces.join(".");
2940 event.namespace_re = event.namespace ?
2941 new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
2942 null;
2943
2944 // Clean up the event in case it is being reused
2945 event.result = undefined;
2946 if ( !event.target ) {
2947 event.target = elem;
2948 }
2949
2950 // Clone any incoming data and prepend the event, creating the handler arg list
2951 data = data == null ?
2952 [ event ] :
2953 jQuery.makeArray( data, [ event ] );
2954
2955 // Allow special events to draw outside the lines
2956 special = jQuery.event.special[ type ] || {};
2957 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
2958 return;
2959 }
2960
2961 // Determine event propagation path in advance, per W3C events spec (#9951)
2962 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
2963 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
2964
2965 bubbleType = special.delegateType || type;
2966 if ( !rfocusMorph.test( bubbleType + type ) ) {
2967 cur = cur.parentNode;
2968 }
2969 for ( ; cur; cur = cur.parentNode ) {
2970 eventPath.push( cur );
2971 tmp = cur;
2972 }
2973
2974 // Only add window if we got to document (e.g., not plain obj or detached DOM)
2975 if ( tmp === (elem.ownerDocument || document) ) {
2976 eventPath.push( tmp.defaultView || tmp.parentWindow || window );
2977 }
2978 }
2979
2980 // Fire handlers on the event path
2981 i = 0;
2982 while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
2983
2984 event.type = i > 1 ?
2985 bubbleType :
2986 special.bindType || type;
2987
2988 // jQuery handler
2989 handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
2990 if ( handle ) {
2991 handle.apply( cur, data );
2992 }
2993
2994 // Native handler
2995 handle = ontype && cur[ ontype ];
2996 if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
2997 event.preventDefault();
2998 }
2999 }
3000 event.type = type;
3001
3002 // If nobody prevented the default action, do it now
3003 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
3004
3005 if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
3006 !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
3007
3008 // Call a native DOM method on the target with the same name name as the event.
3009 // Can't use an .isFunction() check here because IE6/7 fails that test.
3010 // Don't do default actions on window, that's where global variables be (#6170)
3011 if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
3012
3013 // Don't re-trigger an onFOO event when we call its FOO() method
3014 tmp = elem[ ontype ];
3015
3016 if ( tmp ) {
3017 elem[ ontype ] = null;
3018 }
3019
3020 // Prevent re-triggering of the same event, since we already bubbled it above
3021 jQuery.event.triggered = type;
3022 try {
3023 elem[ type ]();
3024 } catch ( e ) {
3025 // IE<9 dies on focus/blur to hidden element (#1486,#12518)
3026 // only reproducible on winXP IE8 native, not IE9 in IE8 mode
3027 }
3028 jQuery.event.triggered = undefined;
3029
3030 if ( tmp ) {
3031 elem[ ontype ] = tmp;
3032 }
3033 }
3034 }
3035 }
3036
3037 return event.result;
3038 },
3039
3040 dispatch: function( event ) {
3041
3042 // Make a writable jQuery.Event from the native event object
3043 event = jQuery.event.fix( event );
3044
3045 var i, ret, handleObj, matched, j,
3046 handlerQueue = [],
3047 args = core_slice.call( arguments ),
3048 handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
3049 special = jQuery.event.special[ event.type ] || {};
3050
3051 // Use the fix-ed jQuery.Event rather than the (read-only) native event
3052 args[0] = event;
3053 event.delegateTarget = this;
3054
3055 // Call the preDispatch hook for the mapped type, and let it bail if desired
3056 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
3057 return;
3058 }
3059
3060 // Determine handlers
3061 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
3062
3063 // Run delegates first; they may want to stop propagation beneath us
3064 i = 0;
3065 while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
3066 event.currentTarget = matched.elem;
3067
3068 j = 0;
3069 while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
3070
3071 // Triggered event must either 1) have no namespace, or
3072 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
3073 if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
3074
3075 event.handleObj = handleObj;
3076 event.data = handleObj.data;
3077
3078 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
3079 .apply( matched.elem, args );
3080
3081 if ( ret !== undefined ) {
3082 if ( (event.result = ret) === false ) {
3083 event.preventDefault();
3084 event.stopPropagation();
3085 }
3086 }
3087 }
3088 }
3089 }
3090
3091 // Call the postDispatch hook for the mapped type
3092 if ( special.postDispatch ) {
3093 special.postDispatch.call( this, event );
3094 }
3095
3096 return event.result;
3097 },
3098
3099 handlers: function( event, handlers ) {
3100 var sel, handleObj, matches, i,
3101 handlerQueue = [],
3102 delegateCount = handlers.delegateCount,
3103 cur = event.target;
3104
3105 // Find delegate handlers
3106 // Black-hole SVG <use> instance trees (#13180)
3107 // Avoid non-left-click bubbling in Firefox (#3861)
3108 if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
3109
3110 for ( ; cur != this; cur = cur.parentNode || this ) {
3111
3112 // Don't check non-elements (#13208)
3113 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
3114 if ( cur.nodeType === 1 && (cur.disabled !== true || event.type !== "click") ) {
3115 matches = [];
3116 for ( i = 0; i < delegateCount; i++ ) {
3117 handleObj = handlers[ i ];
3118
3119 // Don't conflict with Object.prototype properties (#13203)
3120 sel = handleObj.selector + " ";
3121
3122 if ( matches[ sel ] === undefined ) {
3123 matches[ sel ] = handleObj.needsContext ?
3124 jQuery( sel, this ).index( cur ) >= 0 :
3125 jQuery.find( sel, this, null, [ cur ] ).length;
3126 }
3127 if ( matches[ sel ] ) {
3128 matches.push( handleObj );
3129 }
3130 }
3131 if ( matches.length ) {
3132 handlerQueue.push({ elem: cur, handlers: matches });
3133 }
3134 }
3135 }
3136 }
3137
3138 // Add the remaining (directly-bound) handlers
3139 if ( delegateCount < handlers.length ) {
3140 handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
3141 }
3142
3143 return handlerQueue;
3144 },
3145
3146 fix: function( event ) {
3147 if ( event[ jQuery.expando ] ) {
3148 return event;
3149 }
3150
3151 // Create a writable copy of the event object and normalize some properties
3152 var i, prop, copy,
3153 type = event.type,
3154 originalEvent = event,
3155 fixHook = this.fixHooks[ type ];
3156
3157 if ( !fixHook ) {
3158 this.fixHooks[ type ] = fixHook =
3159 rmouseEvent.test( type ) ? this.mouseHooks :
3160 rkeyEvent.test( type ) ? this.keyHooks :
3161 {};
3162 }
3163 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
3164
3165 event = new jQuery.Event( originalEvent );
3166
3167 i = copy.length;
3168 while ( i-- ) {
3169 prop = copy[ i ];
3170 event[ prop ] = originalEvent[ prop ];
3171 }
3172
3173 // Support: IE<9
3174 // Fix target property (#1925)
3175 if ( !event.target ) {
3176 event.target = originalEvent.srcElement || document;
3177 }
3178
3179 // Support: Chrome 23+, Safari?
3180 // Target should not be a text node (#504, #13143)
3181 if ( event.target.nodeType === 3 ) {
3182 event.target = event.target.parentNode;
3183 }
3184
3185 // Support: IE<9
3186 // For mouse/key events, metaKey==false if it's undefined (#3368, #11328)
3187 event.metaKey = !!event.metaKey;
3188
3189 return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
3190 },
3191
3192 // Includes some event props shared by KeyEvent and MouseEvent
3193 props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
3194
3195 fixHooks: {},
3196
3197 keyHooks: {
3198 props: "char charCode key keyCode".split(" "),
3199 filter: function( event, original ) {
3200
3201 // Add which for key events
3202 if ( event.which == null ) {
3203 event.which = original.charCode != null ? original.charCode : original.keyCode;
3204 }
3205
3206 return event;
3207 }
3208 },
3209
3210 mouseHooks: {
3211 props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
3212 filter: function( event, original ) {
3213 var body, eventDoc, doc,
3214 button = original.button,
3215 fromElement = original.fromElement;
3216
3217 // Calculate pageX/Y if missing and clientX/Y available
3218 if ( event.pageX == null && original.clientX != null ) {
3219 eventDoc = event.target.ownerDocument || document;
3220 doc = eventDoc.documentElement;
3221 body = eventDoc.body;
3222
3223 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
3224 event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
3225 }
3226
3227 // Add relatedTarget, if necessary
3228 if ( !event.relatedTarget && fromElement ) {
3229 event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
3230 }
3231
3232 // Add which for click: 1 === left; 2 === middle; 3 === right
3233 // Note: button is not normalized, so don't use it
3234 if ( !event.which && button !== undefined ) {
3235 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
3236 }
3237
3238 return event;
3239 }
3240 },
3241
3242 special: {
3243 load: {
3244 // Prevent triggered image.load events from bubbling to window.load
3245 noBubble: true
3246 },
3247 click: {
3248 // For checkbox, fire native event so checked state will be right
3249 trigger: function() {
3250 if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) {
3251 this.click();
3252 return false;
3253 }
3254 }
3255 },
3256 focus: {
3257 // Fire native event if possible so blur/focus sequence is correct
3258 trigger: function() {
3259 if ( this !== document.activeElement && this.focus ) {
3260 try {
3261 this.focus();
3262 return false;
3263 } catch ( e ) {
3264 // Support: IE<9
3265 // If we error on focus to hidden element (#1486, #12518),
3266 // let .trigger() run the handlers
3267 }
3268 }
3269 },
3270 delegateType: "focusin"
3271 },
3272 blur: {
3273 trigger: function() {
3274 if ( this === document.activeElement && this.blur ) {
3275 this.blur();
3276 return false;
3277 }
3278 },
3279 delegateType: "focusout"
3280 },
3281
3282 beforeunload: {
3283 postDispatch: function( event ) {
3284
3285 // Even when returnValue equals to undefined Firefox will still show alert
3286 if ( event.result !== undefined ) {
3287 event.originalEvent.returnValue = event.result;
3288 }
3289 }
3290 }
3291 },
3292
3293 simulate: function( type, elem, event, bubble ) {
3294 // Piggyback on a donor event to simulate a different one.
3295 // Fake originalEvent to avoid donor's stopPropagation, but if the
3296 // simulated event prevents default then we do the same on the donor.
3297 var e = jQuery.extend(
3298 new jQuery.Event(),
3299 event,
3300 { type: type,
3301 isSimulated: true,
3302 originalEvent: {}
3303 }
3304 );
3305 if ( bubble ) {
3306 jQuery.event.trigger( e, null, elem );
3307 } else {
3308 jQuery.event.dispatch.call( elem, e );
3309 }
3310 if ( e.isDefaultPrevented() ) {
3311 event.preventDefault();
3312 }
3313 }
3314};
3315
3316jQuery.removeEvent = document.removeEventListener ?
3317 function( elem, type, handle ) {
3318 if ( elem.removeEventListener ) {
3319 elem.removeEventListener( type, handle, false );
3320 }
3321 } :
3322 function( elem, type, handle ) {
3323 var name = "on" + type;
3324
3325 if ( elem.detachEvent ) {
3326
3327 // #8545, #7054, preventing memory leaks for custom events in IE6-8
3328 // detachEvent needed property on element, by name of that event, to properly expose it to GC
3329 if ( typeof elem[ name ] === core_strundefined ) {
3330 elem[ name ] = null;
3331 }
3332
3333 elem.detachEvent( name, handle );
3334 }
3335 };
3336
3337jQuery.Event = function( src, props ) {
3338 // Allow instantiation without the 'new' keyword
3339 if ( !(this instanceof jQuery.Event) ) {
3340 return new jQuery.Event( src, props );
3341 }
3342
3343 // Event object
3344 if ( src && src.type ) {
3345 this.originalEvent = src;
3346 this.type = src.type;
3347
3348 // Events bubbling up the document may have been marked as prevented
3349 // by a handler lower down the tree; reflect the correct value.
3350 this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
3351 src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
3352
3353 // Event type
3354 } else {
3355 this.type = src;
3356 }
3357
3358 // Put explicitly provided properties onto the event object
3359 if ( props ) {
3360 jQuery.extend( this, props );
3361 }
3362
3363 // Create a timestamp if incoming event doesn't have one
3364 this.timeStamp = src && src.timeStamp || jQuery.now();
3365
3366 // Mark it as fixed
3367 this[ jQuery.expando ] = true;
3368};
3369
3370// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
3371// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
3372jQuery.Event.prototype = {
3373 isDefaultPrevented: returnFalse,
3374 isPropagationStopped: returnFalse,
3375 isImmediatePropagationStopped: returnFalse,
3376
3377 preventDefault: function() {
3378 var e = this.originalEvent;
3379
3380 this.isDefaultPrevented = returnTrue;
3381 if ( !e ) {
3382 return;
3383 }
3384
3385 // If preventDefault exists, run it on the original event
3386 if ( e.preventDefault ) {
3387 e.preventDefault();
3388
3389 // Support: IE
3390 // Otherwise set the returnValue property of the original event to false
3391 } else {
3392 e.returnValue = false;
3393 }
3394 },
3395 stopPropagation: function() {
3396 var e = this.originalEvent;
3397
3398 this.isPropagationStopped = returnTrue;
3399 if ( !e ) {
3400 return;
3401 }
3402 // If stopPropagation exists, run it on the original event
3403 if ( e.stopPropagation ) {
3404 e.stopPropagation();
3405 }
3406
3407 // Support: IE
3408 // Set the cancelBubble property of the original event to true
3409 e.cancelBubble = true;
3410 },
3411 stopImmediatePropagation: function() {
3412 this.isImmediatePropagationStopped = returnTrue;
3413 this.stopPropagation();
3414 }
3415};
3416
3417// Create mouseenter/leave events using mouseover/out and event-time checks
3418jQuery.each({
3419 mouseenter: "mouseover",
3420 mouseleave: "mouseout"
3421}, function( orig, fix ) {
3422 jQuery.event.special[ orig ] = {
3423 delegateType: fix,
3424 bindType: fix,
3425
3426 handle: function( event ) {
3427 var ret,
3428 target = this,
3429 related = event.relatedTarget,
3430 handleObj = event.handleObj;
3431
3432 // For mousenter/leave call the handler if related is outside the target.
3433 // NB: No relatedTarget if the mouse left/entered the browser window
3434 if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
3435 event.type = handleObj.origType;
3436 ret = handleObj.handler.apply( this, arguments );
3437 event.type = fix;
3438 }
3439 return ret;
3440 }
3441 };
3442});
3443
3444// IE submit delegation
3445if ( !jQuery.support.submitBubbles ) {
3446
3447 jQuery.event.special.submit = {
3448 setup: function() {
3449 // Only need this for delegated form submit events
3450 if ( jQuery.nodeName( this, "form" ) ) {
3451 return false;
3452 }
3453
3454 // Lazy-add a submit handler when a descendant form may potentially be submitted
3455 jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
3456 // Node name check avoids a VML-related crash in IE (#9807)
3457 var elem = e.target,
3458 form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
3459 if ( form && !jQuery._data( form, "submitBubbles" ) ) {
3460 jQuery.event.add( form, "submit._submit", function( event ) {
3461 event._submit_bubble = true;
3462 });
3463 jQuery._data( form, "submitBubbles", true );
3464 }
3465 });
3466 // return undefined since we don't need an event listener
3467 },
3468
3469 postDispatch: function( event ) {
3470 // If form was submitted by the user, bubble the event up the tree
3471 if ( event._submit_bubble ) {
3472 delete event._submit_bubble;
3473 if ( this.parentNode && !event.isTrigger ) {
3474 jQuery.event.simulate( "submit", this.parentNode, event, true );
3475 }
3476 }
3477 },
3478
3479 teardown: function() {
3480 // Only need this for delegated form submit events
3481 if ( jQuery.nodeName( this, "form" ) ) {
3482 return false;
3483 }
3484
3485 // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
3486 jQuery.event.remove( this, "._submit" );
3487 }
3488 };
3489}
3490
3491// IE change delegation and checkbox/radio fix
3492if ( !jQuery.support.changeBubbles ) {
3493
3494 jQuery.event.special.change = {
3495
3496 setup: function() {
3497
3498 if ( rformElems.test( this.nodeName ) ) {
3499 // IE doesn't fire change on a check/radio until blur; trigger it on click
3500 // after a propertychange. Eat the blur-change in special.change.handle.
3501 // This still fires onchange a second time for check/radio after blur.
3502 if ( this.type === "checkbox" || this.type === "radio" ) {
3503 jQuery.event.add( this, "propertychange._change", function( event ) {
3504 if ( event.originalEvent.propertyName === "checked" ) {
3505 this._just_changed = true;
3506 }
3507 });
3508 jQuery.event.add( this, "click._change", function( event ) {
3509 if ( this._just_changed && !event.isTrigger ) {
3510 this._just_changed = false;
3511 }
3512 // Allow triggered, simulated change events (#11500)
3513 jQuery.event.simulate( "change", this, event, true );
3514 });
3515 }
3516 return false;
3517 }
3518 // Delegated event; lazy-add a change handler on descendant inputs
3519 jQuery.event.add( this, "beforeactivate._change", function( e ) {
3520 var elem = e.target;
3521
3522 if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "changeBubbles" ) ) {
3523 jQuery.event.add( elem, "change._change", function( event ) {
3524 if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
3525 jQuery.event.simulate( "change", this.parentNode, event, true );
3526 }
3527 });
3528 jQuery._data( elem, "changeBubbles", true );
3529 }
3530 });
3531 },
3532
3533 handle: function( event ) {
3534 var elem = event.target;
3535
3536 // Swallow native change events from checkbox/radio, we already triggered them above
3537 if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
3538 return event.handleObj.handler.apply( this, arguments );
3539 }
3540 },
3541
3542 teardown: function() {
3543 jQuery.event.remove( this, "._change" );
3544
3545 return !rformElems.test( this.nodeName );
3546 }
3547 };
3548}
3549
3550// Create "bubbling" focus and blur events
3551if ( !jQuery.support.focusinBubbles ) {
3552 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
3553
3554 // Attach a single capturing handler while someone wants focusin/focusout
3555 var attaches = 0,
3556 handler = function( event ) {
3557 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
3558 };
3559
3560 jQuery.event.special[ fix ] = {
3561 setup: function() {
3562 if ( attaches++ === 0 ) {
3563 document.addEventListener( orig, handler, true );
3564 }
3565 },
3566 teardown: function() {
3567 if ( --attaches === 0 ) {
3568 document.removeEventListener( orig, handler, true );
3569 }
3570 }
3571 };
3572 });
3573}
3574
3575jQuery.fn.extend({
3576
3577 on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
3578 var type, origFn;
3579
3580 // Types can be a map of types/handlers
3581 if ( typeof types === "object" ) {
3582 // ( types-Object, selector, data )
3583 if ( typeof selector !== "string" ) {
3584 // ( types-Object, data )
3585 data = data || selector;
3586 selector = undefined;
3587 }
3588 for ( type in types ) {
3589 this.on( type, selector, data, types[ type ], one );
3590 }
3591 return this;
3592 }
3593
3594 if ( data == null && fn == null ) {
3595 // ( types, fn )
3596 fn = selector;
3597 data = selector = undefined;
3598 } else if ( fn == null ) {
3599 if ( typeof selector === "string" ) {
3600 // ( types, selector, fn )
3601 fn = data;
3602 data = undefined;
3603 } else {
3604 // ( types, data, fn )
3605 fn = data;
3606 data = selector;
3607 selector = undefined;
3608 }
3609 }
3610 if ( fn === false ) {
3611 fn = returnFalse;
3612 } else if ( !fn ) {
3613 return this;
3614 }
3615
3616 if ( one === 1 ) {
3617 origFn = fn;
3618 fn = function( event ) {
3619 // Can use an empty set, since event contains the info
3620 jQuery().off( event );
3621 return origFn.apply( this, arguments );
3622 };
3623 // Use same guid so caller can remove using origFn
3624 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
3625 }
3626 return this.each( function() {
3627 jQuery.event.add( this, types, fn, data, selector );
3628 });
3629 },
3630 one: function( types, selector, data, fn ) {
3631 return this.on( types, selector, data, fn, 1 );
3632 },
3633 off: function( types, selector, fn ) {
3634 var handleObj, type;
3635 if ( types && types.preventDefault && types.handleObj ) {
3636 // ( event ) dispatched jQuery.Event
3637 handleObj = types.handleObj;
3638 jQuery( types.delegateTarget ).off(
3639 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
3640 handleObj.selector,
3641 handleObj.handler
3642 );
3643 return this;
3644 }
3645 if ( typeof types === "object" ) {
3646 // ( types-object [, selector] )
3647 for ( type in types ) {
3648 this.off( type, selector, types[ type ] );
3649 }
3650 return this;
3651 }
3652 if ( selector === false || typeof selector === "function" ) {
3653 // ( types [, fn] )
3654 fn = selector;
3655 selector = undefined;
3656 }
3657 if ( fn === false ) {
3658 fn = returnFalse;
3659 }
3660 return this.each(function() {
3661 jQuery.event.remove( this, types, fn, selector );
3662 });
3663 },
3664
3665 bind: function( types, data, fn ) {
3666 return this.on( types, null, data, fn );
3667 },
3668 unbind: function( types, fn ) {
3669 return this.off( types, null, fn );
3670 },
3671
3672 delegate: function( selector, types, data, fn ) {
3673 return this.on( types, selector, data, fn );
3674 },
3675 undelegate: function( selector, types, fn ) {
3676 // ( namespace ) or ( selector, types [, fn] )
3677 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
3678 },
3679
3680 trigger: function( type, data ) {
3681 return this.each(function() {
3682 jQuery.event.trigger( type, data, this );
3683 });
3684 },
3685 triggerHandler: function( type, data ) {
3686 var elem = this[0];
3687 if ( elem ) {
3688 return jQuery.event.trigger( type, data, elem, true );
3689 }
3690 }
3691});
3692/*!
3693 * Sizzle CSS Selector Engine
3694 * Copyright 2012 jQuery Foundation and other contributors
3695 * Released under the MIT license
3696 * http://sizzlejs.com/
3697 */
3698(function( window, undefined ) {
3699
3700var i,
3701 cachedruns,
3702 Expr,
3703 getText,
3704 isXML,
3705 compile,
3706 hasDuplicate,
3707 outermostContext,
3708
3709 // Local document vars
3710 setDocument,
3711 document,
3712 docElem,
3713 documentIsXML,
3714 rbuggyQSA,
3715 rbuggyMatches,
3716 matches,
3717 contains,
3718 sortOrder,
3719
3720 // Instance-specific data
3721 expando = "sizzle" + -(new Date()),
3722 preferredDoc = window.document,
3723 support = {},
3724 dirruns = 0,
3725 done = 0,
3726 classCache = createCache(),
3727 tokenCache = createCache(),
3728 compilerCache = createCache(),
3729
3730 // General-purpose constants
3731 strundefined = typeof undefined,
3732 MAX_NEGATIVE = 1 << 31,
3733
3734 // Array methods
3735 arr = [],
3736 pop = arr.pop,
3737 push = arr.push,
3738 slice = arr.slice,
3739 // Use a stripped-down indexOf if we can't use a native one
3740 indexOf = arr.indexOf || function( elem ) {
3741 var i = 0,
3742 len = this.length;
3743 for ( ; i < len; i++ ) {
3744 if ( this[i] === elem ) {
3745 return i;
3746 }
3747 }
3748 return -1;
3749 },
3750
3751
3752 // Regular expressions
3753
3754 // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
3755 whitespace = "[\\x20\\t\\r\\n\\f]",
3756 // http://www.w3.org/TR/css3-syntax/#characters
3757 characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
3758
3759 // Loosely modeled on CSS identifier characters
3760 // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
3761 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
3762 identifier = characterEncoding.replace( "w", "w#" ),
3763
3764 // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
3765 operators = "([*^$|!~]?=)",
3766 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
3767 "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
3768
3769 // Prefer arguments quoted,
3770 // then not containing pseudos/brackets,
3771 // then attribute selectors/non-parenthetical expressions,
3772 // then anything else
3773 // These preferences are here to reduce the number of selectors
3774 // needing tokenize in the PSEUDO preFilter
3775 pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)",
3776
3777 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
3778 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
3779
3780 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
3781 rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
3782 rpseudo = new RegExp( pseudos ),
3783 ridentifier = new RegExp( "^" + identifier + "$" ),
3784
3785 matchExpr = {
3786 "ID": new RegExp( "^#(" + characterEncoding + ")" ),
3787 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
3788 "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
3789 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
3790 "ATTR": new RegExp( "^" + attributes ),
3791 "PSEUDO": new RegExp( "^" + pseudos ),
3792 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
3793 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
3794 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
3795 // For use in libraries implementing .is()
3796 // We use this for POS matching in `select`
3797 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
3798 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
3799 },
3800
3801 rsibling = /[\x20\t\r\n\f]*[+~]/,
3802
3803 rnative = /^[^{]+\{\s*\[native code/,
3804
3805 // Easily-parseable/retrievable ID or TAG or CLASS selectors
3806 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
3807
3808 rinputs = /^(?:input|select|textarea|button)$/i,
3809 rheader = /^h\d$/i,
3810
3811 rescape = /'|\\/g,
3812 rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
3813
3814 // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
3815 runescape = /\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,
3816 funescape = function( _, escaped ) {
3817 var high = "0x" + escaped - 0x10000;
3818 // NaN means non-codepoint
3819 return high !== high ?
3820 escaped :
3821 // BMP codepoint
3822 high < 0 ?
3823 String.fromCharCode( high + 0x10000 ) :
3824 // Supplemental Plane codepoint (surrogate pair)
3825 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
3826 };
3827
3828// Use a stripped-down slice if we can't use a native one
3829try {
3830 slice.call( preferredDoc.documentElement.childNodes, 0 )[0].nodeType;
3831} catch ( e ) {
3832 slice = function( i ) {
3833 var elem,
3834 results = [];
3835 while ( (elem = this[i++]) ) {
3836 results.push( elem );
3837 }
3838 return results;
3839 };
3840}
3841
3842/**
3843 * For feature detection
3844 * @param {Function} fn The function to test for native support
3845 */
3846function isNative( fn ) {
3847 return rnative.test( fn + "" );
3848}
3849
3850/**
3851 * Create key-value caches of limited size
3852 * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
3853 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
3854 * deleting the oldest entry
3855 */
3856function createCache() {
3857 var cache,
3858 keys = [];
3859
3860 return (cache = function( key, value ) {
3861 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
3862 if ( keys.push( key += " " ) > Expr.cacheLength ) {
3863 // Only keep the most recent entries
3864 delete cache[ keys.shift() ];
3865 }
3866 return (cache[ key ] = value);
3867 });
3868}
3869
3870/**
3871 * Mark a function for special use by Sizzle
3872 * @param {Function} fn The function to mark
3873 */
3874function markFunction( fn ) {
3875 fn[ expando ] = true;
3876 return fn;
3877}
3878
3879/**
3880 * Support testing using an element
3881 * @param {Function} fn Passed the created div and expects a boolean result
3882 */
3883function assert( fn ) {
3884 var div = document.createElement("div");
3885
3886 try {
3887 return fn( div );
3888 } catch (e) {
3889 return false;
3890 } finally {
3891 // release memory in IE
3892 div = null;
3893 }
3894}
3895
3896function Sizzle( selector, context, results, seed ) {
3897 var match, elem, m, nodeType,
3898 // QSA vars
3899 i, groups, old, nid, newContext, newSelector;
3900
3901 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
3902 setDocument( context );
3903 }
3904
3905 context = context || document;
3906 results = results || [];
3907
3908 if ( !selector || typeof selector !== "string" ) {
3909 return results;
3910 }
3911
3912 if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
3913 return [];
3914 }
3915
3916 if ( !documentIsXML && !seed ) {
3917
3918 // Shortcuts
3919 if ( (match = rquickExpr.exec( selector )) ) {
3920 // Speed-up: Sizzle("#ID")
3921 if ( (m = match[1]) ) {
3922 if ( nodeType === 9 ) {
3923 elem = context.getElementById( m );
3924 // Check parentNode to catch when Blackberry 4.6 returns
3925 // nodes that are no longer in the document #6963
3926 if ( elem && elem.parentNode ) {
3927 // Handle the case where IE, Opera, and Webkit return items
3928 // by name instead of ID
3929 if ( elem.id === m ) {
3930 results.push( elem );
3931 return results;
3932 }
3933 } else {
3934 return results;
3935 }
3936 } else {
3937 // Context is not a document
3938 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
3939 contains( context, elem ) && elem.id === m ) {
3940 results.push( elem );
3941 return results;
3942 }
3943 }
3944
3945 // Speed-up: Sizzle("TAG")
3946 } else if ( match[2] ) {
3947 push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
3948 return results;
3949
3950 // Speed-up: Sizzle(".CLASS")
3951 } else if ( (m = match[3]) && support.getByClassName && context.getElementsByClassName ) {
3952 push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
3953 return results;
3954 }
3955 }
3956
3957 // QSA path
3958 if ( support.qsa && !rbuggyQSA.test(selector) ) {
3959 old = true;
3960 nid = expando;
3961 newContext = context;
3962 newSelector = nodeType === 9 && selector;
3963
3964 // qSA works strangely on Element-rooted queries
3965 // We can work around this by specifying an extra ID on the root
3966 // and working up from there (Thanks to Andrew Dupont for the technique)
3967 // IE 8 doesn't work on object elements
3968 if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
3969 groups = tokenize( selector );
3970
3971 if ( (old = context.getAttribute("id")) ) {
3972 nid = old.replace( rescape, "\\$&" );
3973 } else {
3974 context.setAttribute( "id", nid );
3975 }
3976 nid = "[id='" + nid + "'] ";
3977
3978 i = groups.length;
3979 while ( i-- ) {
3980 groups[i] = nid + toSelector( groups[i] );
3981 }
3982 newContext = rsibling.test( selector ) && context.parentNode || context;
3983 newSelector = groups.join(",");
3984 }
3985
3986 if ( newSelector ) {
3987 try {
3988 push.apply( results, slice.call( newContext.querySelectorAll(
3989 newSelector
3990 ), 0 ) );
3991 return results;
3992 } catch(qsaError) {
3993 } finally {
3994 if ( !old ) {
3995 context.removeAttribute("id");
3996 }
3997 }
3998 }
3999 }
4000 }
4001
4002 // All others
4003 return select( selector.replace( rtrim, "$1" ), context, results, seed );
4004}
4005
4006/**
4007 * Detect xml
4008 * @param {Element|Object} elem An element or a document
4009 */
4010isXML = Sizzle.isXML = function( elem ) {
4011 // documentElement is verified for cases where it doesn't yet exist
4012 // (such as loading iframes in IE - #4833)
4013 var documentElement = elem && (elem.ownerDocument || elem).documentElement;
4014 return documentElement ? documentElement.nodeName !== "HTML" : false;
4015};
4016
4017/**
4018 * Sets document-related variables once based on the current document
4019 * @param {Element|Object} [doc] An element or document object to use to set the document
4020 * @returns {Object} Returns the current document
4021 */
4022setDocument = Sizzle.setDocument = function( node ) {
4023 var doc = node ? node.ownerDocument || node : preferredDoc;
4024
4025 // If no document and documentElement is available, return
4026 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
4027 return document;
4028 }
4029
4030 // Set our document
4031 document = doc;
4032 docElem = doc.documentElement;
4033
4034 // Support tests
4035 documentIsXML = isXML( doc );
4036
4037 // Check if getElementsByTagName("*") returns only elements
4038 support.tagNameNoComments = assert(function( div ) {
4039 div.appendChild( doc.createComment("") );
4040 return !div.getElementsByTagName("*").length;
4041 });
4042
4043 // Check if attributes should be retrieved by attribute nodes
4044 support.attributes = assert(function( div ) {
4045 div.innerHTML = "<select></select>";
4046 var type = typeof div.lastChild.getAttribute("multiple");
4047 // IE8 returns a string for some attributes even when not present
4048 return type !== "boolean" && type !== "string";
4049 });
4050
4051 // Check if getElementsByClassName can be trusted
4052 support.getByClassName = assert(function( div ) {
4053 // Opera can't find a second classname (in 9.6)
4054 div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
4055 if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
4056 return false;
4057 }
4058
4059 // Safari 3.2 caches class attributes and doesn't catch changes
4060 div.lastChild.className = "e";
4061 return div.getElementsByClassName("e").length === 2;
4062 });
4063
4064 // Check if getElementById returns elements by name
4065 // Check if getElementsByName privileges form controls or returns elements by ID
4066 support.getByName = assert(function( div ) {
4067 // Inject content
4068 div.id = expando + 0;
4069 div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
4070 docElem.insertBefore( div, docElem.firstChild );
4071
4072 // Test
4073 var pass = doc.getElementsByName &&
4074 // buggy browsers will return fewer than the correct 2
4075 doc.getElementsByName( expando ).length === 2 +
4076 // buggy browsers will return more than the correct 0
4077 doc.getElementsByName( expando + 0 ).length;
4078 support.getIdNotName = !doc.getElementById( expando );
4079
4080 // Cleanup
4081 docElem.removeChild( div );
4082
4083 return pass;
4084 });
4085
4086 // IE6/7 return modified attributes
4087 Expr.attrHandle = assert(function( div ) {
4088 div.innerHTML = "<a href='#'></a>";
4089 return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
4090 div.firstChild.getAttribute("href") === "#";
4091 }) ?
4092 {} :
4093 {
4094 "href": function( elem ) {
4095 return elem.getAttribute( "href", 2 );
4096 },
4097 "type": function( elem ) {
4098 return elem.getAttribute("type");
4099 }
4100 };
4101
4102 // ID find and filter
4103 if ( support.getIdNotName ) {
4104 Expr.find["ID"] = function( id, context ) {
4105 if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
4106 var m = context.getElementById( id );
4107 // Check parentNode to catch when Blackberry 4.6 returns
4108 // nodes that are no longer in the document #6963
4109 return m && m.parentNode ? [m] : [];
4110 }
4111 };
4112 Expr.filter["ID"] = function( id ) {
4113 var attrId = id.replace( runescape, funescape );
4114 return function( elem ) {
4115 return elem.getAttribute("id") === attrId;
4116 };
4117 };
4118 } else {
4119 Expr.find["ID"] = function( id, context ) {
4120 if ( typeof context.getElementById !== strundefined && !documentIsXML ) {
4121 var m = context.getElementById( id );
4122
4123 return m ?
4124 m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
4125 [m] :
4126 undefined :
4127 [];
4128 }
4129 };
4130 Expr.filter["ID"] = function( id ) {
4131 var attrId = id.replace( runescape, funescape );
4132 return function( elem ) {
4133 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
4134 return node && node.value === attrId;
4135 };
4136 };
4137 }
4138
4139 // Tag
4140 Expr.find["TAG"] = support.tagNameNoComments ?
4141 function( tag, context ) {
4142 if ( typeof context.getElementsByTagName !== strundefined ) {
4143 return context.getElementsByTagName( tag );
4144 }
4145 } :
4146 function( tag, context ) {
4147 var elem,
4148 tmp = [],
4149 i = 0,
4150 results = context.getElementsByTagName( tag );
4151
4152 // Filter out possible comments
4153 if ( tag === "*" ) {
4154 while ( (elem = results[i++]) ) {
4155 if ( elem.nodeType === 1 ) {
4156 tmp.push( elem );
4157 }
4158 }
4159
4160 return tmp;
4161 }
4162 return results;
4163 };
4164
4165 // Name
4166 Expr.find["NAME"] = support.getByName && function( tag, context ) {
4167 if ( typeof context.getElementsByName !== strundefined ) {
4168 return context.getElementsByName( name );
4169 }
4170 };
4171
4172 // Class
4173 Expr.find["CLASS"] = support.getByClassName && function( className, context ) {
4174 if ( typeof context.getElementsByClassName !== strundefined && !documentIsXML ) {
4175 return context.getElementsByClassName( className );
4176 }
4177 };
4178
4179 // QSA and matchesSelector support
4180
4181 // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
4182 rbuggyMatches = [];
4183
4184 // qSa(:focus) reports false when true (Chrome 21),
4185 // no need to also add to buggyMatches since matches checks buggyQSA
4186 // A support test would require too much code (would include document ready)
4187 rbuggyQSA = [ ":focus" ];
4188
4189 if ( (support.qsa = isNative(doc.querySelectorAll)) ) {
4190 // Build QSA regex
4191 // Regex strategy adopted from Diego Perini
4192 assert(function( div ) {
4193 // Select is set to empty string on purpose
4194 // This is to test IE's treatment of not explictly
4195 // setting a boolean content attribute,
4196 // since its presence should be enough
4197 // http://bugs.jquery.com/ticket/12359
4198 div.innerHTML = "<select><option selected=''></option></select>";
4199
4200 // IE8 - Some boolean attributes are not treated correctly
4201 if ( !div.querySelectorAll("[selected]").length ) {
4202 rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
4203 }
4204
4205 // Webkit/Opera - :checked should return selected option elements
4206 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
4207 // IE8 throws error here and will not see later tests
4208 if ( !div.querySelectorAll(":checked").length ) {
4209 rbuggyQSA.push(":checked");
4210 }
4211 });
4212
4213 assert(function( div ) {
4214
4215 // Opera 10-12/IE8 - ^= $= *= and empty values
4216 // Should not select anything
4217 div.innerHTML = "<input type='hidden' i=''/>";
4218 if ( div.querySelectorAll("[i^='']").length ) {
4219 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
4220 }
4221
4222 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
4223 // IE8 throws error here and will not see later tests
4224 if ( !div.querySelectorAll(":enabled").length ) {
4225 rbuggyQSA.push( ":enabled", ":disabled" );
4226 }
4227
4228 // Opera 10-11 does not throw on post-comma invalid pseudos
4229 div.querySelectorAll("*,:x");
4230 rbuggyQSA.push(",.*:");
4231 });
4232 }
4233
4234 if ( (support.matchesSelector = isNative( (matches = docElem.matchesSelector ||
4235 docElem.mozMatchesSelector ||
4236 docElem.webkitMatchesSelector ||
4237 docElem.oMatchesSelector ||
4238 docElem.msMatchesSelector) )) ) {
4239
4240 assert(function( div ) {
4241 // Check to see if it's possible to do matchesSelector
4242 // on a disconnected node (IE 9)
4243 support.disconnectedMatch = matches.call( div, "div" );
4244
4245 // This should fail with an exception
4246 // Gecko does not error, returns false instead
4247 matches.call( div, "[s!='']:x" );
4248 rbuggyMatches.push( "!=", pseudos );
4249 });
4250 }
4251
4252 rbuggyQSA = new RegExp( rbuggyQSA.join("|") );
4253 rbuggyMatches = new RegExp( rbuggyMatches.join("|") );
4254
4255 // Element contains another
4256 // Purposefully does not implement inclusive descendent
4257 // As in, an element does not contain itself
4258 contains = isNative(docElem.contains) || docElem.compareDocumentPosition ?
4259 function( a, b ) {
4260 var adown = a.nodeType === 9 ? a.documentElement : a,
4261 bup = b && b.parentNode;
4262 return a === bup || !!( bup && bup.nodeType === 1 && (
4263 adown.contains ?
4264 adown.contains( bup ) :
4265 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
4266 ));
4267 } :
4268 function( a, b ) {
4269 if ( b ) {
4270 while ( (b = b.parentNode) ) {
4271 if ( b === a ) {
4272 return true;
4273 }
4274 }
4275 }
4276 return false;
4277 };
4278
4279 // Document order sorting
4280 sortOrder = docElem.compareDocumentPosition ?
4281 function( a, b ) {
4282 var compare;
4283
4284 if ( a === b ) {
4285 hasDuplicate = true;
4286 return 0;
4287 }
4288
4289 if ( (compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b )) ) {
4290 if ( compare & 1 || a.parentNode && a.parentNode.nodeType === 11 ) {
4291 if ( a === doc || contains( preferredDoc, a ) ) {
4292 return -1;
4293 }
4294 if ( b === doc || contains( preferredDoc, b ) ) {
4295 return 1;
4296 }
4297 return 0;
4298 }
4299 return compare & 4 ? -1 : 1;
4300 }
4301
4302 return a.compareDocumentPosition ? -1 : 1;
4303 } :
4304 function( a, b ) {
4305 var cur,
4306 i = 0,
4307 aup = a.parentNode,
4308 bup = b.parentNode,
4309 ap = [ a ],
4310 bp = [ b ];
4311
4312 // Exit early if the nodes are identical
4313 if ( a === b ) {
4314 hasDuplicate = true;
4315 return 0;
4316
4317 // Parentless nodes are either documents or disconnected
4318 } else if ( !aup || !bup ) {
4319 return a === doc ? -1 :
4320 b === doc ? 1 :
4321 aup ? -1 :
4322 bup ? 1 :
4323 0;
4324
4325 // If the nodes are siblings, we can do a quick check
4326 } else if ( aup === bup ) {
4327 return siblingCheck( a, b );
4328 }
4329
4330 // Otherwise we need full lists of their ancestors for comparison
4331 cur = a;
4332 while ( (cur = cur.parentNode) ) {
4333 ap.unshift( cur );
4334 }
4335 cur = b;
4336 while ( (cur = cur.parentNode) ) {
4337 bp.unshift( cur );
4338 }
4339
4340 // Walk down the tree looking for a discrepancy
4341 while ( ap[i] === bp[i] ) {
4342 i++;
4343 }
4344
4345 return i ?
4346 // Do a sibling check if the nodes have a common ancestor
4347 siblingCheck( ap[i], bp[i] ) :
4348
4349 // Otherwise nodes in our document sort first
4350 ap[i] === preferredDoc ? -1 :
4351 bp[i] === preferredDoc ? 1 :
4352 0;
4353 };
4354
4355 // Always assume the presence of duplicates if sort doesn't
4356 // pass them to our comparison function (as in Google Chrome).
4357 hasDuplicate = false;
4358 [0, 0].sort( sortOrder );
4359 support.detectDuplicates = hasDuplicate;
4360
4361 return document;
4362};
4363
4364Sizzle.matches = function( expr, elements ) {
4365 return Sizzle( expr, null, null, elements );
4366};
4367
4368Sizzle.matchesSelector = function( elem, expr ) {
4369 // Set document vars if needed
4370 if ( ( elem.ownerDocument || elem ) !== document ) {
4371 setDocument( elem );
4372 }
4373
4374 // Make sure that attribute selectors are quoted
4375 expr = expr.replace( rattributeQuotes, "='$1']" );
4376
4377 // rbuggyQSA always contains :focus, so no need for an existence check
4378 if ( support.matchesSelector && !documentIsXML && (!rbuggyMatches || !rbuggyMatches.test(expr)) && !rbuggyQSA.test(expr) ) {
4379 try {
4380 var ret = matches.call( elem, expr );
4381
4382 // IE 9's matchesSelector returns false on disconnected nodes
4383 if ( ret || support.disconnectedMatch ||
4384 // As well, disconnected nodes are said to be in a document
4385 // fragment in IE 9
4386 elem.document && elem.document.nodeType !== 11 ) {
4387 return ret;
4388 }
4389 } catch(e) {}
4390 }
4391
4392 return Sizzle( expr, document, null, [elem] ).length > 0;
4393};
4394
4395Sizzle.contains = function( context, elem ) {
4396 // Set document vars if needed
4397 if ( ( context.ownerDocument || context ) !== document ) {
4398 setDocument( context );
4399 }
4400 return contains( context, elem );
4401};
4402
4403Sizzle.attr = function( elem, name ) {
4404 var val;
4405
4406 // Set document vars if needed
4407 if ( ( elem.ownerDocument || elem ) !== document ) {
4408 setDocument( elem );
4409 }
4410
4411 if ( !documentIsXML ) {
4412 name = name.toLowerCase();
4413 }
4414 if ( (val = Expr.attrHandle[ name ]) ) {
4415 return val( elem );
4416 }
4417 if ( documentIsXML || support.attributes ) {
4418 return elem.getAttribute( name );
4419 }
4420 return ( (val = elem.getAttributeNode( name )) || elem.getAttribute( name ) ) && elem[ name ] === true ?
4421 name :
4422 val && val.specified ? val.value : null;
4423};
4424
4425Sizzle.error = function( msg ) {
4426 throw new Error( "Syntax error, unrecognized expression: " + msg );
4427};
4428
4429// Document sorting and removing duplicates
4430Sizzle.uniqueSort = function( results ) {
4431 var elem,
4432 duplicates = [],
4433 i = 1,
4434 j = 0;
4435
4436 // Unless we *know* we can detect duplicates, assume their presence
4437 hasDuplicate = !support.detectDuplicates;
4438 results.sort( sortOrder );
4439
4440 if ( hasDuplicate ) {
4441 for ( ; (elem = results[i]); i++ ) {
4442 if ( elem === results[ i - 1 ] ) {
4443 j = duplicates.push( i );
4444 }
4445 }
4446 while ( j-- ) {
4447 results.splice( duplicates[ j ], 1 );
4448 }
4449 }
4450
4451 return results;
4452};
4453
4454function siblingCheck( a, b ) {
4455 var cur = b && a,
4456 diff = cur && ( ~b.sourceIndex || MAX_NEGATIVE ) - ( ~a.sourceIndex || MAX_NEGATIVE );
4457
4458 // Use IE sourceIndex if available on both nodes
4459 if ( diff ) {
4460 return diff;
4461 }
4462
4463 // Check if b follows a
4464 if ( cur ) {
4465 while ( (cur = cur.nextSibling) ) {
4466 if ( cur === b ) {
4467 return -1;
4468 }
4469 }
4470 }
4471
4472 return a ? 1 : -1;
4473}
4474
4475// Returns a function to use in pseudos for input types
4476function createInputPseudo( type ) {
4477 return function( elem ) {
4478 var name = elem.nodeName.toLowerCase();
4479 return name === "input" && elem.type === type;
4480 };
4481}
4482
4483// Returns a function to use in pseudos for buttons
4484function createButtonPseudo( type ) {
4485 return function( elem ) {
4486 var name = elem.nodeName.toLowerCase();
4487 return (name === "input" || name === "button") && elem.type === type;
4488 };
4489}
4490
4491// Returns a function to use in pseudos for positionals
4492function createPositionalPseudo( fn ) {
4493 return markFunction(function( argument ) {
4494 argument = +argument;
4495 return markFunction(function( seed, matches ) {
4496 var j,
4497 matchIndexes = fn( [], seed.length, argument ),
4498 i = matchIndexes.length;
4499
4500 // Match elements found at the specified indexes
4501 while ( i-- ) {
4502 if ( seed[ (j = matchIndexes[i]) ] ) {
4503 seed[j] = !(matches[j] = seed[j]);
4504 }
4505 }
4506 });
4507 });
4508}
4509
4510/**
4511 * Utility function for retrieving the text value of an array of DOM nodes
4512 * @param {Array|Element} elem
4513 */
4514getText = Sizzle.getText = function( elem ) {
4515 var node,
4516 ret = "",
4517 i = 0,
4518 nodeType = elem.nodeType;
4519
4520 if ( !nodeType ) {
4521 // If no nodeType, this is expected to be an array
4522 for ( ; (node = elem[i]); i++ ) {
4523 // Do not traverse comment nodes
4524 ret += getText( node );
4525 }
4526 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
4527 // Use textContent for elements
4528 // innerText usage removed for consistency of new lines (see #11153)
4529 if ( typeof elem.textContent === "string" ) {
4530 return elem.textContent;
4531 } else {
4532 // Traverse its children
4533 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
4534 ret += getText( elem );
4535 }
4536 }
4537 } else if ( nodeType === 3 || nodeType === 4 ) {
4538 return elem.nodeValue;
4539 }
4540 // Do not include comment or processing instruction nodes
4541
4542 return ret;
4543};
4544
4545Expr = Sizzle.selectors = {
4546
4547 // Can be adjusted by the user
4548 cacheLength: 50,
4549
4550 createPseudo: markFunction,
4551
4552 match: matchExpr,
4553
4554 find: {},
4555
4556 relative: {
4557 ">": { dir: "parentNode", first: true },
4558 " ": { dir: "parentNode" },
4559 "+": { dir: "previousSibling", first: true },
4560 "~": { dir: "previousSibling" }
4561 },
4562
4563 preFilter: {
4564 "ATTR": function( match ) {
4565 match[1] = match[1].replace( runescape, funescape );
4566
4567 // Move the given value to match[3] whether quoted or unquoted
4568 match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape );
4569
4570 if ( match[2] === "~=" ) {
4571 match[3] = " " + match[3] + " ";
4572 }
4573
4574 return match.slice( 0, 4 );
4575 },
4576
4577 "CHILD": function( match ) {
4578 /* matches from matchExpr["CHILD"]
4579 1 type (only|nth|...)
4580 2 what (child|of-type)
4581 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4582 4 xn-component of xn+y argument ([+-]?\d*n|)
4583 5 sign of xn-component
4584 6 x of xn-component
4585 7 sign of y-component
4586 8 y of y-component
4587 */
4588 match[1] = match[1].toLowerCase();
4589
4590 if ( match[1].slice( 0, 3 ) === "nth" ) {
4591 // nth-* requires argument
4592 if ( !match[3] ) {
4593 Sizzle.error( match[0] );
4594 }
4595
4596 // numeric x and y parameters for Expr.filter.CHILD
4597 // remember that false/true cast respectively to 0/1
4598 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
4599 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
4600
4601 // other types prohibit arguments
4602 } else if ( match[3] ) {
4603 Sizzle.error( match[0] );
4604 }
4605
4606 return match;
4607 },
4608
4609 "PSEUDO": function( match ) {
4610 var excess,
4611 unquoted = !match[5] && match[2];
4612
4613 if ( matchExpr["CHILD"].test( match[0] ) ) {
4614 return null;
4615 }
4616
4617 // Accept quoted arguments as-is
4618 if ( match[4] ) {
4619 match[2] = match[4];
4620
4621 // Strip excess characters from unquoted arguments
4622 } else if ( unquoted && rpseudo.test( unquoted ) &&
4623 // Get excess from tokenize (recursively)
4624 (excess = tokenize( unquoted, true )) &&
4625 // advance to the next closing parenthesis
4626 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
4627
4628 // excess is a negative index
4629 match[0] = match[0].slice( 0, excess );
4630 match[2] = unquoted.slice( 0, excess );
4631 }
4632
4633 // Return only captures needed by the pseudo filter method (type and argument)
4634 return match.slice( 0, 3 );
4635 }
4636 },
4637
4638 filter: {
4639
4640 "TAG": function( nodeName ) {
4641 if ( nodeName === "*" ) {
4642 return function() { return true; };
4643 }
4644
4645 nodeName = nodeName.replace( runescape, funescape ).toLowerCase();
4646 return function( elem ) {
4647 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
4648 };
4649 },
4650
4651 "CLASS": function( className ) {
4652 var pattern = classCache[ className + " " ];
4653
4654 return pattern ||
4655 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
4656 classCache( className, function( elem ) {
4657 return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
4658 });
4659 },
4660
4661 "ATTR": function( name, operator, check ) {
4662 return function( elem ) {
4663 var result = Sizzle.attr( elem, name );
4664
4665 if ( result == null ) {
4666 return operator === "!=";
4667 }
4668 if ( !operator ) {
4669 return true;
4670 }
4671
4672 result += "";
4673
4674 return operator === "=" ? result === check :
4675 operator === "!=" ? result !== check :
4676 operator === "^=" ? check && result.indexOf( check ) === 0 :
4677 operator === "*=" ? check && result.indexOf( check ) > -1 :
4678 operator === "$=" ? check && result.slice( -check.length ) === check :
4679 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
4680 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
4681 false;
4682 };
4683 },
4684
4685 "CHILD": function( type, what, argument, first, last ) {
4686 var simple = type.slice( 0, 3 ) !== "nth",
4687 forward = type.slice( -4 ) !== "last",
4688 ofType = what === "of-type";
4689
4690 return first === 1 && last === 0 ?
4691
4692 // Shortcut for :nth-*(n)
4693 function( elem ) {
4694 return !!elem.parentNode;
4695 } :
4696
4697 function( elem, context, xml ) {
4698 var cache, outerCache, node, diff, nodeIndex, start,
4699 dir = simple !== forward ? "nextSibling" : "previousSibling",
4700 parent = elem.parentNode,
4701 name = ofType && elem.nodeName.toLowerCase(),
4702 useCache = !xml && !ofType;
4703
4704 if ( parent ) {
4705
4706 // :(first|last|only)-(child|of-type)
4707 if ( simple ) {
4708 while ( dir ) {
4709 node = elem;
4710 while ( (node = node[ dir ]) ) {
4711 if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
4712 return false;
4713 }
4714 }
4715 // Reverse direction for :only-* (if we haven't yet done so)
4716 start = dir = type === "only" && !start && "nextSibling";
4717 }
4718 return true;
4719 }
4720
4721 start = [ forward ? parent.firstChild : parent.lastChild ];
4722
4723 // non-xml :nth-child(...) stores cache data on `parent`
4724 if ( forward && useCache ) {
4725 // Seek `elem` from a previously-cached index
4726 outerCache = parent[ expando ] || (parent[ expando ] = {});
4727 cache = outerCache[ type ] || [];
4728 nodeIndex = cache[0] === dirruns && cache[1];
4729 diff = cache[0] === dirruns && cache[2];
4730 node = nodeIndex && parent.childNodes[ nodeIndex ];
4731
4732 while ( (node = ++nodeIndex && node && node[ dir ] ||
4733
4734 // Fallback to seeking `elem` from the start
4735 (diff = nodeIndex = 0) || start.pop()) ) {
4736
4737 // When found, cache indexes on `parent` and break
4738 if ( node.nodeType === 1 && ++diff && node === elem ) {
4739 outerCache[ type ] = [ dirruns, nodeIndex, diff ];
4740 break;
4741 }
4742 }
4743
4744 // Use previously-cached element index if available
4745 } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
4746 diff = cache[1];
4747
4748 // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
4749 } else {
4750 // Use the same loop as above to seek `elem` from the start
4751 while ( (node = ++nodeIndex && node && node[ dir ] ||
4752 (diff = nodeIndex = 0) || start.pop()) ) {
4753
4754 if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
4755 // Cache the index of each encountered element
4756 if ( useCache ) {
4757 (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
4758 }
4759
4760 if ( node === elem ) {
4761 break;
4762 }
4763 }
4764 }
4765 }
4766
4767 // Incorporate the offset, then check against cycle size
4768 diff -= last;
4769 return diff === first || ( diff % first === 0 && diff / first >= 0 );
4770 }
4771 };
4772 },
4773
4774 "PSEUDO": function( pseudo, argument ) {
4775 // pseudo-class names are case-insensitive
4776 // http://www.w3.org/TR/selectors/#pseudo-classes
4777 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
4778 // Remember that setFilters inherits from pseudos
4779 var args,
4780 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
4781 Sizzle.error( "unsupported pseudo: " + pseudo );
4782
4783 // The user may use createPseudo to indicate that
4784 // arguments are needed to create the filter function
4785 // just as Sizzle does
4786 if ( fn[ expando ] ) {
4787 return fn( argument );
4788 }
4789
4790 // But maintain support for old signatures
4791 if ( fn.length > 1 ) {
4792 args = [ pseudo, pseudo, "", argument ];
4793 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
4794 markFunction(function( seed, matches ) {
4795 var idx,
4796 matched = fn( seed, argument ),
4797 i = matched.length;
4798 while ( i-- ) {
4799 idx = indexOf.call( seed, matched[i] );
4800 seed[ idx ] = !( matches[ idx ] = matched[i] );
4801 }
4802 }) :
4803 function( elem ) {
4804 return fn( elem, 0, args );
4805 };
4806 }
4807
4808 return fn;
4809 }
4810 },
4811
4812 pseudos: {
4813 // Potentially complex pseudos
4814 "not": markFunction(function( selector ) {
4815 // Trim the selector passed to compile
4816 // to avoid treating leading and trailing
4817 // spaces as combinators
4818 var input = [],
4819 results = [],
4820 matcher = compile( selector.replace( rtrim, "$1" ) );
4821
4822 return matcher[ expando ] ?
4823 markFunction(function( seed, matches, context, xml ) {
4824 var elem,
4825 unmatched = matcher( seed, null, xml, [] ),
4826 i = seed.length;
4827
4828 // Match elements unmatched by `matcher`
4829 while ( i-- ) {
4830 if ( (elem = unmatched[i]) ) {
4831 seed[i] = !(matches[i] = elem);
4832 }
4833 }
4834 }) :
4835 function( elem, context, xml ) {
4836 input[0] = elem;
4837 matcher( input, null, xml, results );
4838 return !results.pop();
4839 };
4840 }),
4841
4842 "has": markFunction(function( selector ) {
4843 return function( elem ) {
4844 return Sizzle( selector, elem ).length > 0;
4845 };
4846 }),
4847
4848 "contains": markFunction(function( text ) {
4849 return function( elem ) {
4850 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
4851 };
4852 }),
4853
4854 // "Whether an element is represented by a :lang() selector
4855 // is based solely on the element's language value
4856 // being equal to the identifier C,
4857 // or beginning with the identifier C immediately followed by "-".
4858 // The matching of C against the element's language value is performed case-insensitively.
4859 // The identifier C does not have to be a valid language name."
4860 // http://www.w3.org/TR/selectors/#lang-pseudo
4861 "lang": markFunction( function( lang ) {
4862 // lang value must be a valid identifider
4863 if ( !ridentifier.test(lang || "") ) {
4864 Sizzle.error( "unsupported lang: " + lang );
4865 }
4866 lang = lang.replace( runescape, funescape ).toLowerCase();
4867 return function( elem ) {
4868 var elemLang;
4869 do {
4870 if ( (elemLang = documentIsXML ?
4871 elem.getAttribute("xml:lang") || elem.getAttribute("lang") :
4872 elem.lang) ) {
4873
4874 elemLang = elemLang.toLowerCase();
4875 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
4876 }
4877 } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
4878 return false;
4879 };
4880 }),
4881
4882 // Miscellaneous
4883 "target": function( elem ) {
4884 var hash = window.location && window.location.hash;
4885 return hash && hash.slice( 1 ) === elem.id;
4886 },
4887
4888 "root": function( elem ) {
4889 return elem === docElem;
4890 },
4891
4892 "focus": function( elem ) {
4893 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
4894 },
4895
4896 // Boolean properties
4897 "enabled": function( elem ) {
4898 return elem.disabled === false;
4899 },
4900
4901 "disabled": function( elem ) {
4902 return elem.disabled === true;
4903 },
4904
4905 "checked": function( elem ) {
4906 // In CSS3, :checked should return both checked and selected elements
4907 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
4908 var nodeName = elem.nodeName.toLowerCase();
4909 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
4910 },
4911
4912 "selected": function( elem ) {
4913 // Accessing this property makes selected-by-default
4914 // options in Safari work properly
4915 if ( elem.parentNode ) {
4916 elem.parentNode.selectedIndex;
4917 }
4918
4919 return elem.selected === true;
4920 },
4921
4922 // Contents
4923 "empty": function( elem ) {
4924 // http://www.w3.org/TR/selectors/#empty-pseudo
4925 // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
4926 // not comment, processing instructions, or others
4927 // Thanks to Diego Perini for the nodeName shortcut
4928 // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
4929 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
4930 if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) {
4931 return false;
4932 }
4933 }
4934 return true;
4935 },
4936
4937 "parent": function( elem ) {
4938 return !Expr.pseudos["empty"]( elem );
4939 },
4940
4941 // Element/input types
4942 "header": function( elem ) {
4943 return rheader.test( elem.nodeName );
4944 },
4945
4946 "input": function( elem ) {
4947 return rinputs.test( elem.nodeName );
4948 },
4949
4950 "button": function( elem ) {
4951 var name = elem.nodeName.toLowerCase();
4952 return name === "input" && elem.type === "button" || name === "button";
4953 },
4954
4955 "text": function( elem ) {
4956 var attr;
4957 // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
4958 // use getAttribute instead to test this case
4959 return elem.nodeName.toLowerCase() === "input" &&
4960 elem.type === "text" &&
4961 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type );
4962 },
4963
4964 // Position-in-collection
4965 "first": createPositionalPseudo(function() {
4966 return [ 0 ];
4967 }),
4968
4969 "last": createPositionalPseudo(function( matchIndexes, length ) {
4970 return [ length - 1 ];
4971 }),
4972
4973 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
4974 return [ argument < 0 ? argument + length : argument ];
4975 }),
4976
4977 "even": createPositionalPseudo(function( matchIndexes, length ) {
4978 var i = 0;
4979 for ( ; i < length; i += 2 ) {
4980 matchIndexes.push( i );
4981 }
4982 return matchIndexes;
4983 }),
4984
4985 "odd": createPositionalPseudo(function( matchIndexes, length ) {
4986 var i = 1;
4987 for ( ; i < length; i += 2 ) {
4988 matchIndexes.push( i );
4989 }
4990 return matchIndexes;
4991 }),
4992
4993 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
4994 var i = argument < 0 ? argument + length : argument;
4995 for ( ; --i >= 0; ) {
4996 matchIndexes.push( i );
4997 }
4998 return matchIndexes;
4999 }),
5000
5001 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5002 var i = argument < 0 ? argument + length : argument;
5003 for ( ; ++i < length; ) {
5004 matchIndexes.push( i );
5005 }
5006 return matchIndexes;
5007 })
5008 }
5009};
5010
5011// Add button/input type pseudos
5012for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
5013 Expr.pseudos[ i ] = createInputPseudo( i );
5014}
5015for ( i in { submit: true, reset: true } ) {
5016 Expr.pseudos[ i ] = createButtonPseudo( i );
5017}
5018
5019function tokenize( selector, parseOnly ) {
5020 var matched, match, tokens, type,
5021 soFar, groups, preFilters,
5022 cached = tokenCache[ selector + " " ];
5023
5024 if ( cached ) {
5025 return parseOnly ? 0 : cached.slice( 0 );
5026 }
5027
5028 soFar = selector;
5029 groups = [];
5030 preFilters = Expr.preFilter;
5031
5032 while ( soFar ) {
5033
5034 // Comma and first run
5035 if ( !matched || (match = rcomma.exec( soFar )) ) {
5036 if ( match ) {
5037 // Don't consume trailing commas as valid
5038 soFar = soFar.slice( match[0].length ) || soFar;
5039 }
5040 groups.push( tokens = [] );
5041 }
5042
5043 matched = false;
5044
5045 // Combinators
5046 if ( (match = rcombinators.exec( soFar )) ) {
5047 matched = match.shift();
5048 tokens.push( {
5049 value: matched,
5050 // Cast descendant combinators to space
5051 type: match[0].replace( rtrim, " " )
5052 } );
5053 soFar = soFar.slice( matched.length );
5054 }
5055
5056 // Filters
5057 for ( type in Expr.filter ) {
5058 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
5059 (match = preFilters[ type ]( match ))) ) {
5060 matched = match.shift();
5061 tokens.push( {
5062 value: matched,
5063 type: type,
5064 matches: match
5065 } );
5066 soFar = soFar.slice( matched.length );
5067 }
5068 }
5069
5070 if ( !matched ) {
5071 break;
5072 }
5073 }
5074
5075 // Return the length of the invalid excess
5076 // if we're just parsing
5077 // Otherwise, throw an error or return tokens
5078 return parseOnly ?
5079 soFar.length :
5080 soFar ?
5081 Sizzle.error( selector ) :
5082 // Cache the tokens
5083 tokenCache( selector, groups ).slice( 0 );
5084}
5085
5086function toSelector( tokens ) {
5087 var i = 0,
5088 len = tokens.length,
5089 selector = "";
5090 for ( ; i < len; i++ ) {
5091 selector += tokens[i].value;
5092 }
5093 return selector;
5094}
5095
5096function addCombinator( matcher, combinator, base ) {
5097 var dir = combinator.dir,
5098 checkNonElements = base && dir === "parentNode",
5099 doneName = done++;
5100
5101 return combinator.first ?
5102 // Check against closest ancestor/preceding element
5103 function( elem, context, xml ) {
5104 while ( (elem = elem[ dir ]) ) {
5105 if ( elem.nodeType === 1 || checkNonElements ) {
5106 return matcher( elem, context, xml );
5107 }
5108 }
5109 } :
5110
5111 // Check against all ancestor/preceding elements
5112 function( elem, context, xml ) {
5113 var data, cache, outerCache,
5114 dirkey = dirruns + " " + doneName;
5115
5116 // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
5117 if ( xml ) {
5118 while ( (elem = elem[ dir ]) ) {
5119 if ( elem.nodeType === 1 || checkNonElements ) {
5120 if ( matcher( elem, context, xml ) ) {
5121 return true;
5122 }
5123 }
5124 }
5125 } else {
5126 while ( (elem = elem[ dir ]) ) {
5127 if ( elem.nodeType === 1 || checkNonElements ) {
5128 outerCache = elem[ expando ] || (elem[ expando ] = {});
5129 if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) {
5130 if ( (data = cache[1]) === true || data === cachedruns ) {
5131 return data === true;
5132 }
5133 } else {
5134 cache = outerCache[ dir ] = [ dirkey ];
5135 cache[1] = matcher( elem, context, xml ) || cachedruns;
5136 if ( cache[1] === true ) {
5137 return true;
5138 }
5139 }
5140 }
5141 }
5142 }
5143 };
5144}
5145
5146function elementMatcher( matchers ) {
5147 return matchers.length > 1 ?
5148 function( elem, context, xml ) {
5149 var i = matchers.length;
5150 while ( i-- ) {
5151 if ( !matchers[i]( elem, context, xml ) ) {
5152 return false;
5153 }
5154 }
5155 return true;
5156 } :
5157 matchers[0];
5158}
5159
5160function condense( unmatched, map, filter, context, xml ) {
5161 var elem,
5162 newUnmatched = [],
5163 i = 0,
5164 len = unmatched.length,
5165 mapped = map != null;
5166
5167 for ( ; i < len; i++ ) {
5168 if ( (elem = unmatched[i]) ) {
5169 if ( !filter || filter( elem, context, xml ) ) {
5170 newUnmatched.push( elem );
5171 if ( mapped ) {
5172 map.push( i );
5173 }
5174 }
5175 }
5176 }
5177
5178 return newUnmatched;
5179}
5180
5181function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
5182 if ( postFilter && !postFilter[ expando ] ) {
5183 postFilter = setMatcher( postFilter );
5184 }
5185 if ( postFinder && !postFinder[ expando ] ) {
5186 postFinder = setMatcher( postFinder, postSelector );
5187 }
5188 return markFunction(function( seed, results, context, xml ) {
5189 var temp, i, elem,
5190 preMap = [],
5191 postMap = [],
5192 preexisting = results.length,
5193
5194 // Get initial elements from seed or context
5195 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
5196
5197 // Prefilter to get matcher input, preserving a map for seed-results synchronization
5198 matcherIn = preFilter && ( seed || !selector ) ?
5199 condense( elems, preMap, preFilter, context, xml ) :
5200 elems,
5201
5202 matcherOut = matcher ?
5203 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
5204 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
5205
5206 // ...intermediate processing is necessary
5207 [] :
5208
5209 // ...otherwise use results directly
5210 results :
5211 matcherIn;
5212
5213 // Find primary matches
5214 if ( matcher ) {
5215 matcher( matcherIn, matcherOut, context, xml );
5216 }
5217
5218 // Apply postFilter
5219 if ( postFilter ) {
5220 temp = condense( matcherOut, postMap );
5221 postFilter( temp, [], context, xml );
5222
5223 // Un-match failing elements by moving them back to matcherIn
5224 i = temp.length;
5225 while ( i-- ) {
5226 if ( (elem = temp[i]) ) {
5227 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
5228 }
5229 }
5230 }
5231
5232 if ( seed ) {
5233 if ( postFinder || preFilter ) {
5234 if ( postFinder ) {
5235 // Get the final matcherOut by condensing this intermediate into postFinder contexts
5236 temp = [];
5237 i = matcherOut.length;
5238 while ( i-- ) {
5239 if ( (elem = matcherOut[i]) ) {
5240 // Restore matcherIn since elem is not yet a final match
5241 temp.push( (matcherIn[i] = elem) );
5242 }
5243 }
5244 postFinder( null, (matcherOut = []), temp, xml );
5245 }
5246
5247 // Move matched elements from seed to results to keep them synchronized
5248 i = matcherOut.length;
5249 while ( i-- ) {
5250 if ( (elem = matcherOut[i]) &&
5251 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
5252
5253 seed[temp] = !(results[temp] = elem);
5254 }
5255 }
5256 }
5257
5258 // Add elements to results, through postFinder if defined
5259 } else {
5260 matcherOut = condense(
5261 matcherOut === results ?
5262 matcherOut.splice( preexisting, matcherOut.length ) :
5263 matcherOut
5264 );
5265 if ( postFinder ) {
5266 postFinder( null, results, matcherOut, xml );
5267 } else {
5268 push.apply( results, matcherOut );
5269 }
5270 }
5271 });
5272}
5273
5274function matcherFromTokens( tokens ) {
5275 var checkContext, matcher, j,
5276 len = tokens.length,
5277 leadingRelative = Expr.relative[ tokens[0].type ],
5278 implicitRelative = leadingRelative || Expr.relative[" "],
5279 i = leadingRelative ? 1 : 0,
5280
5281 // The foundational matcher ensures that elements are reachable from top-level context(s)
5282 matchContext = addCombinator( function( elem ) {
5283 return elem === checkContext;
5284 }, implicitRelative, true ),
5285 matchAnyContext = addCombinator( function( elem ) {
5286 return indexOf.call( checkContext, elem ) > -1;
5287 }, implicitRelative, true ),
5288 matchers = [ function( elem, context, xml ) {
5289 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
5290 (checkContext = context).nodeType ?
5291 matchContext( elem, context, xml ) :
5292 matchAnyContext( elem, context, xml ) );
5293 } ];
5294
5295 for ( ; i < len; i++ ) {
5296 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
5297 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
5298 } else {
5299 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
5300
5301 // Return special upon seeing a positional matcher
5302 if ( matcher[ expando ] ) {
5303 // Find the next relative operator (if any) for proper handling
5304 j = ++i;
5305 for ( ; j < len; j++ ) {
5306 if ( Expr.relative[ tokens[j].type ] ) {
5307 break;
5308 }
5309 }
5310 return setMatcher(
5311 i > 1 && elementMatcher( matchers ),
5312 i > 1 && toSelector( tokens.slice( 0, i - 1 ) ).replace( rtrim, "$1" ),
5313 matcher,
5314 i < j && matcherFromTokens( tokens.slice( i, j ) ),
5315 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
5316 j < len && toSelector( tokens )
5317 );
5318 }
5319 matchers.push( matcher );
5320 }
5321 }
5322
5323 return elementMatcher( matchers );
5324}
5325
5326function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
5327 // A counter to specify which element is currently being matched
5328 var matcherCachedRuns = 0,
5329 bySet = setMatchers.length > 0,
5330 byElement = elementMatchers.length > 0,
5331 superMatcher = function( seed, context, xml, results, expandContext ) {
5332 var elem, j, matcher,
5333 setMatched = [],
5334 matchedCount = 0,
5335 i = "0",
5336 unmatched = seed && [],
5337 outermost = expandContext != null,
5338 contextBackup = outermostContext,
5339 // We must always have either seed elements or context
5340 elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
5341 // Use integer dirruns iff this is the outermost matcher
5342 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1);
5343
5344 if ( outermost ) {
5345 outermostContext = context !== document && context;
5346 cachedruns = matcherCachedRuns;
5347 }
5348
5349 // Add elements passing elementMatchers directly to results
5350 // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
5351 for ( ; (elem = elems[i]) != null; i++ ) {
5352 if ( byElement && elem ) {
5353 j = 0;
5354 while ( (matcher = elementMatchers[j++]) ) {
5355 if ( matcher( elem, context, xml ) ) {
5356 results.push( elem );
5357 break;
5358 }
5359 }
5360 if ( outermost ) {
5361 dirruns = dirrunsUnique;
5362 cachedruns = ++matcherCachedRuns;
5363 }
5364 }
5365
5366 // Track unmatched elements for set filters
5367 if ( bySet ) {
5368 // They will have gone through all possible matchers
5369 if ( (elem = !matcher && elem) ) {
5370 matchedCount--;
5371 }
5372
5373 // Lengthen the array for every element, matched or not
5374 if ( seed ) {
5375 unmatched.push( elem );
5376 }
5377 }
5378 }
5379
5380 // Apply set filters to unmatched elements
5381 matchedCount += i;
5382 if ( bySet && i !== matchedCount ) {
5383 j = 0;
5384 while ( (matcher = setMatchers[j++]) ) {
5385 matcher( unmatched, setMatched, context, xml );
5386 }
5387
5388 if ( seed ) {
5389 // Reintegrate element matches to eliminate the need for sorting
5390 if ( matchedCount > 0 ) {
5391 while ( i-- ) {
5392 if ( !(unmatched[i] || setMatched[i]) ) {
5393 setMatched[i] = pop.call( results );
5394 }
5395 }
5396 }
5397
5398 // Discard index placeholder values to get only actual matches
5399 setMatched = condense( setMatched );
5400 }
5401
5402 // Add matches to results
5403 push.apply( results, setMatched );
5404
5405 // Seedless set matches succeeding multiple successful matchers stipulate sorting
5406 if ( outermost && !seed && setMatched.length > 0 &&
5407 ( matchedCount + setMatchers.length ) > 1 ) {
5408
5409 Sizzle.uniqueSort( results );
5410 }
5411 }
5412
5413 // Override manipulation of globals by nested matchers
5414 if ( outermost ) {
5415 dirruns = dirrunsUnique;
5416 outermostContext = contextBackup;
5417 }
5418
5419 return unmatched;
5420 };
5421
5422 return bySet ?
5423 markFunction( superMatcher ) :
5424 superMatcher;
5425}
5426
5427compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
5428 var i,
5429 setMatchers = [],
5430 elementMatchers = [],
5431 cached = compilerCache[ selector + " " ];
5432
5433 if ( !cached ) {
5434 // Generate a function of recursive functions that can be used to check each element
5435 if ( !group ) {
5436 group = tokenize( selector );
5437 }
5438 i = group.length;
5439 while ( i-- ) {
5440 cached = matcherFromTokens( group[i] );
5441 if ( cached[ expando ] ) {
5442 setMatchers.push( cached );
5443 } else {
5444 elementMatchers.push( cached );
5445 }
5446 }
5447
5448 // Cache the compiled function
5449 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
5450 }
5451 return cached;
5452};
5453
5454function multipleContexts( selector, contexts, results ) {
5455 var i = 0,
5456 len = contexts.length;
5457 for ( ; i < len; i++ ) {
5458 Sizzle( selector, contexts[i], results );
5459 }
5460 return results;
5461}
5462
5463function select( selector, context, results, seed ) {
5464 var i, tokens, token, type, find,
5465 match = tokenize( selector );
5466
5467 if ( !seed ) {
5468 // Try to minimize operations if there is only one group
5469 if ( match.length === 1 ) {
5470
5471 // Take a shortcut and set the context if the root selector is an ID
5472 tokens = match[0] = match[0].slice( 0 );
5473 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
5474 context.nodeType === 9 && !documentIsXML &&
5475 Expr.relative[ tokens[1].type ] ) {
5476
5477 context = Expr.find["ID"]( token.matches[0].replace( runescape, funescape ), context )[0];
5478 if ( !context ) {
5479 return results;
5480 }
5481
5482 selector = selector.slice( tokens.shift().value.length );
5483 }
5484
5485 // Fetch a seed set for right-to-left matching
5486 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
5487 while ( i-- ) {
5488 token = tokens[i];
5489
5490 // Abort if we hit a combinator
5491 if ( Expr.relative[ (type = token.type) ] ) {
5492 break;
5493 }
5494 if ( (find = Expr.find[ type ]) ) {
5495 // Search, expanding context for leading sibling combinators
5496 if ( (seed = find(
5497 token.matches[0].replace( runescape, funescape ),
5498 rsibling.test( tokens[0].type ) && context.parentNode || context
5499 )) ) {
5500
5501 // If seed is empty or no tokens remain, we can return early
5502 tokens.splice( i, 1 );
5503 selector = seed.length && toSelector( tokens );
5504 if ( !selector ) {
5505 push.apply( results, slice.call( seed, 0 ) );
5506 return results;
5507 }
5508
5509 break;
5510 }
5511 }
5512 }
5513 }
5514 }
5515
5516 // Compile and execute a filtering function
5517 // Provide `match` to avoid retokenization if we modified the selector above
5518 compile( selector, match )(
5519 seed,
5520 context,
5521 documentIsXML,
5522 results,
5523 rsibling.test( selector )
5524 );
5525 return results;
5526}
5527
5528// Deprecated
5529Expr.pseudos["nth"] = Expr.pseudos["eq"];
5530
5531// Easy API for creating new setFilters
5532function setFilters() {}
5533Expr.filters = setFilters.prototype = Expr.pseudos;
5534Expr.setFilters = new setFilters();
5535
5536// Initialize with the default document
5537setDocument();
5538
5539// Override sizzle attribute retrieval
5540Sizzle.attr = jQuery.attr;
5541jQuery.find = Sizzle;
5542jQuery.expr = Sizzle.selectors;
5543jQuery.expr[":"] = jQuery.expr.pseudos;
5544jQuery.unique = Sizzle.uniqueSort;
5545jQuery.text = Sizzle.getText;
5546jQuery.isXMLDoc = Sizzle.isXML;
5547jQuery.contains = Sizzle.contains;
5548
5549
5550})( window );
5551var runtil = /Until$/,
5552 rparentsprev = /^(?:parents|prev(?:Until|All))/,
5553 isSimple = /^.[^:#\[\.,]*$/,
5554 rneedsContext = jQuery.expr.match.needsContext,
5555 // methods guaranteed to produce a unique set when starting from a unique set
5556 guaranteedUnique = {
5557 children: true,
5558 contents: true,
5559 next: true,
5560 prev: true
5561 };
5562
5563jQuery.fn.extend({
5564 find: function( selector ) {
5565 var i, ret, self,
5566 len = this.length;
5567
5568 if ( typeof selector !== "string" ) {
5569 self = this;
5570 return this.pushStack( jQuery( selector ).filter(function() {
5571 for ( i = 0; i < len; i++ ) {
5572 if ( jQuery.contains( self[ i ], this ) ) {
5573 return true;
5574 }
5575 }
5576 }) );
5577 }
5578
5579 ret = [];
5580 for ( i = 0; i < len; i++ ) {
5581 jQuery.find( selector, this[ i ], ret );
5582 }
5583
5584 // Needed because $( selector, context ) becomes $( context ).find( selector )
5585 ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
5586 ret.selector = ( this.selector ? this.selector + " " : "" ) + selector;
5587 return ret;
5588 },
5589
5590 has: function( target ) {
5591 var i,
5592 targets = jQuery( target, this ),
5593 len = targets.length;
5594
5595 return this.filter(function() {
5596 for ( i = 0; i < len; i++ ) {
5597 if ( jQuery.contains( this, targets[i] ) ) {
5598 return true;
5599 }
5600 }
5601 });
5602 },
5603
5604 not: function( selector ) {
5605 return this.pushStack( winnow(this, selector, false) );
5606 },
5607
5608 filter: function( selector ) {
5609 return this.pushStack( winnow(this, selector, true) );
5610 },
5611
5612 is: function( selector ) {
5613 return !!selector && (
5614 typeof selector === "string" ?
5615 // If this is a positional/relative selector, check membership in the returned set
5616 // so $("p:first").is("p:last") won't return true for a doc with two "p".
5617 rneedsContext.test( selector ) ?
5618 jQuery( selector, this.context ).index( this[0] ) >= 0 :
5619 jQuery.filter( selector, this ).length > 0 :
5620 this.filter( selector ).length > 0 );
5621 },
5622
5623 closest: function( selectors, context ) {
5624 var cur,
5625 i = 0,
5626 l = this.length,
5627 ret = [],
5628 pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
5629 jQuery( selectors, context || this.context ) :
5630 0;
5631
5632 for ( ; i < l; i++ ) {
5633 cur = this[i];
5634
5635 while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
5636 if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
5637 ret.push( cur );
5638 break;
5639 }
5640 cur = cur.parentNode;
5641 }
5642 }
5643
5644 return this.pushStack( ret.length > 1 ? jQuery.unique( ret ) : ret );
5645 },
5646
5647 // Determine the position of an element within
5648 // the matched set of elements
5649 index: function( elem ) {
5650
5651 // No argument, return index in parent
5652 if ( !elem ) {
5653 return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
5654 }
5655
5656 // index in selector
5657 if ( typeof elem === "string" ) {
5658 return jQuery.inArray( this[0], jQuery( elem ) );
5659 }
5660
5661 // Locate the position of the desired element
5662 return jQuery.inArray(
5663 // If it receives a jQuery object, the first element is used
5664 elem.jquery ? elem[0] : elem, this );
5665 },
5666
5667 add: function( selector, context ) {
5668 var set = typeof selector === "string" ?
5669 jQuery( selector, context ) :
5670 jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
5671 all = jQuery.merge( this.get(), set );
5672
5673 return this.pushStack( jQuery.unique(all) );
5674 },
5675
5676 addBack: function( selector ) {
5677 return this.add( selector == null ?
5678 this.prevObject : this.prevObject.filter(selector)
5679 );
5680 }
5681});
5682
5683jQuery.fn.andSelf = jQuery.fn.addBack;
5684
5685function sibling( cur, dir ) {
5686 do {
5687 cur = cur[ dir ];
5688 } while ( cur && cur.nodeType !== 1 );
5689
5690 return cur;
5691}
5692
5693jQuery.each({
5694 parent: function( elem ) {
5695 var parent = elem.parentNode;
5696 return parent && parent.nodeType !== 11 ? parent : null;
5697 },
5698 parents: function( elem ) {
5699 return jQuery.dir( elem, "parentNode" );
5700 },
5701 parentsUntil: function( elem, i, until ) {
5702 return jQuery.dir( elem, "parentNode", until );
5703 },
5704 next: function( elem ) {
5705 return sibling( elem, "nextSibling" );
5706 },
5707 prev: function( elem ) {
5708 return sibling( elem, "previousSibling" );
5709 },
5710 nextAll: function( elem ) {
5711 return jQuery.dir( elem, "nextSibling" );
5712 },
5713 prevAll: function( elem ) {
5714 return jQuery.dir( elem, "previousSibling" );
5715 },
5716 nextUntil: function( elem, i, until ) {
5717 return jQuery.dir( elem, "nextSibling", until );
5718 },
5719 prevUntil: function( elem, i, until ) {
5720 return jQuery.dir( elem, "previousSibling", until );
5721 },
5722 siblings: function( elem ) {
5723 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
5724 },
5725 children: function( elem ) {
5726 return jQuery.sibling( elem.firstChild );
5727 },
5728 contents: function( elem ) {
5729 return jQuery.nodeName( elem, "iframe" ) ?
5730 elem.contentDocument || elem.contentWindow.document :
5731 jQuery.merge( [], elem.childNodes );
5732 }
5733}, function( name, fn ) {
5734 jQuery.fn[ name ] = function( until, selector ) {
5735 var ret = jQuery.map( this, fn, until );
5736
5737 if ( !runtil.test( name ) ) {
5738 selector = until;
5739 }
5740
5741 if ( selector && typeof selector === "string" ) {
5742 ret = jQuery.filter( selector, ret );
5743 }
5744
5745 ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
5746
5747 if ( this.length > 1 && rparentsprev.test( name ) ) {
5748 ret = ret.reverse();
5749 }
5750
5751 return this.pushStack( ret );
5752 };
5753});
5754
5755jQuery.extend({
5756 filter: function( expr, elems, not ) {
5757 if ( not ) {
5758 expr = ":not(" + expr + ")";
5759 }
5760
5761 return elems.length === 1 ?
5762 jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
5763 jQuery.find.matches(expr, elems);
5764 },
5765
5766 dir: function( elem, dir, until ) {
5767 var matched = [],
5768 cur = elem[ dir ];
5769
5770 while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
5771 if ( cur.nodeType === 1 ) {
5772 matched.push( cur );
5773 }
5774 cur = cur[dir];
5775 }
5776 return matched;
5777 },
5778
5779 sibling: function( n, elem ) {
5780 var r = [];
5781
5782 for ( ; n; n = n.nextSibling ) {
5783 if ( n.nodeType === 1 && n !== elem ) {
5784 r.push( n );
5785 }
5786 }
5787
5788 return r;
5789 }
5790});
5791
5792// Implement the identical functionality for filter and not
5793function winnow( elements, qualifier, keep ) {
5794
5795 // Can't pass null or undefined to indexOf in Firefox 4
5796 // Set to 0 to skip string check
5797 qualifier = qualifier || 0;
5798
5799 if ( jQuery.isFunction( qualifier ) ) {
5800 return jQuery.grep(elements, function( elem, i ) {
5801 var retVal = !!qualifier.call( elem, i, elem );
5802 return retVal === keep;
5803 });
5804
5805 } else if ( qualifier.nodeType ) {
5806 return jQuery.grep(elements, function( elem ) {
5807 return ( elem === qualifier ) === keep;
5808 });
5809
5810 } else if ( typeof qualifier === "string" ) {
5811 var filtered = jQuery.grep(elements, function( elem ) {
5812 return elem.nodeType === 1;
5813 });
5814
5815 if ( isSimple.test( qualifier ) ) {
5816 return jQuery.filter(qualifier, filtered, !keep);
5817 } else {
5818 qualifier = jQuery.filter( qualifier, filtered );
5819 }
5820 }
5821
5822 return jQuery.grep(elements, function( elem ) {
5823 return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
5824 });
5825}
5826function createSafeFragment( document ) {
5827 var list = nodeNames.split( "|" ),
5828 safeFrag = document.createDocumentFragment();
5829
5830 if ( safeFrag.createElement ) {
5831 while ( list.length ) {
5832 safeFrag.createElement(
5833 list.pop()
5834 );
5835 }
5836 }
5837 return safeFrag;
5838}
5839
5840var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
5841 "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
5842 rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
5843 rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
5844 rleadingWhitespace = /^\s+/,
5845 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
5846 rtagName = /<([\w:]+)/,
5847 rtbody = /<tbody/i,
5848 rhtml = /<|&#?\w+;/,
5849 rnoInnerhtml = /<(?:script|style|link)/i,
5850 manipulation_rcheckableType = /^(?:checkbox|radio)$/i,
5851 // checked="checked" or checked
5852 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
5853 rscriptType = /^$|\/(?:java|ecma)script/i,
5854 rscriptTypeMasked = /^true\/(.*)/,
5855 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
5856
5857 // We have to close these tags to support XHTML (#13200)
5858 wrapMap = {
5859 option: [ 1, "<select multiple='multiple'>", "</select>" ],
5860 legend: [ 1, "<fieldset>", "</fieldset>" ],
5861 area: [ 1, "<map>", "</map>" ],
5862 param: [ 1, "<object>", "</object>" ],
5863 thead: [ 1, "<table>", "</table>" ],
5864 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
5865 col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
5866 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
5867
5868 // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
5869 // unless wrapped in a div with non-breaking characters in front of it.
5870 _default: jQuery.support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X<div>", "</div>" ]
5871 },
5872 safeFragment = createSafeFragment( document ),
5873 fragmentDiv = safeFragment.appendChild( document.createElement("div") );
5874
5875wrapMap.optgroup = wrapMap.option;
5876wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5877wrapMap.th = wrapMap.td;
5878
5879jQuery.fn.extend({
5880 text: function( value ) {
5881 return jQuery.access( this, function( value ) {
5882 return value === undefined ?
5883 jQuery.text( this ) :
5884 this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
5885 }, null, value, arguments.length );
5886 },
5887
5888 wrapAll: function( html ) {
5889 if ( jQuery.isFunction( html ) ) {
5890 return this.each(function(i) {
5891 jQuery(this).wrapAll( html.call(this, i) );
5892 });
5893 }
5894
5895 if ( this[0] ) {
5896 // The elements to wrap the target around
5897 var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
5898
5899 if ( this[0].parentNode ) {
5900 wrap.insertBefore( this[0] );
5901 }
5902
5903 wrap.map(function() {
5904 var elem = this;
5905
5906 while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
5907 elem = elem.firstChild;
5908 }
5909
5910 return elem;
5911 }).append( this );
5912 }
5913
5914 return this;
5915 },
5916
5917 wrapInner: function( html ) {
5918 if ( jQuery.isFunction( html ) ) {
5919 return this.each(function(i) {
5920 jQuery(this).wrapInner( html.call(this, i) );
5921 });
5922 }
5923
5924 return this.each(function() {
5925 var self = jQuery( this ),
5926 contents = self.contents();
5927
5928 if ( contents.length ) {
5929 contents.wrapAll( html );
5930
5931 } else {
5932 self.append( html );
5933 }
5934 });
5935 },
5936
5937 wrap: function( html ) {
5938 var isFunction = jQuery.isFunction( html );
5939
5940 return this.each(function(i) {
5941 jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
5942 });
5943 },
5944
5945 unwrap: function() {
5946 return this.parent().each(function() {
5947 if ( !jQuery.nodeName( this, "body" ) ) {
5948 jQuery( this ).replaceWith( this.childNodes );
5949 }
5950 }).end();
5951 },
5952
5953 append: function() {
5954 return this.domManip(arguments, true, function( elem ) {
5955 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5956 this.appendChild( elem );
5957 }
5958 });
5959 },
5960
5961 prepend: function() {
5962 return this.domManip(arguments, true, function( elem ) {
5963 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
5964 this.insertBefore( elem, this.firstChild );
5965 }
5966 });
5967 },
5968
5969 before: function() {
5970 return this.domManip( arguments, false, function( elem ) {
5971 if ( this.parentNode ) {
5972 this.parentNode.insertBefore( elem, this );
5973 }
5974 });
5975 },
5976
5977 after: function() {
5978 return this.domManip( arguments, false, function( elem ) {
5979 if ( this.parentNode ) {
5980 this.parentNode.insertBefore( elem, this.nextSibling );
5981 }
5982 });
5983 },
5984
5985 // keepData is for internal use only--do not document
5986 remove: function( selector, keepData ) {
5987 var elem,
5988 i = 0;
5989
5990 for ( ; (elem = this[i]) != null; i++ ) {
5991 if ( !selector || jQuery.filter( selector, [ elem ] ).length > 0 ) {
5992 if ( !keepData && elem.nodeType === 1 ) {
5993 jQuery.cleanData( getAll( elem ) );
5994 }
5995
5996 if ( elem.parentNode ) {
5997 if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
5998 setGlobalEval( getAll( elem, "script" ) );
5999 }
6000 elem.parentNode.removeChild( elem );
6001 }
6002 }
6003 }
6004
6005 return this;
6006 },
6007
6008 empty: function() {
6009 var elem,
6010 i = 0;
6011
6012 for ( ; (elem = this[i]) != null; i++ ) {
6013 // Remove element nodes and prevent memory leaks
6014 if ( elem.nodeType === 1 ) {
6015 jQuery.cleanData( getAll( elem, false ) );
6016 }
6017
6018 // Remove any remaining nodes
6019 while ( elem.firstChild ) {
6020 elem.removeChild( elem.firstChild );
6021 }
6022
6023 // If this is a select, ensure that it displays empty (#12336)
6024 // Support: IE<9
6025 if ( elem.options && jQuery.nodeName( elem, "select" ) ) {
6026 elem.options.length = 0;
6027 }
6028 }
6029
6030 return this;
6031 },
6032
6033 clone: function( dataAndEvents, deepDataAndEvents ) {
6034 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
6035 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
6036
6037 return this.map( function () {
6038 return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
6039 });
6040 },
6041
6042 html: function( value ) {
6043 return jQuery.access( this, function( value ) {
6044 var elem = this[0] || {},
6045 i = 0,
6046 l = this.length;
6047
6048 if ( value === undefined ) {
6049 return elem.nodeType === 1 ?
6050 elem.innerHTML.replace( rinlinejQuery, "" ) :
6051 undefined;
6052 }
6053
6054 // See if we can take a shortcut and just use innerHTML
6055 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
6056 ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
6057 ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
6058 !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
6059
6060 value = value.replace( rxhtmlTag, "<$1></$2>" );
6061
6062 try {
6063 for (; i < l; i++ ) {
6064 // Remove element nodes and prevent memory leaks
6065 elem = this[i] || {};
6066 if ( elem.nodeType === 1 ) {
6067 jQuery.cleanData( getAll( elem, false ) );
6068 elem.innerHTML = value;
6069 }
6070 }
6071
6072 elem = 0;
6073
6074 // If using innerHTML throws an exception, use the fallback method
6075 } catch(e) {}
6076 }
6077
6078 if ( elem ) {
6079 this.empty().append( value );
6080 }
6081 }, null, value, arguments.length );
6082 },
6083
6084 replaceWith: function( value ) {
6085 var isFunc = jQuery.isFunction( value );
6086
6087 // Make sure that the elements are removed from the DOM before they are inserted
6088 // this can help fix replacing a parent with child elements
6089 if ( !isFunc && typeof value !== "string" ) {
6090 value = jQuery( value ).not( this ).detach();
6091 }
6092
6093 return this.domManip( [ value ], true, function( elem ) {
6094 var next = this.nextSibling,
6095 parent = this.parentNode;
6096
6097 if ( parent ) {
6098 jQuery( this ).remove();
6099 parent.insertBefore( elem, next );
6100 }
6101 });
6102 },
6103
6104 detach: function( selector ) {
6105 return this.remove( selector, true );
6106 },
6107
6108 domManip: function( args, table, callback ) {
6109
6110 // Flatten any nested arrays
6111 args = core_concat.apply( [], args );
6112
6113 var first, node, hasScripts,
6114 scripts, doc, fragment,
6115 i = 0,
6116 l = this.length,
6117 set = this,
6118 iNoClone = l - 1,
6119 value = args[0],
6120 isFunction = jQuery.isFunction( value );
6121
6122 // We can't cloneNode fragments that contain checked, in WebKit
6123 if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) {
6124 return this.each(function( index ) {
6125 var self = set.eq( index );
6126 if ( isFunction ) {
6127 args[0] = value.call( this, index, table ? self.html() : undefined );
6128 }
6129 self.domManip( args, table, callback );
6130 });
6131 }
6132
6133 if ( l ) {
6134 fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
6135 first = fragment.firstChild;
6136
6137 if ( fragment.childNodes.length === 1 ) {
6138 fragment = first;
6139 }
6140
6141 if ( first ) {
6142 table = table && jQuery.nodeName( first, "tr" );
6143 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
6144 hasScripts = scripts.length;
6145
6146 // Use the original fragment for the last item instead of the first because it can end up
6147 // being emptied incorrectly in certain situations (#8070).
6148 for ( ; i < l; i++ ) {
6149 node = fragment;
6150
6151 if ( i !== iNoClone ) {
6152 node = jQuery.clone( node, true, true );
6153
6154 // Keep references to cloned scripts for later restoration
6155 if ( hasScripts ) {
6156 jQuery.merge( scripts, getAll( node, "script" ) );
6157 }
6158 }
6159
6160 callback.call(
6161 table && jQuery.nodeName( this[i], "table" ) ?
6162 findOrAppend( this[i], "tbody" ) :
6163 this[i],
6164 node,
6165 i
6166 );
6167 }
6168
6169 if ( hasScripts ) {
6170 doc = scripts[ scripts.length - 1 ].ownerDocument;
6171
6172 // Reenable scripts
6173 jQuery.map( scripts, restoreScript );
6174
6175 // Evaluate executable scripts on first document insertion
6176 for ( i = 0; i < hasScripts; i++ ) {
6177 node = scripts[ i ];
6178 if ( rscriptType.test( node.type || "" ) &&
6179 !jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
6180
6181 if ( node.src ) {
6182 // Hope ajax is available...
6183 jQuery.ajax({
6184 url: node.src,
6185 type: "GET",
6186 dataType: "script",
6187 async: false,
6188 global: false,
6189 "throws": true
6190 });
6191 } else {
6192 jQuery.globalEval( ( node.text || node.textContent || node.innerHTML || "" ).replace( rcleanScript, "" ) );
6193 }
6194 }
6195 }
6196 }
6197
6198 // Fix #11809: Avoid leaking memory
6199 fragment = first = null;
6200 }
6201 }
6202
6203 return this;
6204 }
6205});
6206
6207function findOrAppend( elem, tag ) {
6208 return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
6209}
6210
6211// Replace/restore the type attribute of script elements for safe DOM manipulation
6212function disableScript( elem ) {
6213 var attr = elem.getAttributeNode("type");
6214 elem.type = ( attr && attr.specified ) + "/" + elem.type;
6215 return elem;
6216}
6217function restoreScript( elem ) {
6218 var match = rscriptTypeMasked.exec( elem.type );
6219 if ( match ) {
6220 elem.type = match[1];
6221 } else {
6222 elem.removeAttribute("type");
6223 }
6224 return elem;
6225}
6226
6227// Mark scripts as having already been evaluated
6228function setGlobalEval( elems, refElements ) {
6229 var elem,
6230 i = 0;
6231 for ( ; (elem = elems[i]) != null; i++ ) {
6232 jQuery._data( elem, "globalEval", !refElements || jQuery._data( refElements[i], "globalEval" ) );
6233 }
6234}
6235
6236function cloneCopyEvent( src, dest ) {
6237
6238 if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
6239 return;
6240 }
6241
6242 var type, i, l,
6243 oldData = jQuery._data( src ),
6244 curData = jQuery._data( dest, oldData ),
6245 events = oldData.events;
6246
6247 if ( events ) {
6248 delete curData.handle;
6249 curData.events = {};
6250
6251 for ( type in events ) {
6252 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6253 jQuery.event.add( dest, type, events[ type ][ i ] );
6254 }
6255 }
6256 }
6257
6258 // make the cloned public data object a copy from the original
6259 if ( curData.data ) {
6260 curData.data = jQuery.extend( {}, curData.data );
6261 }
6262}
6263
6264function fixCloneNodeIssues( src, dest ) {
6265 var nodeName, e, data;
6266
6267 // We do not need to do anything for non-Elements
6268 if ( dest.nodeType !== 1 ) {
6269 return;
6270 }
6271
6272 nodeName = dest.nodeName.toLowerCase();
6273
6274 // IE6-8 copies events bound via attachEvent when using cloneNode.
6275 if ( !jQuery.support.noCloneEvent && dest[ jQuery.expando ] ) {
6276 data = jQuery._data( dest );
6277
6278 for ( e in data.events ) {
6279 jQuery.removeEvent( dest, e, data.handle );
6280 }
6281
6282 // Event data gets referenced instead of copied if the expando gets copied too
6283 dest.removeAttribute( jQuery.expando );
6284 }
6285
6286 // IE blanks contents when cloning scripts, and tries to evaluate newly-set text
6287 if ( nodeName === "script" && dest.text !== src.text ) {
6288 disableScript( dest ).text = src.text;
6289 restoreScript( dest );
6290
6291 // IE6-10 improperly clones children of object elements using classid.
6292 // IE10 throws NoModificationAllowedError if parent is null, #12132.
6293 } else if ( nodeName === "object" ) {
6294 if ( dest.parentNode ) {
6295 dest.outerHTML = src.outerHTML;
6296 }
6297
6298 // This path appears unavoidable for IE9. When cloning an object
6299 // element in IE9, the outerHTML strategy above is not sufficient.
6300 // If the src has innerHTML and the destination does not,
6301 // copy the src.innerHTML into the dest.innerHTML. #10324
6302 if ( jQuery.support.html5Clone && ( src.innerHTML && !jQuery.trim(dest.innerHTML) ) ) {
6303 dest.innerHTML = src.innerHTML;
6304 }
6305
6306 } else if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) {
6307 // IE6-8 fails to persist the checked state of a cloned checkbox
6308 // or radio button. Worse, IE6-7 fail to give the cloned element
6309 // a checked appearance if the defaultChecked value isn't also set
6310
6311 dest.defaultChecked = dest.checked = src.checked;
6312
6313 // IE6-7 get confused and end up setting the value of a cloned
6314 // checkbox/radio button to an empty string instead of "on"
6315 if ( dest.value !== src.value ) {
6316 dest.value = src.value;
6317 }
6318
6319 // IE6-8 fails to return the selected option to the default selected
6320 // state when cloning options
6321 } else if ( nodeName === "option" ) {
6322 dest.defaultSelected = dest.selected = src.defaultSelected;
6323
6324 // IE6-8 fails to set the defaultValue to the correct value when
6325 // cloning other types of input fields
6326 } else if ( nodeName === "input" || nodeName === "textarea" ) {
6327 dest.defaultValue = src.defaultValue;
6328 }
6329}
6330
6331jQuery.each({
6332 appendTo: "append",
6333 prependTo: "prepend",
6334 insertBefore: "before",
6335 insertAfter: "after",
6336 replaceAll: "replaceWith"
6337}, function( name, original ) {
6338 jQuery.fn[ name ] = function( selector ) {
6339 var elems,
6340 i = 0,
6341 ret = [],
6342 insert = jQuery( selector ),
6343 last = insert.length - 1;
6344
6345 for ( ; i <= last; i++ ) {
6346 elems = i === last ? this : this.clone(true);
6347 jQuery( insert[i] )[ original ]( elems );
6348
6349 // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get()
6350 core_push.apply( ret, elems.get() );
6351 }
6352
6353 return this.pushStack( ret );
6354 };
6355});
6356
6357function getAll( context, tag ) {
6358 var elems, elem,
6359 i = 0,
6360 found = typeof context.getElementsByTagName !== core_strundefined ? context.getElementsByTagName( tag || "*" ) :
6361 typeof context.querySelectorAll !== core_strundefined ? context.querySelectorAll( tag || "*" ) :
6362 undefined;
6363
6364 if ( !found ) {
6365 for ( found = [], elems = context.childNodes || context; (elem = elems[i]) != null; i++ ) {
6366 if ( !tag || jQuery.nodeName( elem, tag ) ) {
6367 found.push( elem );
6368 } else {
6369 jQuery.merge( found, getAll( elem, tag ) );
6370 }
6371 }
6372 }
6373
6374 return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
6375 jQuery.merge( [ context ], found ) :
6376 found;
6377}
6378
6379// Used in buildFragment, fixes the defaultChecked property
6380function fixDefaultChecked( elem ) {
6381 if ( manipulation_rcheckableType.test( elem.type ) ) {
6382 elem.defaultChecked = elem.checked;
6383 }
6384}
6385
6386jQuery.extend({
6387 clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6388 var destElements, node, clone, i, srcElements,
6389 inPage = jQuery.contains( elem.ownerDocument, elem );
6390
6391 if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
6392 clone = elem.cloneNode( true );
6393
6394 // IE<=8 does not properly clone detached, unknown element nodes
6395 } else {
6396 fragmentDiv.innerHTML = elem.outerHTML;
6397 fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
6398 }
6399
6400 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
6401 (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
6402
6403 // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
6404 destElements = getAll( clone );
6405 srcElements = getAll( elem );
6406
6407 // Fix all IE cloning issues
6408 for ( i = 0; (node = srcElements[i]) != null; ++i ) {
6409 // Ensure that the destination node is not null; Fixes #9587
6410 if ( destElements[i] ) {
6411 fixCloneNodeIssues( node, destElements[i] );
6412 }
6413 }
6414 }
6415
6416 // Copy the events from the original to the clone
6417 if ( dataAndEvents ) {
6418 if ( deepDataAndEvents ) {
6419 srcElements = srcElements || getAll( elem );
6420 destElements = destElements || getAll( clone );
6421
6422 for ( i = 0; (node = srcElements[i]) != null; i++ ) {
6423 cloneCopyEvent( node, destElements[i] );
6424 }
6425 } else {
6426 cloneCopyEvent( elem, clone );
6427 }
6428 }
6429
6430 // Preserve script evaluation history
6431 destElements = getAll( clone, "script" );
6432 if ( destElements.length > 0 ) {
6433 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
6434 }
6435
6436 destElements = srcElements = node = null;
6437
6438 // Return the cloned set
6439 return clone;
6440 },
6441
6442 buildFragment: function( elems, context, scripts, selection ) {
6443 var j, elem, contains,
6444 tmp, tag, tbody, wrap,
6445 l = elems.length,
6446
6447 // Ensure a safe fragment
6448 safe = createSafeFragment( context ),
6449
6450 nodes = [],
6451 i = 0;
6452
6453 for ( ; i < l; i++ ) {
6454 elem = elems[ i ];
6455
6456 if ( elem || elem === 0 ) {
6457
6458 // Add nodes directly
6459 if ( jQuery.type( elem ) === "object" ) {
6460 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
6461
6462 // Convert non-html into a text node
6463 } else if ( !rhtml.test( elem ) ) {
6464 nodes.push( context.createTextNode( elem ) );
6465
6466 // Convert html into DOM nodes
6467 } else {
6468 tmp = tmp || safe.appendChild( context.createElement("div") );
6469
6470 // Deserialize a standard representation
6471 tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
6472 wrap = wrapMap[ tag ] || wrapMap._default;
6473
6474 tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
6475
6476 // Descend through wrappers to the right content
6477 j = wrap[0];
6478 while ( j-- ) {
6479 tmp = tmp.lastChild;
6480 }
6481
6482 // Manually add leading whitespace removed by IE
6483 if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
6484 nodes.push( context.createTextNode( rleadingWhitespace.exec( elem )[0] ) );
6485 }
6486
6487 // Remove IE's autoinserted <tbody> from table fragments
6488 if ( !jQuery.support.tbody ) {
6489
6490 // String was a <table>, *may* have spurious <tbody>
6491 elem = tag === "table" && !rtbody.test( elem ) ?
6492 tmp.firstChild :
6493
6494 // String was a bare <thead> or <tfoot>
6495 wrap[1] === "<table>" && !rtbody.test( elem ) ?
6496 tmp :
6497 0;
6498
6499 j = elem && elem.childNodes.length;
6500 while ( j-- ) {
6501 if ( jQuery.nodeName( (tbody = elem.childNodes[j]), "tbody" ) && !tbody.childNodes.length ) {
6502 elem.removeChild( tbody );
6503 }
6504 }
6505 }
6506
6507 jQuery.merge( nodes, tmp.childNodes );
6508
6509 // Fix #12392 for WebKit and IE > 9
6510 tmp.textContent = "";
6511
6512 // Fix #12392 for oldIE
6513 while ( tmp.firstChild ) {
6514 tmp.removeChild( tmp.firstChild );
6515 }
6516
6517 // Remember the top-level container for proper cleanup
6518 tmp = safe.lastChild;
6519 }
6520 }
6521 }
6522
6523 // Fix #11356: Clear elements from fragment
6524 if ( tmp ) {
6525 safe.removeChild( tmp );
6526 }
6527
6528 // Reset defaultChecked for any radios and checkboxes
6529 // about to be appended to the DOM in IE 6/7 (#8060)
6530 if ( !jQuery.support.appendChecked ) {
6531 jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked );
6532 }
6533
6534 i = 0;
6535 while ( (elem = nodes[ i++ ]) ) {
6536
6537 // #4087 - If origin and destination elements are the same, and this is
6538 // that element, do not do anything
6539 if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
6540 continue;
6541 }
6542
6543 contains = jQuery.contains( elem.ownerDocument, elem );
6544
6545 // Append to fragment
6546 tmp = getAll( safe.appendChild( elem ), "script" );
6547
6548 // Preserve script evaluation history
6549 if ( contains ) {
6550 setGlobalEval( tmp );
6551 }
6552
6553 // Capture executables
6554 if ( scripts ) {
6555 j = 0;
6556 while ( (elem = tmp[ j++ ]) ) {
6557 if ( rscriptType.test( elem.type || "" ) ) {
6558 scripts.push( elem );
6559 }
6560 }
6561 }
6562 }
6563
6564 tmp = null;
6565
6566 return safe;
6567 },
6568
6569 cleanData: function( elems, /* internal */ acceptData ) {
6570 var elem, type, id, data,
6571 i = 0,
6572 internalKey = jQuery.expando,
6573 cache = jQuery.cache,
6574 deleteExpando = jQuery.support.deleteExpando,
6575 special = jQuery.event.special;
6576
6577 for ( ; (elem = elems[i]) != null; i++ ) {
6578
6579 if ( acceptData || jQuery.acceptData( elem ) ) {
6580
6581 id = elem[ internalKey ];
6582 data = id && cache[ id ];
6583
6584 if ( data ) {
6585 if ( data.events ) {
6586 for ( type in data.events ) {
6587 if ( special[ type ] ) {
6588 jQuery.event.remove( elem, type );
6589
6590 // This is a shortcut to avoid jQuery.event.remove's overhead
6591 } else {
6592 jQuery.removeEvent( elem, type, data.handle );
6593 }
6594 }
6595 }
6596
6597 // Remove cache only if it was not already removed by jQuery.event.remove
6598 if ( cache[ id ] ) {
6599
6600 delete cache[ id ];
6601
6602 // IE does not allow us to delete expando properties from nodes,
6603 // nor does it have a removeAttribute function on Document nodes;
6604 // we must handle all of these cases
6605 if ( deleteExpando ) {
6606 delete elem[ internalKey ];
6607
6608 } else if ( typeof elem.removeAttribute !== core_strundefined ) {
6609 elem.removeAttribute( internalKey );
6610
6611 } else {
6612 elem[ internalKey ] = null;
6613 }
6614
6615 core_deletedIds.push( id );
6616 }
6617 }
6618 }
6619 }
6620 }
6621});
6622var iframe, getStyles, curCSS,
6623 ralpha = /alpha\([^)]*\)/i,
6624 ropacity = /opacity\s*=\s*([^)]*)/,
6625 rposition = /^(top|right|bottom|left)$/,
6626 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6627 // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
6628 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
6629 rmargin = /^margin/,
6630 rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
6631 rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
6632 rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ),
6633 elemdisplay = { BODY: "block" },
6634
6635 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
6636 cssNormalTransform = {
6637 letterSpacing: 0,
6638 fontWeight: 400
6639 },
6640
6641 cssExpand = [ "Top", "Right", "Bottom", "Left" ],
6642 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
6643
6644// return a css property mapped to a potentially vendor prefixed property
6645function vendorPropName( style, name ) {
6646
6647 // shortcut for names that are not vendor prefixed
6648 if ( name in style ) {
6649 return name;
6650 }
6651
6652 // check for vendor prefixed names
6653 var capName = name.charAt(0).toUpperCase() + name.slice(1),
6654 origName = name,
6655 i = cssPrefixes.length;
6656
6657 while ( i-- ) {
6658 name = cssPrefixes[ i ] + capName;
6659 if ( name in style ) {
6660 return name;
6661 }
6662 }
6663
6664 return origName;
6665}
6666
6667function isHidden( elem, el ) {
6668 // isHidden might be called from jQuery#filter function;
6669 // in that case, element will be second argument
6670 elem = el || elem;
6671 return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
6672}
6673
6674function showHide( elements, show ) {
6675 var display, elem, hidden,
6676 values = [],
6677 index = 0,
6678 length = elements.length;
6679
6680 for ( ; index < length; index++ ) {
6681 elem = elements[ index ];
6682 if ( !elem.style ) {
6683 continue;
6684 }
6685
6686 values[ index ] = jQuery._data( elem, "olddisplay" );
6687 display = elem.style.display;
6688 if ( show ) {
6689 // Reset the inline display of this element to learn if it is
6690 // being hidden by cascaded rules or not
6691 if ( !values[ index ] && display === "none" ) {
6692 elem.style.display = "";
6693 }
6694
6695 // Set elements which have been overridden with display: none
6696 // in a stylesheet to whatever the default browser style is
6697 // for such an element
6698 if ( elem.style.display === "" && isHidden( elem ) ) {
6699 values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
6700 }
6701 } else {
6702
6703 if ( !values[ index ] ) {
6704 hidden = isHidden( elem );
6705
6706 if ( display && display !== "none" || !hidden ) {
6707 jQuery._data( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
6708 }
6709 }
6710 }
6711 }
6712
6713 // Set the display of most of the elements in a second loop
6714 // to avoid the constant reflow
6715 for ( index = 0; index < length; index++ ) {
6716 elem = elements[ index ];
6717 if ( !elem.style ) {
6718 continue;
6719 }
6720 if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
6721 elem.style.display = show ? values[ index ] || "" : "none";
6722 }
6723 }
6724
6725 return elements;
6726}
6727
6728jQuery.fn.extend({
6729 css: function( name, value ) {
6730 return jQuery.access( this, function( elem, name, value ) {
6731 var len, styles,
6732 map = {},
6733 i = 0;
6734
6735 if ( jQuery.isArray( name ) ) {
6736 styles = getStyles( elem );
6737 len = name.length;
6738
6739 for ( ; i < len; i++ ) {
6740 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
6741 }
6742
6743 return map;
6744 }
6745
6746 return value !== undefined ?
6747 jQuery.style( elem, name, value ) :
6748 jQuery.css( elem, name );
6749 }, name, value, arguments.length > 1 );
6750 },
6751 show: function() {
6752 return showHide( this, true );
6753 },
6754 hide: function() {
6755 return showHide( this );
6756 },
6757 toggle: function( state ) {
6758 var bool = typeof state === "boolean";
6759
6760 return this.each(function() {
6761 if ( bool ? state : isHidden( this ) ) {
6762 jQuery( this ).show();
6763 } else {
6764 jQuery( this ).hide();
6765 }
6766 });
6767 }
6768});
6769
6770jQuery.extend({
6771 // Add in style property hooks for overriding the default
6772 // behavior of getting and setting a style property
6773 cssHooks: {
6774 opacity: {
6775 get: function( elem, computed ) {
6776 if ( computed ) {
6777 // We should always get a number back from opacity
6778 var ret = curCSS( elem, "opacity" );
6779 return ret === "" ? "1" : ret;
6780 }
6781 }
6782 }
6783 },
6784
6785 // Exclude the following css properties to add px
6786 cssNumber: {
6787 "columnCount": true,
6788 "fillOpacity": true,
6789 "fontWeight": true,
6790 "lineHeight": true,
6791 "opacity": true,
6792 "orphans": true,
6793 "widows": true,
6794 "zIndex": true,
6795 "zoom": true
6796 },
6797
6798 // Add in properties whose names you wish to fix before
6799 // setting or getting the value
6800 cssProps: {
6801 // normalize float css property
6802 "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
6803 },
6804
6805 // Get and set the style property on a DOM Node
6806 style: function( elem, name, value, extra ) {
6807 // Don't set styles on text and comment nodes
6808 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
6809 return;
6810 }
6811
6812 // Make sure that we're working with the right name
6813 var ret, type, hooks,
6814 origName = jQuery.camelCase( name ),
6815 style = elem.style;
6816
6817 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
6818
6819 // gets hook for the prefixed version
6820 // followed by the unprefixed version
6821 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6822
6823 // Check if we're setting a value
6824 if ( value !== undefined ) {
6825 type = typeof value;
6826
6827 // convert relative number strings (+= or -=) to relative numbers. #7345
6828 if ( type === "string" && (ret = rrelNum.exec( value )) ) {
6829 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
6830 // Fixes bug #9237
6831 type = "number";
6832 }
6833
6834 // Make sure that NaN and null values aren't set. See: #7116
6835 if ( value == null || type === "number" && isNaN( value ) ) {
6836 return;
6837 }
6838
6839 // If a number was passed in, add 'px' to the (except for certain CSS properties)
6840 if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
6841 value += "px";
6842 }
6843
6844 // Fixes #8908, it can be done more correctly by specifing setters in cssHooks,
6845 // but it would mean to define eight (for every problematic property) identical functions
6846 if ( !jQuery.support.clearCloneStyle && value === "" && name.indexOf("background") === 0 ) {
6847 style[ name ] = "inherit";
6848 }
6849
6850 // If a hook was provided, use that value, otherwise just set the specified value
6851 if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
6852
6853 // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
6854 // Fixes bug #5509
6855 try {
6856 style[ name ] = value;
6857 } catch(e) {}
6858 }
6859
6860 } else {
6861 // If a hook was provided get the non-computed value from there
6862 if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
6863 return ret;
6864 }
6865
6866 // Otherwise just get the value from the style object
6867 return style[ name ];
6868 }
6869 },
6870
6871 css: function( elem, name, extra, styles ) {
6872 var num, val, hooks,
6873 origName = jQuery.camelCase( name );
6874
6875 // Make sure that we're working with the right name
6876 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
6877
6878 // gets hook for the prefixed version
6879 // followed by the unprefixed version
6880 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6881
6882 // If a hook was provided get the computed value from there
6883 if ( hooks && "get" in hooks ) {
6884 val = hooks.get( elem, true, extra );
6885 }
6886
6887 // Otherwise, if a way to get the computed value exists, use that
6888 if ( val === undefined ) {
6889 val = curCSS( elem, name, styles );
6890 }
6891
6892 //convert "normal" to computed value
6893 if ( val === "normal" && name in cssNormalTransform ) {
6894 val = cssNormalTransform[ name ];
6895 }
6896
6897 // Return, converting to number if forced or a qualifier was provided and val looks numeric
6898 if ( extra === "" || extra ) {
6899 num = parseFloat( val );
6900 return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
6901 }
6902 return val;
6903 },
6904
6905 // A method for quickly swapping in/out CSS properties to get correct calculations
6906 swap: function( elem, options, callback, args ) {
6907 var ret, name,
6908 old = {};
6909
6910 // Remember the old values, and insert the new ones
6911 for ( name in options ) {
6912 old[ name ] = elem.style[ name ];
6913 elem.style[ name ] = options[ name ];
6914 }
6915
6916 ret = callback.apply( elem, args || [] );
6917
6918 // Revert the old values
6919 for ( name in options ) {
6920 elem.style[ name ] = old[ name ];
6921 }
6922
6923 return ret;
6924 }
6925});
6926
6927// NOTE: we've included the "window" in window.getComputedStyle
6928// because jsdom on node.js will break without it.
6929if ( window.getComputedStyle ) {
6930 getStyles = function( elem ) {
6931 return window.getComputedStyle( elem, null );
6932 };
6933
6934 curCSS = function( elem, name, _computed ) {
6935 var width, minWidth, maxWidth,
6936 computed = _computed || getStyles( elem ),
6937
6938 // getPropertyValue is only needed for .css('filter') in IE9, see #12537
6939 ret = computed ? computed.getPropertyValue( name ) || computed[ name ] : undefined,
6940 style = elem.style;
6941
6942 if ( computed ) {
6943
6944 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
6945 ret = jQuery.style( elem, name );
6946 }
6947
6948 // A tribute to the "awesome hack by Dean Edwards"
6949 // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
6950 // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
6951 // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
6952 if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
6953
6954 // Remember the original values
6955 width = style.width;
6956 minWidth = style.minWidth;
6957 maxWidth = style.maxWidth;
6958
6959 // Put in the new values to get a computed value out
6960 style.minWidth = style.maxWidth = style.width = ret;
6961 ret = computed.width;
6962
6963 // Revert the changed values
6964 style.width = width;
6965 style.minWidth = minWidth;
6966 style.maxWidth = maxWidth;
6967 }
6968 }
6969
6970 return ret;
6971 };
6972} else if ( document.documentElement.currentStyle ) {
6973 getStyles = function( elem ) {
6974 return elem.currentStyle;
6975 };
6976
6977 curCSS = function( elem, name, _computed ) {
6978 var left, rs, rsLeft,
6979 computed = _computed || getStyles( elem ),
6980 ret = computed ? computed[ name ] : undefined,
6981 style = elem.style;
6982
6983 // Avoid setting ret to empty string here
6984 // so we don't default to auto
6985 if ( ret == null && style && style[ name ] ) {
6986 ret = style[ name ];
6987 }
6988
6989 // From the awesome hack by Dean Edwards
6990 // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
6991
6992 // If we're not dealing with a regular pixel number
6993 // but a number that has a weird ending, we need to convert it to pixels
6994 // but not position css attributes, as those are proportional to the parent element instead
6995 // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
6996 if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
6997
6998 // Remember the original values
6999 left = style.left;
7000 rs = elem.runtimeStyle;
7001 rsLeft = rs && rs.left;
7002
7003 // Put in the new values to get a computed value out
7004 if ( rsLeft ) {
7005 rs.left = elem.currentStyle.left;
7006 }
7007 style.left = name === "fontSize" ? "1em" : ret;
7008 ret = style.pixelLeft + "px";
7009
7010 // Revert the changed values
7011 style.left = left;
7012 if ( rsLeft ) {
7013 rs.left = rsLeft;
7014 }
7015 }
7016
7017 return ret === "" ? "auto" : ret;
7018 };
7019}
7020
7021function setPositiveNumber( elem, value, subtract ) {
7022 var matches = rnumsplit.exec( value );
7023 return matches ?
7024 // Guard against undefined "subtract", e.g., when used as in cssHooks
7025 Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
7026 value;
7027}
7028
7029function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
7030 var i = extra === ( isBorderBox ? "border" : "content" ) ?
7031 // If we already have the right measurement, avoid augmentation
7032 4 :
7033 // Otherwise initialize for horizontal or vertical properties
7034 name === "width" ? 1 : 0,
7035
7036 val = 0;
7037
7038 for ( ; i < 4; i += 2 ) {
7039 // both box models exclude margin, so add it if we want it
7040 if ( extra === "margin" ) {
7041 val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
7042 }
7043
7044 if ( isBorderBox ) {
7045 // border-box includes padding, so remove it if we want content
7046 if ( extra === "content" ) {
7047 val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7048 }
7049
7050 // at this point, extra isn't border nor margin, so remove border
7051 if ( extra !== "margin" ) {
7052 val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7053 }
7054 } else {
7055 // at this point, extra isn't content, so add padding
7056 val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
7057
7058 // at this point, extra isn't content nor padding, so add border
7059 if ( extra !== "padding" ) {
7060 val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
7061 }
7062 }
7063 }
7064
7065 return val;
7066}
7067
7068function getWidthOrHeight( elem, name, extra ) {
7069
7070 // Start with offset property, which is equivalent to the border-box value
7071 var valueIsBorderBox = true,
7072 val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
7073 styles = getStyles( elem ),
7074 isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
7075
7076 // some non-html elements return undefined for offsetWidth, so check for null/undefined
7077 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
7078 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
7079 if ( val <= 0 || val == null ) {
7080 // Fall back to computed then uncomputed css if necessary
7081 val = curCSS( elem, name, styles );
7082 if ( val < 0 || val == null ) {
7083 val = elem.style[ name ];
7084 }
7085
7086 // Computed unit is not pixels. Stop here and return.
7087 if ( rnumnonpx.test(val) ) {
7088 return val;
7089 }
7090
7091 // we need the check for style in case a browser which returns unreliable values
7092 // for getComputedStyle silently falls back to the reliable elem.style
7093 valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
7094
7095 // Normalize "", auto, and prepare for extra
7096 val = parseFloat( val ) || 0;
7097 }
7098
7099 // use the active box-sizing model to add/subtract irrelevant styles
7100 return ( val +
7101 augmentWidthOrHeight(
7102 elem,
7103 name,
7104 extra || ( isBorderBox ? "border" : "content" ),
7105 valueIsBorderBox,
7106 styles
7107 )
7108 ) + "px";
7109}
7110
7111// Try to determine the default display value of an element
7112function css_defaultDisplay( nodeName ) {
7113 var doc = document,
7114 display = elemdisplay[ nodeName ];
7115
7116 if ( !display ) {
7117 display = actualDisplay( nodeName, doc );
7118
7119 // If the simple way fails, read from inside an iframe
7120 if ( display === "none" || !display ) {
7121 // Use the already-created iframe if possible
7122 iframe = ( iframe ||
7123 jQuery("<iframe frameborder='0' width='0' height='0'/>")
7124 .css( "cssText", "display:block !important" )
7125 ).appendTo( doc.documentElement );
7126
7127 // Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
7128 doc = ( iframe[0].contentWindow || iframe[0].contentDocument ).document;
7129 doc.write("<!doctype html><html><body>");
7130 doc.close();
7131
7132 display = actualDisplay( nodeName, doc );
7133 iframe.detach();
7134 }
7135
7136 // Store the correct default display
7137 elemdisplay[ nodeName ] = display;
7138 }
7139
7140 return display;
7141}
7142
7143// Called ONLY from within css_defaultDisplay
7144function actualDisplay( name, doc ) {
7145 var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
7146 display = jQuery.css( elem[0], "display" );
7147 elem.remove();
7148 return display;
7149}
7150
7151jQuery.each([ "height", "width" ], function( i, name ) {
7152 jQuery.cssHooks[ name ] = {
7153 get: function( elem, computed, extra ) {
7154 if ( computed ) {
7155 // certain elements can have dimension info if we invisibly show them
7156 // however, it must have a current display style that would benefit from this
7157 return elem.offsetWidth === 0 && rdisplayswap.test( jQuery.css( elem, "display" ) ) ?
7158 jQuery.swap( elem, cssShow, function() {
7159 return getWidthOrHeight( elem, name, extra );
7160 }) :
7161 getWidthOrHeight( elem, name, extra );
7162 }
7163 },
7164
7165 set: function( elem, value, extra ) {
7166 var styles = extra && getStyles( elem );
7167 return setPositiveNumber( elem, value, extra ?
7168 augmentWidthOrHeight(
7169 elem,
7170 name,
7171 extra,
7172 jQuery.support.boxSizing && jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
7173 styles
7174 ) : 0
7175 );
7176 }
7177 };
7178});
7179
7180if ( !jQuery.support.opacity ) {
7181 jQuery.cssHooks.opacity = {
7182 get: function( elem, computed ) {
7183 // IE uses filters for opacity
7184 return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
7185 ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
7186 computed ? "1" : "";
7187 },
7188
7189 set: function( elem, value ) {
7190 var style = elem.style,
7191 currentStyle = elem.currentStyle,
7192 opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
7193 filter = currentStyle && currentStyle.filter || style.filter || "";
7194
7195 // IE has trouble with opacity if it does not have layout
7196 // Force it by setting the zoom level
7197 style.zoom = 1;
7198
7199 // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
7200 // if value === "", then remove inline opacity #12685
7201 if ( ( value >= 1 || value === "" ) &&
7202 jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
7203 style.removeAttribute ) {
7204
7205 // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
7206 // if "filter:" is present at all, clearType is disabled, we want to avoid this
7207 // style.removeAttribute is IE Only, but so apparently is this code path...
7208 style.removeAttribute( "filter" );
7209
7210 // if there is no filter style applied in a css rule or unset inline opacity, we are done
7211 if ( value === "" || currentStyle && !currentStyle.filter ) {
7212 return;
7213 }
7214 }
7215
7216 // otherwise, set new filter values
7217 style.filter = ralpha.test( filter ) ?
7218 filter.replace( ralpha, opacity ) :
7219 filter + " " + opacity;
7220 }
7221 };
7222}
7223
7224// These hooks cannot be added until DOM ready because the support test
7225// for it is not run until after DOM ready
7226jQuery(function() {
7227 if ( !jQuery.support.reliableMarginRight ) {
7228 jQuery.cssHooks.marginRight = {
7229 get: function( elem, computed ) {
7230 if ( computed ) {
7231 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
7232 // Work around by temporarily setting element display to inline-block
7233 return jQuery.swap( elem, { "display": "inline-block" },
7234 curCSS, [ elem, "marginRight" ] );
7235 }
7236 }
7237 };
7238 }
7239
7240 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
7241 // getComputedStyle returns percent when specified for top/left/bottom/right
7242 // rather than make the css module depend on the offset module, we just check for it here
7243 if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
7244 jQuery.each( [ "top", "left" ], function( i, prop ) {
7245 jQuery.cssHooks[ prop ] = {
7246 get: function( elem, computed ) {
7247 if ( computed ) {
7248 computed = curCSS( elem, prop );
7249 // if curCSS returns percentage, fallback to offset
7250 return rnumnonpx.test( computed ) ?
7251 jQuery( elem ).position()[ prop ] + "px" :
7252 computed;
7253 }
7254 }
7255 };
7256 });
7257 }
7258
7259});
7260
7261if ( jQuery.expr && jQuery.expr.filters ) {
7262 jQuery.expr.filters.hidden = function( elem ) {
7263 // Support: Opera <= 12.12
7264 // Opera reports offsetWidths and offsetHeights less than zero on some elements
7265 return elem.offsetWidth <= 0 && elem.offsetHeight <= 0 ||
7266 (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none");
7267 };
7268
7269 jQuery.expr.filters.visible = function( elem ) {
7270 return !jQuery.expr.filters.hidden( elem );
7271 };
7272}
7273
7274// These hooks are used by animate to expand properties
7275jQuery.each({
7276 margin: "",
7277 padding: "",
7278 border: "Width"
7279}, function( prefix, suffix ) {
7280 jQuery.cssHooks[ prefix + suffix ] = {
7281 expand: function( value ) {
7282 var i = 0,
7283 expanded = {},
7284
7285 // assumes a single number if not a string
7286 parts = typeof value === "string" ? value.split(" ") : [ value ];
7287
7288 for ( ; i < 4; i++ ) {
7289 expanded[ prefix + cssExpand[ i ] + suffix ] =
7290 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7291 }
7292
7293 return expanded;
7294 }
7295 };
7296
7297 if ( !rmargin.test( prefix ) ) {
7298 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7299 }
7300});
7301var r20 = /%20/g,
7302 rbracket = /\[\]$/,
7303 rCRLF = /\r?\n/g,
7304 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
7305 rsubmittable = /^(?:input|select|textarea|keygen)/i;
7306
7307jQuery.fn.extend({
7308 serialize: function() {
7309 return jQuery.param( this.serializeArray() );
7310 },
7311 serializeArray: function() {
7312 return this.map(function(){
7313 // Can add propHook for "elements" to filter or add form elements
7314 var elements = jQuery.prop( this, "elements" );
7315 return elements ? jQuery.makeArray( elements ) : this;
7316 })
7317 .filter(function(){
7318 var type = this.type;
7319 // Use .is(":disabled") so that fieldset[disabled] works
7320 return this.name && !jQuery( this ).is( ":disabled" ) &&
7321 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
7322 ( this.checked || !manipulation_rcheckableType.test( type ) );
7323 })
7324 .map(function( i, elem ){
7325 var val = jQuery( this ).val();
7326
7327 return val == null ?
7328 null :
7329 jQuery.isArray( val ) ?
7330 jQuery.map( val, function( val ){
7331 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7332 }) :
7333 { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7334 }).get();
7335 }
7336});
7337
7338//Serialize an array of form elements or a set of
7339//key/values into a query string
7340jQuery.param = function( a, traditional ) {
7341 var prefix,
7342 s = [],
7343 add = function( key, value ) {
7344 // If value is a function, invoke it and return its value
7345 value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
7346 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
7347 };
7348
7349 // Set traditional to true for jQuery <= 1.3.2 behavior.
7350 if ( traditional === undefined ) {
7351 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
7352 }
7353
7354 // If an array was passed in, assume that it is an array of form elements.
7355 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
7356 // Serialize the form elements
7357 jQuery.each( a, function() {
7358 add( this.name, this.value );
7359 });
7360
7361 } else {
7362 // If traditional, encode the "old" way (the way 1.3.2 or older
7363 // did it), otherwise encode params recursively.
7364 for ( prefix in a ) {
7365 buildParams( prefix, a[ prefix ], traditional, add );
7366 }
7367 }
7368
7369 // Return the resulting serialization
7370 return s.join( "&" ).replace( r20, "+" );
7371};
7372
7373function buildParams( prefix, obj, traditional, add ) {
7374 var name;
7375
7376 if ( jQuery.isArray( obj ) ) {
7377 // Serialize array item.
7378 jQuery.each( obj, function( i, v ) {
7379 if ( traditional || rbracket.test( prefix ) ) {
7380 // Treat each array item as a scalar.
7381 add( prefix, v );
7382
7383 } else {
7384 // Item is non-scalar (array or object), encode its numeric index.
7385 buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
7386 }
7387 });
7388
7389 } else if ( !traditional && jQuery.type( obj ) === "object" ) {
7390 // Serialize object item.
7391 for ( name in obj ) {
7392 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
7393 }
7394
7395 } else {
7396 // Serialize scalar item.
7397 add( prefix, obj );
7398 }
7399}
7400jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
7401 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
7402 "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
7403
7404 // Handle event binding
7405 jQuery.fn[ name ] = function( data, fn ) {
7406 return arguments.length > 0 ?
7407 this.on( name, null, data, fn ) :
7408 this.trigger( name );
7409 };
7410});
7411
7412jQuery.fn.hover = function( fnOver, fnOut ) {
7413 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
7414};
7415var
7416 // Document location
7417 ajaxLocParts,
7418 ajaxLocation,
7419 ajax_nonce = jQuery.now(),
7420
7421 ajax_rquery = /\?/,
7422 rhash = /#.*$/,
7423 rts = /([?&])_=[^&]*/,
7424 rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
7425 // #7653, #8125, #8152: local protocol detection
7426 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
7427 rnoContent = /^(?:GET|HEAD)$/,
7428 rprotocol = /^\/\//,
7429 rurl = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
7430
7431 // Keep a copy of the old load method
7432 _load = jQuery.fn.load,
7433
7434 /* Prefilters
7435 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7436 * 2) These are called:
7437 * - BEFORE asking for a transport
7438 * - AFTER param serialization (s.data is a string if s.processData is true)
7439 * 3) key is the dataType
7440 * 4) the catchall symbol "*" can be used
7441 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7442 */
7443 prefilters = {},
7444
7445 /* Transports bindings
7446 * 1) key is the dataType
7447 * 2) the catchall symbol "*" can be used
7448 * 3) selection will start with transport dataType and THEN go to "*" if needed
7449 */
7450 transports = {},
7451
7452 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7453 allTypes = "*/".concat("*");
7454
7455// #8138, IE may throw an exception when accessing
7456// a field from window.location if document.domain has been set
7457try {
7458 ajaxLocation = location.href;
7459} catch( e ) {
7460 // Use the href attribute of an A element
7461 // since IE will modify it given document.location
7462 ajaxLocation = document.createElement( "a" );
7463 ajaxLocation.href = "";
7464 ajaxLocation = ajaxLocation.href;
7465}
7466
7467// Segment location into parts
7468ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7469
7470// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7471function addToPrefiltersOrTransports( structure ) {
7472
7473 // dataTypeExpression is optional and defaults to "*"
7474 return function( dataTypeExpression, func ) {
7475
7476 if ( typeof dataTypeExpression !== "string" ) {
7477 func = dataTypeExpression;
7478 dataTypeExpression = "*";
7479 }
7480
7481 var dataType,
7482 i = 0,
7483 dataTypes = dataTypeExpression.toLowerCase().match( core_rnotwhite ) || [];
7484
7485 if ( jQuery.isFunction( func ) ) {
7486 // For each dataType in the dataTypeExpression
7487 while ( (dataType = dataTypes[i++]) ) {
7488 // Prepend if requested
7489 if ( dataType[0] === "+" ) {
7490 dataType = dataType.slice( 1 ) || "*";
7491 (structure[ dataType ] = structure[ dataType ] || []).unshift( func );
7492
7493 // Otherwise append
7494 } else {
7495 (structure[ dataType ] = structure[ dataType ] || []).push( func );
7496 }
7497 }
7498 }
7499 };
7500}
7501
7502// Base inspection function for prefilters and transports
7503function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
7504
7505 var inspected = {},
7506 seekingTransport = ( structure === transports );
7507
7508 function inspect( dataType ) {
7509 var selected;
7510 inspected[ dataType ] = true;
7511 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
7512 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
7513 if( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
7514 options.dataTypes.unshift( dataTypeOrTransport );
7515 inspect( dataTypeOrTransport );
7516 return false;
7517 } else if ( seekingTransport ) {
7518 return !( selected = dataTypeOrTransport );
7519 }
7520 });
7521 return selected;
7522 }
7523
7524 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
7525}
7526
7527// A special extend for ajax options
7528// that takes "flat" options (not to be deep extended)
7529// Fixes #9887
7530function ajaxExtend( target, src ) {
7531 var deep, key,
7532 flatOptions = jQuery.ajaxSettings.flatOptions || {};
7533
7534 for ( key in src ) {
7535 if ( src[ key ] !== undefined ) {
7536 ( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
7537 }
7538 }
7539 if ( deep ) {
7540 jQuery.extend( true, target, deep );
7541 }
7542
7543 return target;
7544}
7545
7546jQuery.fn.load = function( url, params, callback ) {
7547 if ( typeof url !== "string" && _load ) {
7548 return _load.apply( this, arguments );
7549 }
7550
7551 var selector, response, type,
7552 self = this,
7553 off = url.indexOf(" ");
7554
7555 if ( off >= 0 ) {
7556 selector = url.slice( off, url.length );
7557 url = url.slice( 0, off );
7558 }
7559
7560 // If it's a function
7561 if ( jQuery.isFunction( params ) ) {
7562
7563 // We assume that it's the callback
7564 callback = params;
7565 params = undefined;
7566
7567 // Otherwise, build a param string
7568 } else if ( params && typeof params === "object" ) {
7569 type = "POST";
7570 }
7571
7572 // If we have elements to modify, make the request
7573 if ( self.length > 0 ) {
7574 jQuery.ajax({
7575 url: url,
7576
7577 // if "type" variable is undefined, then "GET" method will be used
7578 type: type,
7579 dataType: "html",
7580 data: params
7581 }).done(function( responseText ) {
7582
7583 // Save response for use in complete callback
7584 response = arguments;
7585
7586 self.html( selector ?
7587
7588 // If a selector was specified, locate the right elements in a dummy div
7589 // Exclude scripts to avoid IE 'Permission Denied' errors
7590 jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
7591
7592 // Otherwise use the full result
7593 responseText );
7594
7595 }).complete( callback && function( jqXHR, status ) {
7596 self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
7597 });
7598 }
7599
7600 return this;
7601};
7602
7603// Attach a bunch of functions for handling common AJAX events
7604jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
7605 jQuery.fn[ type ] = function( fn ){
7606 return this.on( type, fn );
7607 };
7608});
7609
7610jQuery.each( [ "get", "post" ], function( i, method ) {
7611 jQuery[ method ] = function( url, data, callback, type ) {
7612 // shift arguments if data argument was omitted
7613 if ( jQuery.isFunction( data ) ) {
7614 type = type || callback;
7615 callback = data;
7616 data = undefined;
7617 }
7618
7619 return jQuery.ajax({
7620 url: url,
7621 type: method,
7622 dataType: type,
7623 data: data,
7624 success: callback
7625 });
7626 };
7627});
7628
7629jQuery.extend({
7630
7631 // Counter for holding the number of active queries
7632 active: 0,
7633
7634 // Last-Modified header cache for next request
7635 lastModified: {},
7636 etag: {},
7637
7638 ajaxSettings: {
7639 url: ajaxLocation,
7640 type: "GET",
7641 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7642 global: true,
7643 processData: true,
7644 async: true,
7645 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7646 /*
7647 timeout: 0,
7648 data: null,
7649 dataType: null,
7650 username: null,
7651 password: null,
7652 cache: null,
7653 throws: false,
7654 traditional: false,
7655 headers: {},
7656 */
7657
7658 accepts: {
7659 "*": allTypes,
7660 text: "text/plain",
7661 html: "text/html",
7662 xml: "application/xml, text/xml",
7663 json: "application/json, text/javascript"
7664 },
7665
7666 contents: {
7667 xml: /xml/,
7668 html: /html/,
7669 json: /json/
7670 },
7671
7672 responseFields: {
7673 xml: "responseXML",
7674 text: "responseText"
7675 },
7676
7677 // Data converters
7678 // Keys separate source (or catchall "*") and destination types with a single space
7679 converters: {
7680
7681 // Convert anything to text
7682 "* text": window.String,
7683
7684 // Text to html (true = no transformation)
7685 "text html": true,
7686
7687 // Evaluate text as a json expression
7688 "text json": jQuery.parseJSON,
7689
7690 // Parse text as xml
7691 "text xml": jQuery.parseXML
7692 },
7693
7694 // For options that shouldn't be deep extended:
7695 // you can add your own custom options here if
7696 // and when you create one that shouldn't be
7697 // deep extended (see ajaxExtend)
7698 flatOptions: {
7699 url: true,
7700 context: true
7701 }
7702 },
7703
7704 // Creates a full fledged settings object into target
7705 // with both ajaxSettings and settings fields.
7706 // If target is omitted, writes into ajaxSettings.
7707 ajaxSetup: function( target, settings ) {
7708 return settings ?
7709
7710 // Building a settings object
7711 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
7712
7713 // Extending ajaxSettings
7714 ajaxExtend( jQuery.ajaxSettings, target );
7715 },
7716
7717 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7718 ajaxTransport: addToPrefiltersOrTransports( transports ),
7719
7720 // Main method
7721 ajax: function( url, options ) {
7722
7723 // If url is an object, simulate pre-1.5 signature
7724 if ( typeof url === "object" ) {
7725 options = url;
7726 url = undefined;
7727 }
7728
7729 // Force options to be an object
7730 options = options || {};
7731
7732 var // Cross-domain detection vars
7733 parts,
7734 // Loop variable
7735 i,
7736 // URL without anti-cache param
7737 cacheURL,
7738 // Response headers as string
7739 responseHeadersString,
7740 // timeout handle
7741 timeoutTimer,
7742
7743 // To know if global events are to be dispatched
7744 fireGlobals,
7745
7746 transport,
7747 // Response headers
7748 responseHeaders,
7749 // Create the final options object
7750 s = jQuery.ajaxSetup( {}, options ),
7751 // Callbacks context
7752 callbackContext = s.context || s,
7753 // Context for global events is callbackContext if it is a DOM node or jQuery collection
7754 globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
7755 jQuery( callbackContext ) :
7756 jQuery.event,
7757 // Deferreds
7758 deferred = jQuery.Deferred(),
7759 completeDeferred = jQuery.Callbacks("once memory"),
7760 // Status-dependent callbacks
7761 statusCode = s.statusCode || {},
7762 // Headers (they are sent all at once)
7763 requestHeaders = {},
7764 requestHeadersNames = {},
7765 // The jqXHR state
7766 state = 0,
7767 // Default abort message
7768 strAbort = "canceled",
7769 // Fake xhr
7770 jqXHR = {
7771 readyState: 0,
7772
7773 // Builds headers hashtable if needed
7774 getResponseHeader: function( key ) {
7775 var match;
7776 if ( state === 2 ) {
7777 if ( !responseHeaders ) {
7778 responseHeaders = {};
7779 while ( (match = rheaders.exec( responseHeadersString )) ) {
7780 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7781 }
7782 }
7783 match = responseHeaders[ key.toLowerCase() ];
7784 }
7785 return match == null ? null : match;
7786 },
7787
7788 // Raw string
7789 getAllResponseHeaders: function() {
7790 return state === 2 ? responseHeadersString : null;
7791 },
7792
7793 // Caches the header
7794 setRequestHeader: function( name, value ) {
7795 var lname = name.toLowerCase();
7796 if ( !state ) {
7797 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7798 requestHeaders[ name ] = value;
7799 }
7800 return this;
7801 },
7802
7803 // Overrides response content-type header
7804 overrideMimeType: function( type ) {
7805 if ( !state ) {
7806 s.mimeType = type;
7807 }
7808 return this;
7809 },
7810
7811 // Status-dependent callbacks
7812 statusCode: function( map ) {
7813 var code;
7814 if ( map ) {
7815 if ( state < 2 ) {
7816 for ( code in map ) {
7817 // Lazy-add the new callback in a way that preserves old ones
7818 statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
7819 }
7820 } else {
7821 // Execute the appropriate callbacks
7822 jqXHR.always( map[ jqXHR.status ] );
7823 }
7824 }
7825 return this;
7826 },
7827
7828 // Cancel the request
7829 abort: function( statusText ) {
7830 var finalText = statusText || strAbort;
7831 if ( transport ) {
7832 transport.abort( finalText );
7833 }
7834 done( 0, finalText );
7835 return this;
7836 }
7837 };
7838
7839 // Attach deferreds
7840 deferred.promise( jqXHR ).complete = completeDeferred.add;
7841 jqXHR.success = jqXHR.done;
7842 jqXHR.error = jqXHR.fail;
7843
7844 // Remove hash character (#7531: and string promotion)
7845 // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
7846 // Handle falsy url in the settings object (#10093: consistency with old signature)
7847 // We also use the url parameter if available
7848 s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
7849
7850 // Alias method option to type as per ticket #12004
7851 s.type = options.method || options.type || s.method || s.type;
7852
7853 // Extract dataTypes list
7854 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
7855
7856 // A cross-domain request is in order when we have a protocol:host:port mismatch
7857 if ( s.crossDomain == null ) {
7858 parts = rurl.exec( s.url.toLowerCase() );
7859 s.crossDomain = !!( parts &&
7860 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
7861 ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
7862 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
7863 );
7864 }
7865
7866 // Convert data if not already a string
7867 if ( s.data && s.processData && typeof s.data !== "string" ) {
7868 s.data = jQuery.param( s.data, s.traditional );
7869 }
7870
7871 // Apply prefilters
7872 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
7873
7874 // If request was aborted inside a prefilter, stop there
7875 if ( state === 2 ) {
7876 return jqXHR;
7877 }
7878
7879 // We can fire global events as of now if asked to
7880 fireGlobals = s.global;
7881
7882 // Watch for a new set of requests
7883 if ( fireGlobals && jQuery.active++ === 0 ) {
7884 jQuery.event.trigger("ajaxStart");
7885 }
7886
7887 // Uppercase the type
7888 s.type = s.type.toUpperCase();
7889
7890 // Determine if request has content
7891 s.hasContent = !rnoContent.test( s.type );
7892
7893 // Save the URL in case we're toying with the If-Modified-Since
7894 // and/or If-None-Match header later on
7895 cacheURL = s.url;
7896
7897 // More options handling for requests with no content
7898 if ( !s.hasContent ) {
7899
7900 // If data is available, append data to url
7901 if ( s.data ) {
7902 cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
7903 // #9682: remove data so that it's not used in an eventual retry
7904 delete s.data;
7905 }
7906
7907 // Add anti-cache in url if needed
7908 if ( s.cache === false ) {
7909 s.url = rts.test( cacheURL ) ?
7910
7911 // If there is already a '_' parameter, set its value
7912 cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
7913
7914 // Otherwise add one to the end
7915 cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
7916 }
7917 }
7918
7919 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
7920 if ( s.ifModified ) {
7921 if ( jQuery.lastModified[ cacheURL ] ) {
7922 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
7923 }
7924 if ( jQuery.etag[ cacheURL ] ) {
7925 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
7926 }
7927 }
7928
7929 // Set the correct header, if data is being sent
7930 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
7931 jqXHR.setRequestHeader( "Content-Type", s.contentType );
7932 }
7933
7934 // Set the Accepts header for the server, depending on the dataType
7935 jqXHR.setRequestHeader(
7936 "Accept",
7937 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
7938 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
7939 s.accepts[ "*" ]
7940 );
7941
7942 // Check for headers option
7943 for ( i in s.headers ) {
7944 jqXHR.setRequestHeader( i, s.headers[ i ] );
7945 }
7946
7947 // Allow custom headers/mimetypes and early abort
7948 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
7949 // Abort if not done already and return
7950 return jqXHR.abort();
7951 }
7952
7953 // aborting is no longer a cancellation
7954 strAbort = "abort";
7955
7956 // Install callbacks on deferreds
7957 for ( i in { success: 1, error: 1, complete: 1 } ) {
7958 jqXHR[ i ]( s[ i ] );
7959 }
7960
7961 // Get transport
7962 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
7963
7964 // If no transport, we auto-abort
7965 if ( !transport ) {
7966 done( -1, "No Transport" );
7967 } else {
7968 jqXHR.readyState = 1;
7969
7970 // Send global event
7971 if ( fireGlobals ) {
7972 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
7973 }
7974 // Timeout
7975 if ( s.async && s.timeout > 0 ) {
7976 timeoutTimer = setTimeout(function() {
7977 jqXHR.abort("timeout");
7978 }, s.timeout );
7979 }
7980
7981 try {
7982 state = 1;
7983 transport.send( requestHeaders, done );
7984 } catch ( e ) {
7985 // Propagate exception as error if not done
7986 if ( state < 2 ) {
7987 done( -1, e );
7988 // Simply rethrow otherwise
7989 } else {
7990 throw e;
7991 }
7992 }
7993 }
7994
7995 // Callback for when everything is done
7996 function done( status, nativeStatusText, responses, headers ) {
7997 var isSuccess, success, error, response, modified,
7998 statusText = nativeStatusText;
7999
8000 // Called once
8001 if ( state === 2 ) {
8002 return;
8003 }
8004
8005 // State is "done" now
8006 state = 2;
8007
8008 // Clear timeout if it exists
8009 if ( timeoutTimer ) {
8010 clearTimeout( timeoutTimer );
8011 }
8012
8013 // Dereference transport for early garbage collection
8014 // (no matter how long the jqXHR object will be used)
8015 transport = undefined;
8016
8017 // Cache response headers
8018 responseHeadersString = headers || "";
8019
8020 // Set readyState
8021 jqXHR.readyState = status > 0 ? 4 : 0;
8022
8023 // Get response data
8024 if ( responses ) {
8025 response = ajaxHandleResponses( s, jqXHR, responses );
8026 }
8027
8028 // If successful, handle type chaining
8029 if ( status >= 200 && status < 300 || status === 304 ) {
8030
8031 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
8032 if ( s.ifModified ) {
8033 modified = jqXHR.getResponseHeader("Last-Modified");
8034 if ( modified ) {
8035 jQuery.lastModified[ cacheURL ] = modified;
8036 }
8037 modified = jqXHR.getResponseHeader("etag");
8038 if ( modified ) {
8039 jQuery.etag[ cacheURL ] = modified;
8040 }
8041 }
8042
8043 // if no content
8044 if ( status === 204 ) {
8045 isSuccess = true;
8046 statusText = "nocontent";
8047
8048 // if not modified
8049 } else if ( status === 304 ) {
8050 isSuccess = true;
8051 statusText = "notmodified";
8052
8053 // If we have data, let's convert it
8054 } else {
8055 isSuccess = ajaxConvert( s, response );
8056 statusText = isSuccess.state;
8057 success = isSuccess.data;
8058 error = isSuccess.error;
8059 isSuccess = !error;
8060 }
8061 } else {
8062 // We extract error from statusText
8063 // then normalize statusText and status for non-aborts
8064 error = statusText;
8065 if ( status || !statusText ) {
8066 statusText = "error";
8067 if ( status < 0 ) {
8068 status = 0;
8069 }
8070 }
8071 }
8072
8073 // Set data for the fake xhr object
8074 jqXHR.status = status;
8075 jqXHR.statusText = ( nativeStatusText || statusText ) + "";
8076
8077 // Success/Error
8078 if ( isSuccess ) {
8079 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
8080 } else {
8081 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
8082 }
8083
8084 // Status-dependent callbacks
8085 jqXHR.statusCode( statusCode );
8086 statusCode = undefined;
8087
8088 if ( fireGlobals ) {
8089 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
8090 [ jqXHR, s, isSuccess ? success : error ] );
8091 }
8092
8093 // Complete
8094 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
8095
8096 if ( fireGlobals ) {
8097 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
8098 // Handle the global AJAX counter
8099 if ( !( --jQuery.active ) ) {
8100 jQuery.event.trigger("ajaxStop");
8101 }
8102 }
8103 }
8104
8105 return jqXHR;
8106 },
8107
8108 getScript: function( url, callback ) {
8109 return jQuery.get( url, undefined, callback, "script" );
8110 },
8111
8112 getJSON: function( url, data, callback ) {
8113 return jQuery.get( url, data, callback, "json" );
8114 }
8115});
8116
8117/* Handles responses to an ajax request:
8118 * - sets all responseXXX fields accordingly
8119 * - finds the right dataType (mediates between content-type and expected dataType)
8120 * - returns the corresponding response
8121 */
8122function ajaxHandleResponses( s, jqXHR, responses ) {
8123 var firstDataType, ct, finalDataType, type,
8124 contents = s.contents,
8125 dataTypes = s.dataTypes,
8126 responseFields = s.responseFields;
8127
8128 // Fill responseXXX fields
8129 for ( type in responseFields ) {
8130 if ( type in responses ) {
8131 jqXHR[ responseFields[type] ] = responses[ type ];
8132 }
8133 }
8134
8135 // Remove auto dataType and get content-type in the process
8136 while( dataTypes[ 0 ] === "*" ) {
8137 dataTypes.shift();
8138 if ( ct === undefined ) {
8139 ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
8140 }
8141 }
8142
8143 // Check if we're dealing with a known content-type
8144 if ( ct ) {
8145 for ( type in contents ) {
8146 if ( contents[ type ] && contents[ type ].test( ct ) ) {
8147 dataTypes.unshift( type );
8148 break;
8149 }
8150 }
8151 }
8152
8153 // Check to see if we have a response for the expected dataType
8154 if ( dataTypes[ 0 ] in responses ) {
8155 finalDataType = dataTypes[ 0 ];
8156 } else {
8157 // Try convertible dataTypes
8158 for ( type in responses ) {
8159 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
8160 finalDataType = type;
8161 break;
8162 }
8163 if ( !firstDataType ) {
8164 firstDataType = type;
8165 }
8166 }
8167 // Or just use first one
8168 finalDataType = finalDataType || firstDataType;
8169 }
8170
8171 // If we found a dataType
8172 // We add the dataType to the list if needed
8173 // and return the corresponding response
8174 if ( finalDataType ) {
8175 if ( finalDataType !== dataTypes[ 0 ] ) {
8176 dataTypes.unshift( finalDataType );
8177 }
8178 return responses[ finalDataType ];
8179 }
8180}
8181
8182// Chain conversions given the request and the original response
8183function ajaxConvert( s, response ) {
8184 var conv2, current, conv, tmp,
8185 converters = {},
8186 i = 0,
8187 // Work with a copy of dataTypes in case we need to modify it for conversion
8188 dataTypes = s.dataTypes.slice(),
8189 prev = dataTypes[ 0 ];
8190
8191 // Apply the dataFilter if provided
8192 if ( s.dataFilter ) {
8193 response = s.dataFilter( response, s.dataType );
8194 }
8195
8196 // Create converters map with lowercased keys
8197 if ( dataTypes[ 1 ] ) {
8198 for ( conv in s.converters ) {
8199 converters[ conv.toLowerCase() ] = s.converters[ conv ];
8200 }
8201 }
8202
8203 // Convert to each sequential dataType, tolerating list modification
8204 for ( ; (current = dataTypes[++i]); ) {
8205
8206 // There's only work to do if current dataType is non-auto
8207 if ( current !== "*" ) {
8208
8209 // Convert response if prev dataType is non-auto and differs from current
8210 if ( prev !== "*" && prev !== current ) {
8211
8212 // Seek a direct converter
8213 conv = converters[ prev + " " + current ] || converters[ "* " + current ];
8214
8215 // If none found, seek a pair
8216 if ( !conv ) {
8217 for ( conv2 in converters ) {
8218
8219 // If conv2 outputs current
8220 tmp = conv2.split(" ");
8221 if ( tmp[ 1 ] === current ) {
8222
8223 // If prev can be converted to accepted input
8224 conv = converters[ prev + " " + tmp[ 0 ] ] ||
8225 converters[ "* " + tmp[ 0 ] ];
8226 if ( conv ) {
8227 // Condense equivalence converters
8228 if ( conv === true ) {
8229 conv = converters[ conv2 ];
8230
8231 // Otherwise, insert the intermediate dataType
8232 } else if ( converters[ conv2 ] !== true ) {
8233 current = tmp[ 0 ];
8234 dataTypes.splice( i--, 0, current );
8235 }
8236
8237 break;
8238 }
8239 }
8240 }
8241 }
8242
8243 // Apply converter (if not an equivalence)
8244 if ( conv !== true ) {
8245
8246 // Unless errors are allowed to bubble, catch and return them
8247 if ( conv && s["throws"] ) {
8248 response = conv( response );
8249 } else {
8250 try {
8251 response = conv( response );
8252 } catch ( e ) {
8253 return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
8254 }
8255 }
8256 }
8257 }
8258
8259 // Update prev for next iteration
8260 prev = current;
8261 }
8262 }
8263
8264 return { state: "success", data: response };
8265}
8266// Install script dataType
8267jQuery.ajaxSetup({
8268 accepts: {
8269 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8270 },
8271 contents: {
8272 script: /(?:java|ecma)script/
8273 },
8274 converters: {
8275 "text script": function( text ) {
8276 jQuery.globalEval( text );
8277 return text;
8278 }
8279 }
8280});
8281
8282// Handle cache's special case and global
8283jQuery.ajaxPrefilter( "script", function( s ) {
8284 if ( s.cache === undefined ) {
8285 s.cache = false;
8286 }
8287 if ( s.crossDomain ) {
8288 s.type = "GET";
8289 s.global = false;
8290 }
8291});
8292
8293// Bind script tag hack transport
8294jQuery.ajaxTransport( "script", function(s) {
8295
8296 // This transport only deals with cross domain requests
8297 if ( s.crossDomain ) {
8298
8299 var script,
8300 head = document.head || jQuery("head")[0] || document.documentElement;
8301
8302 return {
8303
8304 send: function( _, callback ) {
8305
8306 script = document.createElement("script");
8307
8308 script.async = true;
8309
8310 if ( s.scriptCharset ) {
8311 script.charset = s.scriptCharset;
8312 }
8313
8314 script.src = s.url;
8315
8316 // Attach handlers for all browsers
8317 script.onload = script.onreadystatechange = function( _, isAbort ) {
8318
8319 if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
8320
8321 // Handle memory leak in IE
8322 script.onload = script.onreadystatechange = null;
8323
8324 // Remove the script
8325 if ( script.parentNode ) {
8326 script.parentNode.removeChild( script );
8327 }
8328
8329 // Dereference the script
8330 script = null;
8331
8332 // Callback if not abort
8333 if ( !isAbort ) {
8334 callback( 200, "success" );
8335 }
8336 }
8337 };
8338
8339 // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
8340 // Use native DOM manipulation to avoid our domManip AJAX trickery
8341 head.insertBefore( script, head.firstChild );
8342 },
8343
8344 abort: function() {
8345 if ( script ) {
8346 script.onload( undefined, true );
8347 }
8348 }
8349 };
8350 }
8351});
8352var oldCallbacks = [],
8353 rjsonp = /(=)\?(?=&|$)|\?\?/;
8354
8355// Default jsonp settings
8356jQuery.ajaxSetup({
8357 jsonp: "callback",
8358 jsonpCallback: function() {
8359 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
8360 this[ callback ] = true;
8361 return callback;
8362 }
8363});
8364
8365// Detect, normalize options and install callbacks for jsonp requests
8366jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8367
8368 var callbackName, overwritten, responseContainer,
8369 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
8370 "url" :
8371 typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
8372 );
8373
8374 // Handle iff the expected data type is "jsonp" or we have a parameter to set
8375 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
8376
8377 // Get callback name, remembering preexisting value associated with it
8378 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8379 s.jsonpCallback() :
8380 s.jsonpCallback;
8381
8382 // Insert callback into url or form data
8383 if ( jsonProp ) {
8384 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
8385 } else if ( s.jsonp !== false ) {
8386 s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8387 }
8388
8389 // Use data converter to retrieve json after script execution
8390 s.converters["script json"] = function() {
8391 if ( !responseContainer ) {
8392 jQuery.error( callbackName + " was not called" );
8393 }
8394 return responseContainer[ 0 ];
8395 };
8396
8397 // force json dataType
8398 s.dataTypes[ 0 ] = "json";
8399
8400 // Install callback
8401 overwritten = window[ callbackName ];
8402 window[ callbackName ] = function() {
8403 responseContainer = arguments;
8404 };
8405
8406 // Clean-up function (fires after converters)
8407 jqXHR.always(function() {
8408 // Restore preexisting value
8409 window[ callbackName ] = overwritten;
8410
8411 // Save back as free
8412 if ( s[ callbackName ] ) {
8413 // make sure that re-using the options doesn't screw things around
8414 s.jsonpCallback = originalSettings.jsonpCallback;
8415
8416 // save the callback name for future use
8417 oldCallbacks.push( callbackName );
8418 }
8419
8420 // Call if it was a function and we have a response
8421 if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8422 overwritten( responseContainer[ 0 ] );
8423 }
8424
8425 responseContainer = overwritten = undefined;
8426 });
8427
8428 // Delegate to script
8429 return "script";
8430 }
8431});
8432var xhrCallbacks, xhrSupported,
8433 xhrId = 0,
8434 // #5280: Internet Explorer will keep connections alive if we don't abort on unload
8435 xhrOnUnloadAbort = window.ActiveXObject && function() {
8436 // Abort all pending requests
8437 var key;
8438 for ( key in xhrCallbacks ) {
8439 xhrCallbacks[ key ]( undefined, true );
8440 }
8441 };
8442
8443// Functions to create xhrs
8444function createStandardXHR() {
8445 try {
8446 return new window.XMLHttpRequest();
8447 } catch( e ) {}
8448}
8449
8450function createActiveXHR() {
8451 try {
8452 return new window.ActiveXObject("Microsoft.XMLHTTP");
8453 } catch( e ) {}
8454}
8455
8456// Create the request object
8457// (This is still attached to ajaxSettings for backward compatibility)
8458jQuery.ajaxSettings.xhr = window.ActiveXObject ?
8459 /* Microsoft failed to properly
8460 * implement the XMLHttpRequest in IE7 (can't request local files),
8461 * so we use the ActiveXObject when it is available
8462 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
8463 * we need a fallback.
8464 */
8465 function() {
8466 return !this.isLocal && createStandardXHR() || createActiveXHR();
8467 } :
8468 // For all other browsers, use the standard XMLHttpRequest object
8469 createStandardXHR;
8470
8471// Determine support properties
8472xhrSupported = jQuery.ajaxSettings.xhr();
8473jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
8474xhrSupported = jQuery.support.ajax = !!xhrSupported;
8475
8476// Create transport if the browser can provide an xhr
8477if ( xhrSupported ) {
8478
8479 jQuery.ajaxTransport(function( s ) {
8480 // Cross domain only allowed if supported through XMLHttpRequest
8481 if ( !s.crossDomain || jQuery.support.cors ) {
8482
8483 var callback;
8484
8485 return {
8486 send: function( headers, complete ) {
8487
8488 // Get a new xhr
8489 var handle, i,
8490 xhr = s.xhr();
8491
8492 // Open the socket
8493 // Passing null username, generates a login popup on Opera (#2865)
8494 if ( s.username ) {
8495 xhr.open( s.type, s.url, s.async, s.username, s.password );
8496 } else {
8497 xhr.open( s.type, s.url, s.async );
8498 }
8499
8500 // Apply custom fields if provided
8501 if ( s.xhrFields ) {
8502 for ( i in s.xhrFields ) {
8503 xhr[ i ] = s.xhrFields[ i ];
8504 }
8505 }
8506
8507 // Override mime type if needed
8508 if ( s.mimeType && xhr.overrideMimeType ) {
8509 xhr.overrideMimeType( s.mimeType );
8510 }
8511
8512 // X-Requested-With header
8513 // For cross-domain requests, seeing as conditions for a preflight are
8514 // akin to a jigsaw puzzle, we simply never set it to be sure.
8515 // (it can always be set on a per-request basis or even using ajaxSetup)
8516 // For same-domain requests, won't change header if already provided.
8517 if ( !s.crossDomain && !headers["X-Requested-With"] ) {
8518 headers["X-Requested-With"] = "XMLHttpRequest";
8519 }
8520
8521 // Need an extra try/catch for cross domain requests in Firefox 3
8522 try {
8523 for ( i in headers ) {
8524 xhr.setRequestHeader( i, headers[ i ] );
8525 }
8526 } catch( err ) {}
8527
8528 // Do send the request
8529 // This may raise an exception which is actually
8530 // handled in jQuery.ajax (so no try/catch here)
8531 xhr.send( ( s.hasContent && s.data ) || null );
8532
8533 // Listener
8534 callback = function( _, isAbort ) {
8535 var status, responseHeaders, statusText, responses;
8536
8537 // Firefox throws exceptions when accessing properties
8538 // of an xhr when a network error occurred
8539 // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
8540 try {
8541
8542 // Was never called and is aborted or complete
8543 if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
8544
8545 // Only called once
8546 callback = undefined;
8547
8548 // Do not keep as active anymore
8549 if ( handle ) {
8550 xhr.onreadystatechange = jQuery.noop;
8551 if ( xhrOnUnloadAbort ) {
8552 delete xhrCallbacks[ handle ];
8553 }
8554 }
8555
8556 // If it's an abort
8557 if ( isAbort ) {
8558 // Abort it manually if needed
8559 if ( xhr.readyState !== 4 ) {
8560 xhr.abort();
8561 }
8562 } else {
8563 responses = {};
8564 status = xhr.status;
8565 responseHeaders = xhr.getAllResponseHeaders();
8566
8567 // When requesting binary data, IE6-9 will throw an exception
8568 // on any attempt to access responseText (#11426)
8569 if ( typeof xhr.responseText === "string" ) {
8570 responses.text = xhr.responseText;
8571 }
8572
8573 // Firefox throws an exception when accessing
8574 // statusText for faulty cross-domain requests
8575 try {
8576 statusText = xhr.statusText;
8577 } catch( e ) {
8578 // We normalize with Webkit giving an empty statusText
8579 statusText = "";
8580 }
8581
8582 // Filter status for non standard behaviors
8583
8584 // If the request is local and we have data: assume a success
8585 // (success with no data won't get notified, that's the best we
8586 // can do given current implementations)
8587 if ( !status && s.isLocal && !s.crossDomain ) {
8588 status = responses.text ? 200 : 404;
8589 // IE - #1450: sometimes returns 1223 when it should be 204
8590 } else if ( status === 1223 ) {
8591 status = 204;
8592 }
8593 }
8594 }
8595 } catch( firefoxAccessException ) {
8596 if ( !isAbort ) {
8597 complete( -1, firefoxAccessException );
8598 }
8599 }
8600
8601 // Call complete if needed
8602 if ( responses ) {
8603 complete( status, statusText, responses, responseHeaders );
8604 }
8605 };
8606
8607 if ( !s.async ) {
8608 // if we're in sync mode we fire the callback
8609 callback();
8610 } else if ( xhr.readyState === 4 ) {
8611 // (IE6 & IE7) if it's in cache and has been
8612 // retrieved directly we need to fire the callback
8613 setTimeout( callback );
8614 } else {
8615 handle = ++xhrId;
8616 if ( xhrOnUnloadAbort ) {
8617 // Create the active xhrs callbacks list if needed
8618 // and attach the unload handler
8619 if ( !xhrCallbacks ) {
8620 xhrCallbacks = {};
8621 jQuery( window ).unload( xhrOnUnloadAbort );
8622 }
8623 // Add to list of active xhrs callbacks
8624 xhrCallbacks[ handle ] = callback;
8625 }
8626 xhr.onreadystatechange = callback;
8627 }
8628 },
8629
8630 abort: function() {
8631 if ( callback ) {
8632 callback( undefined, true );
8633 }
8634 }
8635 };
8636 }
8637 });
8638}
8639var fxNow, timerId,
8640 rfxtypes = /^(?:toggle|show|hide)$/,
8641 rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
8642 rrun = /queueHooks$/,
8643 animationPrefilters = [ defaultPrefilter ],
8644 tweeners = {
8645 "*": [function( prop, value ) {
8646 var end, unit,
8647 tween = this.createTween( prop, value ),
8648 parts = rfxnum.exec( value ),
8649 target = tween.cur(),
8650 start = +target || 0,
8651 scale = 1,
8652 maxIterations = 20;
8653
8654 if ( parts ) {
8655 end = +parts[2];
8656 unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
8657
8658 // We need to compute starting value
8659 if ( unit !== "px" && start ) {
8660 // Iteratively approximate from a nonzero starting point
8661 // Prefer the current property, because this process will be trivial if it uses the same units
8662 // Fallback to end or a simple constant
8663 start = jQuery.css( tween.elem, prop, true ) || end || 1;
8664
8665 do {
8666 // If previous iteration zeroed out, double until we get *something*
8667 // Use a string for doubling factor so we don't accidentally see scale as unchanged below
8668 scale = scale || ".5";
8669
8670 // Adjust and apply
8671 start = start / scale;
8672 jQuery.style( tween.elem, prop, start + unit );
8673
8674 // Update scale, tolerating zero or NaN from tween.cur()
8675 // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
8676 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
8677 }
8678
8679 tween.unit = unit;
8680 tween.start = start;
8681 // If a +=/-= token was provided, we're doing a relative animation
8682 tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
8683 }
8684 return tween;
8685 }]
8686 };
8687
8688// Animations created synchronously will run synchronously
8689function createFxNow() {
8690 setTimeout(function() {
8691 fxNow = undefined;
8692 });
8693 return ( fxNow = jQuery.now() );
8694}
8695
8696function createTweens( animation, props ) {
8697 jQuery.each( props, function( prop, value ) {
8698 var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
8699 index = 0,
8700 length = collection.length;
8701 for ( ; index < length; index++ ) {
8702 if ( collection[ index ].call( animation, prop, value ) ) {
8703
8704 // we're done with this property
8705 return;
8706 }
8707 }
8708 });
8709}
8710
8711function Animation( elem, properties, options ) {
8712 var result,
8713 stopped,
8714 index = 0,
8715 length = animationPrefilters.length,
8716 deferred = jQuery.Deferred().always( function() {
8717 // don't match elem in the :animated selector
8718 delete tick.elem;
8719 }),
8720 tick = function() {
8721 if ( stopped ) {
8722 return false;
8723 }
8724 var currentTime = fxNow || createFxNow(),
8725 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
8726 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
8727 temp = remaining / animation.duration || 0,
8728 percent = 1 - temp,
8729 index = 0,
8730 length = animation.tweens.length;
8731
8732 for ( ; index < length ; index++ ) {
8733 animation.tweens[ index ].run( percent );
8734 }
8735
8736 deferred.notifyWith( elem, [ animation, percent, remaining ]);
8737
8738 if ( percent < 1 && length ) {
8739 return remaining;
8740 } else {
8741 deferred.resolveWith( elem, [ animation ] );
8742 return false;
8743 }
8744 },
8745 animation = deferred.promise({
8746 elem: elem,
8747 props: jQuery.extend( {}, properties ),
8748 opts: jQuery.extend( true, { specialEasing: {} }, options ),
8749 originalProperties: properties,
8750 originalOptions: options,
8751 startTime: fxNow || createFxNow(),
8752 duration: options.duration,
8753 tweens: [],
8754 createTween: function( prop, end ) {
8755 var tween = jQuery.Tween( elem, animation.opts, prop, end,
8756 animation.opts.specialEasing[ prop ] || animation.opts.easing );
8757 animation.tweens.push( tween );
8758 return tween;
8759 },
8760 stop: function( gotoEnd ) {
8761 var index = 0,
8762 // if we are going to the end, we want to run all the tweens
8763 // otherwise we skip this part
8764 length = gotoEnd ? animation.tweens.length : 0;
8765 if ( stopped ) {
8766 return this;
8767 }
8768 stopped = true;
8769 for ( ; index < length ; index++ ) {
8770 animation.tweens[ index ].run( 1 );
8771 }
8772
8773 // resolve when we played the last frame
8774 // otherwise, reject
8775 if ( gotoEnd ) {
8776 deferred.resolveWith( elem, [ animation, gotoEnd ] );
8777 } else {
8778 deferred.rejectWith( elem, [ animation, gotoEnd ] );
8779 }
8780 return this;
8781 }
8782 }),
8783 props = animation.props;
8784
8785 propFilter( props, animation.opts.specialEasing );
8786
8787 for ( ; index < length ; index++ ) {
8788 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
8789 if ( result ) {
8790 return result;
8791 }
8792 }
8793
8794 createTweens( animation, props );
8795
8796 if ( jQuery.isFunction( animation.opts.start ) ) {
8797 animation.opts.start.call( elem, animation );
8798 }
8799
8800 jQuery.fx.timer(
8801 jQuery.extend( tick, {
8802 elem: elem,
8803 anim: animation,
8804 queue: animation.opts.queue
8805 })
8806 );
8807
8808 // attach callbacks from options
8809 return animation.progress( animation.opts.progress )
8810 .done( animation.opts.done, animation.opts.complete )
8811 .fail( animation.opts.fail )
8812 .always( animation.opts.always );
8813}
8814
8815function propFilter( props, specialEasing ) {
8816 var value, name, index, easing, hooks;
8817
8818 // camelCase, specialEasing and expand cssHook pass
8819 for ( index in props ) {
8820 name = jQuery.camelCase( index );
8821 easing = specialEasing[ name ];
8822 value = props[ index ];
8823 if ( jQuery.isArray( value ) ) {
8824 easing = value[ 1 ];
8825 value = props[ index ] = value[ 0 ];
8826 }
8827
8828 if ( index !== name ) {
8829 props[ name ] = value;
8830 delete props[ index ];
8831 }
8832
8833 hooks = jQuery.cssHooks[ name ];
8834 if ( hooks && "expand" in hooks ) {
8835 value = hooks.expand( value );
8836 delete props[ name ];
8837
8838 // not quite $.extend, this wont overwrite keys already present.
8839 // also - reusing 'index' from above because we have the correct "name"
8840 for ( index in value ) {
8841 if ( !( index in props ) ) {
8842 props[ index ] = value[ index ];
8843 specialEasing[ index ] = easing;
8844 }
8845 }
8846 } else {
8847 specialEasing[ name ] = easing;
8848 }
8849 }
8850}
8851
8852jQuery.Animation = jQuery.extend( Animation, {
8853
8854 tweener: function( props, callback ) {
8855 if ( jQuery.isFunction( props ) ) {
8856 callback = props;
8857 props = [ "*" ];
8858 } else {
8859 props = props.split(" ");
8860 }
8861
8862 var prop,
8863 index = 0,
8864 length = props.length;
8865
8866 for ( ; index < length ; index++ ) {
8867 prop = props[ index ];
8868 tweeners[ prop ] = tweeners[ prop ] || [];
8869 tweeners[ prop ].unshift( callback );
8870 }
8871 },
8872
8873 prefilter: function( callback, prepend ) {
8874 if ( prepend ) {
8875 animationPrefilters.unshift( callback );
8876 } else {
8877 animationPrefilters.push( callback );
8878 }
8879 }
8880});
8881
8882function defaultPrefilter( elem, props, opts ) {
8883 /*jshint validthis:true */
8884 var prop, index, length,
8885 value, dataShow, toggle,
8886 tween, hooks, oldfire,
8887 anim = this,
8888 style = elem.style,
8889 orig = {},
8890 handled = [],
8891 hidden = elem.nodeType && isHidden( elem );
8892
8893 // handle queue: false promises
8894 if ( !opts.queue ) {
8895 hooks = jQuery._queueHooks( elem, "fx" );
8896 if ( hooks.unqueued == null ) {
8897 hooks.unqueued = 0;
8898 oldfire = hooks.empty.fire;
8899 hooks.empty.fire = function() {
8900 if ( !hooks.unqueued ) {
8901 oldfire();
8902 }
8903 };
8904 }
8905 hooks.unqueued++;
8906
8907 anim.always(function() {
8908 // doing this makes sure that the complete handler will be called
8909 // before this completes
8910 anim.always(function() {
8911 hooks.unqueued--;
8912 if ( !jQuery.queue( elem, "fx" ).length ) {
8913 hooks.empty.fire();
8914 }
8915 });
8916 });
8917 }
8918
8919 // height/width overflow pass
8920 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
8921 // Make sure that nothing sneaks out
8922 // Record all 3 overflow attributes because IE does not
8923 // change the overflow attribute when overflowX and
8924 // overflowY are set to the same value
8925 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
8926
8927 // Set display property to inline-block for height/width
8928 // animations on inline elements that are having width/height animated
8929 if ( jQuery.css( elem, "display" ) === "inline" &&
8930 jQuery.css( elem, "float" ) === "none" ) {
8931
8932 // inline-level elements accept inline-block;
8933 // block-level elements need to be inline with layout
8934 if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
8935 style.display = "inline-block";
8936
8937 } else {
8938 style.zoom = 1;
8939 }
8940 }
8941 }
8942
8943 if ( opts.overflow ) {
8944 style.overflow = "hidden";
8945 if ( !jQuery.support.shrinkWrapBlocks ) {
8946 anim.always(function() {
8947 style.overflow = opts.overflow[ 0 ];
8948 style.overflowX = opts.overflow[ 1 ];
8949 style.overflowY = opts.overflow[ 2 ];
8950 });
8951 }
8952 }
8953
8954
8955 // show/hide pass
8956 for ( index in props ) {
8957 value = props[ index ];
8958 if ( rfxtypes.exec( value ) ) {
8959 delete props[ index ];
8960 toggle = toggle || value === "toggle";
8961 if ( value === ( hidden ? "hide" : "show" ) ) {
8962 continue;
8963 }
8964 handled.push( index );
8965 }
8966 }
8967
8968 length = handled.length;
8969 if ( length ) {
8970 dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
8971 if ( "hidden" in dataShow ) {
8972 hidden = dataShow.hidden;
8973 }
8974
8975 // store state if its toggle - enables .stop().toggle() to "reverse"
8976 if ( toggle ) {
8977 dataShow.hidden = !hidden;
8978 }
8979 if ( hidden ) {
8980 jQuery( elem ).show();
8981 } else {
8982 anim.done(function() {
8983 jQuery( elem ).hide();
8984 });
8985 }
8986 anim.done(function() {
8987 var prop;
8988 jQuery._removeData( elem, "fxshow" );
8989 for ( prop in orig ) {
8990 jQuery.style( elem, prop, orig[ prop ] );
8991 }
8992 });
8993 for ( index = 0 ; index < length ; index++ ) {
8994 prop = handled[ index ];
8995 tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
8996 orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
8997
8998 if ( !( prop in dataShow ) ) {
8999 dataShow[ prop ] = tween.start;
9000 if ( hidden ) {
9001 tween.end = tween.start;
9002 tween.start = prop === "width" || prop === "height" ? 1 : 0;
9003 }
9004 }
9005 }
9006 }
9007}
9008
9009function Tween( elem, options, prop, end, easing ) {
9010 return new Tween.prototype.init( elem, options, prop, end, easing );
9011}
9012jQuery.Tween = Tween;
9013
9014Tween.prototype = {
9015 constructor: Tween,
9016 init: function( elem, options, prop, end, easing, unit ) {
9017 this.elem = elem;
9018 this.prop = prop;
9019 this.easing = easing || "swing";
9020 this.options = options;
9021 this.start = this.now = this.cur();
9022 this.end = end;
9023 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
9024 },
9025 cur: function() {
9026 var hooks = Tween.propHooks[ this.prop ];
9027
9028 return hooks && hooks.get ?
9029 hooks.get( this ) :
9030 Tween.propHooks._default.get( this );
9031 },
9032 run: function( percent ) {
9033 var eased,
9034 hooks = Tween.propHooks[ this.prop ];
9035
9036 if ( this.options.duration ) {
9037 this.pos = eased = jQuery.easing[ this.easing ](
9038 percent, this.options.duration * percent, 0, 1, this.options.duration
9039 );
9040 } else {
9041 this.pos = eased = percent;
9042 }
9043 this.now = ( this.end - this.start ) * eased + this.start;
9044
9045 if ( this.options.step ) {
9046 this.options.step.call( this.elem, this.now, this );
9047 }
9048
9049 if ( hooks && hooks.set ) {
9050 hooks.set( this );
9051 } else {
9052 Tween.propHooks._default.set( this );
9053 }
9054 return this;
9055 }
9056};
9057
9058Tween.prototype.init.prototype = Tween.prototype;
9059
9060Tween.propHooks = {
9061 _default: {
9062 get: function( tween ) {
9063 var result;
9064
9065 if ( tween.elem[ tween.prop ] != null &&
9066 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
9067 return tween.elem[ tween.prop ];
9068 }
9069
9070 // passing an empty string as a 3rd parameter to .css will automatically
9071 // attempt a parseFloat and fallback to a string if the parse fails
9072 // so, simple values such as "10px" are parsed to Float.
9073 // complex values such as "rotate(1rad)" are returned as is.
9074 result = jQuery.css( tween.elem, tween.prop, "" );
9075 // Empty strings, null, undefined and "auto" are converted to 0.
9076 return !result || result === "auto" ? 0 : result;
9077 },
9078 set: function( tween ) {
9079 // use step hook for back compat - use cssHook if its there - use .style if its
9080 // available and use plain properties where available
9081 if ( jQuery.fx.step[ tween.prop ] ) {
9082 jQuery.fx.step[ tween.prop ]( tween );
9083 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
9084 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
9085 } else {
9086 tween.elem[ tween.prop ] = tween.now;
9087 }
9088 }
9089 }
9090};
9091
9092// Remove in 2.0 - this supports IE8's panic based approach
9093// to setting things on disconnected nodes
9094
9095Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
9096 set: function( tween ) {
9097 if ( tween.elem.nodeType && tween.elem.parentNode ) {
9098 tween.elem[ tween.prop ] = tween.now;
9099 }
9100 }
9101};
9102
9103jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
9104 var cssFn = jQuery.fn[ name ];
9105 jQuery.fn[ name ] = function( speed, easing, callback ) {
9106 return speed == null || typeof speed === "boolean" ?
9107 cssFn.apply( this, arguments ) :
9108 this.animate( genFx( name, true ), speed, easing, callback );
9109 };
9110});
9111
9112jQuery.fn.extend({
9113 fadeTo: function( speed, to, easing, callback ) {
9114
9115 // show any hidden elements after setting opacity to 0
9116 return this.filter( isHidden ).css( "opacity", 0 ).show()
9117
9118 // animate to the value specified
9119 .end().animate({ opacity: to }, speed, easing, callback );
9120 },
9121 animate: function( prop, speed, easing, callback ) {
9122 var empty = jQuery.isEmptyObject( prop ),
9123 optall = jQuery.speed( speed, easing, callback ),
9124 doAnimation = function() {
9125 // Operate on a copy of prop so per-property easing won't be lost
9126 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
9127 doAnimation.finish = function() {
9128 anim.stop( true );
9129 };
9130 // Empty animations, or finishing resolves immediately
9131 if ( empty || jQuery._data( this, "finish" ) ) {
9132 anim.stop( true );
9133 }
9134 };
9135 doAnimation.finish = doAnimation;
9136
9137 return empty || optall.queue === false ?
9138 this.each( doAnimation ) :
9139 this.queue( optall.queue, doAnimation );
9140 },
9141 stop: function( type, clearQueue, gotoEnd ) {
9142 var stopQueue = function( hooks ) {
9143 var stop = hooks.stop;
9144 delete hooks.stop;
9145 stop( gotoEnd );
9146 };
9147
9148 if ( typeof type !== "string" ) {
9149 gotoEnd = clearQueue;
9150 clearQueue = type;
9151 type = undefined;
9152 }
9153 if ( clearQueue && type !== false ) {
9154 this.queue( type || "fx", [] );
9155 }
9156
9157 return this.each(function() {
9158 var dequeue = true,
9159 index = type != null && type + "queueHooks",
9160 timers = jQuery.timers,
9161 data = jQuery._data( this );
9162
9163 if ( index ) {
9164 if ( data[ index ] && data[ index ].stop ) {
9165 stopQueue( data[ index ] );
9166 }
9167 } else {
9168 for ( index in data ) {
9169 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
9170 stopQueue( data[ index ] );
9171 }
9172 }
9173 }
9174
9175 for ( index = timers.length; index--; ) {
9176 if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
9177 timers[ index ].anim.stop( gotoEnd );
9178 dequeue = false;
9179 timers.splice( index, 1 );
9180 }
9181 }
9182
9183 // start the next in the queue if the last step wasn't forced
9184 // timers currently will call their complete callbacks, which will dequeue
9185 // but only if they were gotoEnd
9186 if ( dequeue || !gotoEnd ) {
9187 jQuery.dequeue( this, type );
9188 }
9189 });
9190 },
9191 finish: function( type ) {
9192 if ( type !== false ) {
9193 type = type || "fx";
9194 }
9195 return this.each(function() {
9196 var index,
9197 data = jQuery._data( this ),
9198 queue = data[ type + "queue" ],
9199 hooks = data[ type + "queueHooks" ],
9200 timers = jQuery.timers,
9201 length = queue ? queue.length : 0;
9202
9203 // enable finishing flag on private data
9204 data.finish = true;
9205
9206 // empty the queue first
9207 jQuery.queue( this, type, [] );
9208
9209 if ( hooks && hooks.cur && hooks.cur.finish ) {
9210 hooks.cur.finish.call( this );
9211 }
9212
9213 // look for any active animations, and finish them
9214 for ( index = timers.length; index--; ) {
9215 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
9216 timers[ index ].anim.stop( true );
9217 timers.splice( index, 1 );
9218 }
9219 }
9220
9221 // look for any animations in the old queue and finish them
9222 for ( index = 0; index < length; index++ ) {
9223 if ( queue[ index ] && queue[ index ].finish ) {
9224 queue[ index ].finish.call( this );
9225 }
9226 }
9227
9228 // turn off finishing flag
9229 delete data.finish;
9230 });
9231 }
9232});
9233
9234// Generate parameters to create a standard animation
9235function genFx( type, includeWidth ) {
9236 var which,
9237 attrs = { height: type },
9238 i = 0;
9239
9240 // if we include width, step value is 1 to do all cssExpand values,
9241 // if we don't include width, step value is 2 to skip over Left and Right
9242 includeWidth = includeWidth? 1 : 0;
9243 for( ; i < 4 ; i += 2 - includeWidth ) {
9244 which = cssExpand[ i ];
9245 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
9246 }
9247
9248 if ( includeWidth ) {
9249 attrs.opacity = attrs.width = type;
9250 }
9251
9252 return attrs;
9253}
9254
9255// Generate shortcuts for custom animations
9256jQuery.each({
9257 slideDown: genFx("show"),
9258 slideUp: genFx("hide"),
9259 slideToggle: genFx("toggle"),
9260 fadeIn: { opacity: "show" },
9261 fadeOut: { opacity: "hide" },
9262 fadeToggle: { opacity: "toggle" }
9263}, function( name, props ) {
9264 jQuery.fn[ name ] = function( speed, easing, callback ) {
9265 return this.animate( props, speed, easing, callback );
9266 };
9267});
9268
9269jQuery.speed = function( speed, easing, fn ) {
9270 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
9271 complete: fn || !fn && easing ||
9272 jQuery.isFunction( speed ) && speed,
9273 duration: speed,
9274 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
9275 };
9276
9277 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
9278 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
9279
9280 // normalize opt.queue - true/undefined/null -> "fx"
9281 if ( opt.queue == null || opt.queue === true ) {
9282 opt.queue = "fx";
9283 }
9284
9285 // Queueing
9286 opt.old = opt.complete;
9287
9288 opt.complete = function() {
9289 if ( jQuery.isFunction( opt.old ) ) {
9290 opt.old.call( this );
9291 }
9292
9293 if ( opt.queue ) {
9294 jQuery.dequeue( this, opt.queue );
9295 }
9296 };
9297
9298 return opt;
9299};
9300
9301jQuery.easing = {
9302 linear: function( p ) {
9303 return p;
9304 },
9305 swing: function( p ) {
9306 return 0.5 - Math.cos( p*Math.PI ) / 2;
9307 }
9308};
9309
9310jQuery.timers = [];
9311jQuery.fx = Tween.prototype.init;
9312jQuery.fx.tick = function() {
9313 var timer,
9314 timers = jQuery.timers,
9315 i = 0;
9316
9317 fxNow = jQuery.now();
9318
9319 for ( ; i < timers.length; i++ ) {
9320 timer = timers[ i ];
9321 // Checks the timer has not already been removed
9322 if ( !timer() && timers[ i ] === timer ) {
9323 timers.splice( i--, 1 );
9324 }
9325 }
9326
9327 if ( !timers.length ) {
9328 jQuery.fx.stop();
9329 }
9330 fxNow = undefined;
9331};
9332
9333jQuery.fx.timer = function( timer ) {
9334 if ( timer() && jQuery.timers.push( timer ) ) {
9335 jQuery.fx.start();
9336 }
9337};
9338
9339jQuery.fx.interval = 13;
9340
9341jQuery.fx.start = function() {
9342 if ( !timerId ) {
9343 timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
9344 }
9345};
9346
9347jQuery.fx.stop = function() {
9348 clearInterval( timerId );
9349 timerId = null;
9350};
9351
9352jQuery.fx.speeds = {
9353 slow: 600,
9354 fast: 200,
9355 // Default speed
9356 _default: 400
9357};
9358
9359// Back Compat <1.8 extension point
9360jQuery.fx.step = {};
9361
9362if ( jQuery.expr && jQuery.expr.filters ) {
9363 jQuery.expr.filters.animated = function( elem ) {
9364 return jQuery.grep(jQuery.timers, function( fn ) {
9365 return elem === fn.elem;
9366 }).length;
9367 };
9368}
9369jQuery.fn.offset = function( options ) {
9370 if ( arguments.length ) {
9371 return options === undefined ?
9372 this :
9373 this.each(function( i ) {
9374 jQuery.offset.setOffset( this, options, i );
9375 });
9376 }
9377
9378 var docElem, win,
9379 box = { top: 0, left: 0 },
9380 elem = this[ 0 ],
9381 doc = elem && elem.ownerDocument;
9382
9383 if ( !doc ) {
9384 return;
9385 }
9386
9387 docElem = doc.documentElement;
9388
9389 // Make sure it's not a disconnected DOM node
9390 if ( !jQuery.contains( docElem, elem ) ) {
9391 return box;
9392 }
9393
9394 // If we don't have gBCR, just use 0,0 rather than error
9395 // BlackBerry 5, iOS 3 (original iPhone)
9396 if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
9397 box = elem.getBoundingClientRect();
9398 }
9399 win = getWindow( doc );
9400 return {
9401 top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
9402 left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
9403 };
9404};
9405
9406jQuery.offset = {
9407
9408 setOffset: function( elem, options, i ) {
9409 var position = jQuery.css( elem, "position" );
9410
9411 // set position first, in-case top/left are set even on static elem
9412 if ( position === "static" ) {
9413 elem.style.position = "relative";
9414 }
9415
9416 var curElem = jQuery( elem ),
9417 curOffset = curElem.offset(),
9418 curCSSTop = jQuery.css( elem, "top" ),
9419 curCSSLeft = jQuery.css( elem, "left" ),
9420 calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
9421 props = {}, curPosition = {}, curTop, curLeft;
9422
9423 // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
9424 if ( calculatePosition ) {
9425 curPosition = curElem.position();
9426 curTop = curPosition.top;
9427 curLeft = curPosition.left;
9428 } else {
9429 curTop = parseFloat( curCSSTop ) || 0;
9430 curLeft = parseFloat( curCSSLeft ) || 0;
9431 }
9432
9433 if ( jQuery.isFunction( options ) ) {
9434 options = options.call( elem, i, curOffset );
9435 }
9436
9437 if ( options.top != null ) {
9438 props.top = ( options.top - curOffset.top ) + curTop;
9439 }
9440 if ( options.left != null ) {
9441 props.left = ( options.left - curOffset.left ) + curLeft;
9442 }
9443
9444 if ( "using" in options ) {
9445 options.using.call( elem, props );
9446 } else {
9447 curElem.css( props );
9448 }
9449 }
9450};
9451
9452
9453jQuery.fn.extend({
9454
9455 position: function() {
9456 if ( !this[ 0 ] ) {
9457 return;
9458 }
9459
9460 var offsetParent, offset,
9461 parentOffset = { top: 0, left: 0 },
9462 elem = this[ 0 ];
9463
9464 // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
9465 if ( jQuery.css( elem, "position" ) === "fixed" ) {
9466 // we assume that getBoundingClientRect is available when computed position is fixed
9467 offset = elem.getBoundingClientRect();
9468 } else {
9469 // Get *real* offsetParent
9470 offsetParent = this.offsetParent();
9471
9472 // Get correct offsets
9473 offset = this.offset();
9474 if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
9475 parentOffset = offsetParent.offset();
9476 }
9477
9478 // Add offsetParent borders
9479 parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
9480 parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
9481 }
9482
9483 // Subtract parent offsets and element margins
9484 // note: when an element has margin: auto the offsetLeft and marginLeft
9485 // are the same in Safari causing offset.left to incorrectly be 0
9486 return {
9487 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
9488 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
9489 };
9490 },
9491
9492 offsetParent: function() {
9493 return this.map(function() {
9494 var offsetParent = this.offsetParent || document.documentElement;
9495 while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
9496 offsetParent = offsetParent.offsetParent;
9497 }
9498 return offsetParent || document.documentElement;
9499 });
9500 }
9501});
9502
9503
9504// Create scrollLeft and scrollTop methods
9505jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
9506 var top = /Y/.test( prop );
9507
9508 jQuery.fn[ method ] = function( val ) {
9509 return jQuery.access( this, function( elem, method, val ) {
9510 var win = getWindow( elem );
9511
9512 if ( val === undefined ) {
9513 return win ? (prop in win) ? win[ prop ] :
9514 win.document.documentElement[ method ] :
9515 elem[ method ];
9516 }
9517
9518 if ( win ) {
9519 win.scrollTo(
9520 !top ? val : jQuery( win ).scrollLeft(),
9521 top ? val : jQuery( win ).scrollTop()
9522 );
9523
9524 } else {
9525 elem[ method ] = val;
9526 }
9527 }, method, val, arguments.length, null );
9528 };
9529});
9530
9531function getWindow( elem ) {
9532 return jQuery.isWindow( elem ) ?
9533 elem :
9534 elem.nodeType === 9 ?
9535 elem.defaultView || elem.parentWindow :
9536 false;
9537}
9538// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9539jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9540 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9541 // margin is only for outerHeight, outerWidth
9542 jQuery.fn[ funcName ] = function( margin, value ) {
9543 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9544 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9545
9546 return jQuery.access( this, function( elem, type, value ) {
9547 var doc;
9548
9549 if ( jQuery.isWindow( elem ) ) {
9550 // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9551 // isn't a whole lot we can do. See pull request at this URL for discussion:
9552 // https://github.com/jquery/jquery/pull/764
9553 return elem.document.documentElement[ "client" + name ];
9554 }
9555
9556 // Get document width or height
9557 if ( elem.nodeType === 9 ) {
9558 doc = elem.documentElement;
9559
9560 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
9561 // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
9562 return Math.max(
9563 elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9564 elem.body[ "offset" + name ], doc[ "offset" + name ],
9565 doc[ "client" + name ]
9566 );
9567 }
9568
9569 return value === undefined ?
9570 // Get width or height on the element, requesting but not forcing parseFloat
9571 jQuery.css( elem, type, extra ) :
9572
9573 // Set width or height on the element
9574 jQuery.style( elem, type, value, extra );
9575 }, type, chainable ? margin : undefined, chainable, null );
9576 };
9577 });
9578});
9579// Limit scope pollution from any deprecated API
9580// (function() {
9581
9582// })();
9583// Expose jQuery to the global object
9584window.jQuery = window.$ = jQuery;
9585
9586// Expose jQuery as an AMD module, but only for AMD loaders that
9587// understand the issues with loading multiple versions of jQuery
9588// in a page that all might call define(). The loader will indicate
9589// they have special allowances for multiple jQuery versions by
9590// specifying define.amd.jQuery = true. Register as a named module,
9591// since jQuery can be concatenated with other files that may use define,
9592// but not use a proper concatenation script that understands anonymous
9593// AMD modules. A named AMD is safest and most robust way to register.
9594// Lowercase jquery is used because AMD module names are derived from
9595// file names, and jQuery is normally delivered in a lowercase file name.
9596// Do this after creating the global so that if an AMD module wants to call
9597// noConflict to hide this version of jQuery, it will work.
9598if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9599 define( "jquery", [], function () { return jQuery; } );
9600}
9601
9602})( window );
9603</script>
9604 <script type="application/javascript">
9605(function($){$.extend({tablesorter:new
9606function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1
9607var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[$?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[$]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
9608</script>
9609 <script type="application/javascript">/* Javascript plotting library for jQuery, version 0.8.3.
9610
9611Copyright (c) 2007-2014 IOLA and Ole Laursen.
9612Licensed under the MIT license.
9613
9614*/
9615
9616// first an inline dependency, jquery.colorhelpers.js, we inline it here
9617// for convenience
9618
9619/* Plugin for jQuery for working with colors.
9620 *
9621 * Version 1.1.
9622 *
9623 * Inspiration from jQuery color animation plugin by John Resig.
9624 *
9625 * Released under the MIT license by Ole Laursen, October 2009.
9626 *
9627 * Examples:
9628 *
9629 * $.color.parse("#fff").scale('rgb', 0.25).add('a', -0.5).toString()
9630 * var c = $.color.extract($("#mydiv"), 'background-color');
9631 * console.log(c.r, c.g, c.b, c.a);
9632 * $.color.make(100, 50, 25, 0.4).toString() // returns "rgba(100,50,25,0.4)"
9633 *
9634 * Note that .scale() and .add() return the same modified object
9635 * instead of making a new one.
9636 *
9637 * V. 1.1: Fix error handling so e.g. parsing an empty string does
9638 * produce a color rather than just crashing.
9639 */
9640(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i<c.length;++i)o[c.charAt(i)]+=d;return o.normalize()};o.scale=function(c,f){for(var i=0;i<c.length;++i)o[c.charAt(i)]*=f;return o.normalize()};o.toString=function(){if(o.a>=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return value<min?min:value>max?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],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],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
9641
9642// the actual Flot code
9643(function($) {
9644
9645 // Cache the prototype hasOwnProperty for faster access
9646
9647 var hasOwnProperty = Object.prototype.hasOwnProperty;
9648
9649 // A shim to provide 'detach' to jQuery versions prior to 1.4. Using a DOM
9650 // operation produces the same effect as detach, i.e. removing the element
9651 // without touching its jQuery data.
9652
9653 // Do not merge this into Flot 0.9, since it requires jQuery 1.4.4+.
9654
9655 if (!$.fn.detach) {
9656 $.fn.detach = function() {
9657 return this.each(function() {
9658 if (this.parentNode) {
9659 this.parentNode.removeChild( this );
9660 }
9661 });
9662 };
9663 }
9664
9665 ///////////////////////////////////////////////////////////////////////////
9666 // The Canvas object is a wrapper around an HTML5 <canvas> tag.
9667 //
9668 // @constructor
9669 // @param {string} cls List of classes to apply to the canvas.
9670 // @param {element} container Element onto which to append the canvas.
9671 //
9672 // Requiring a container is a little iffy, but unfortunately canvas
9673 // operations don't work unless the canvas is attached to the DOM.
9674
9675 function Canvas(cls, container) {
9676
9677 var element = container.children("." + cls)[0];
9678
9679 if (element == null) {
9680
9681 element = document.createElement("canvas");
9682 element.className = cls;
9683
9684 $(element).css({ direction: "ltr", position: "absolute", left: 0, top: 0 })
9685 .appendTo(container);
9686
9687 // If HTML5 Canvas isn't available, fall back to [Ex|Flash]canvas
9688
9689 if (!element.getContext) {
9690 if (window.G_vmlCanvasManager) {
9691 element = window.G_vmlCanvasManager.initElement(element);
9692 } else {
9693 throw new Error("Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode.");
9694 }
9695 }
9696 }
9697
9698 this.element = element;
9699
9700 var context = this.context = element.getContext("2d");
9701
9702 // Determine the screen's ratio of physical to device-independent
9703 // pixels. This is the ratio between the canvas width that the browser
9704 // advertises and the number of pixels actually present in that space.
9705
9706 // The iPhone 4, for example, has a device-independent width of 320px,
9707 // but its screen is actually 640px wide. It therefore has a pixel
9708 // ratio of 2, while most normal devices have a ratio of 1.
9709
9710 var devicePixelRatio = window.devicePixelRatio || 1,
9711 backingStoreRatio =
9712 context.webkitBackingStorePixelRatio ||
9713 context.mozBackingStorePixelRatio ||
9714 context.msBackingStorePixelRatio ||
9715 context.oBackingStorePixelRatio ||
9716 context.backingStorePixelRatio || 1;
9717
9718 this.pixelRatio = devicePixelRatio / backingStoreRatio;
9719
9720 // Size the canvas to match the internal dimensions of its container
9721
9722 this.resize(container.width(), container.height());
9723
9724 // Collection of HTML div layers for text overlaid onto the canvas
9725
9726 this.textContainer = null;
9727 this.text = {};
9728
9729 // Cache of text fragments and metrics, so we can avoid expensively
9730 // re-calculating them when the plot is re-rendered in a loop.
9731
9732 this._textCache = {};
9733 }
9734
9735 // Resizes the canvas to the given dimensions.
9736 //
9737 // @param {number} width New width of the canvas, in pixels.
9738 // @param {number} width New height of the canvas, in pixels.
9739
9740 Canvas.prototype.resize = function(width, height) {
9741
9742 if (width <= 0 || height <= 0) {
9743 throw new Error("Invalid dimensions for plot, width = " + width + ", height = " + height);
9744 }
9745
9746 var element = this.element,
9747 context = this.context,
9748 pixelRatio = this.pixelRatio;
9749
9750 // Resize the canvas, increasing its density based on the display's
9751 // pixel ratio; basically giving it more pixels without increasing the
9752 // size of its element, to take advantage of the fact that retina
9753 // displays have that many more pixels in the same advertised space.
9754
9755 // Resizing should reset the state (excanvas seems to be buggy though)
9756
9757 if (this.width != width) {
9758 element.width = width * pixelRatio;
9759 element.style.width = width + "px";
9760 this.width = width;
9761 }
9762
9763 if (this.height != height) {
9764 element.height = height * pixelRatio;
9765 element.style.height = height + "px";
9766 this.height = height;
9767 }
9768
9769 // Save the context, so we can reset in case we get replotted. The
9770 // restore ensure that we're really back at the initial state, and
9771 // should be safe even if we haven't saved the initial state yet.
9772
9773 context.restore();
9774 context.save();
9775
9776 // Scale the coordinate space to match the display density; so even though we
9777 // may have twice as many pixels, we still want lines and other drawing to
9778 // appear at the same size; the extra pixels will just make them crisper.
9779
9780 context.scale(pixelRatio, pixelRatio);
9781 };
9782
9783 // Clears the entire canvas area, not including any overlaid HTML text
9784
9785 Canvas.prototype.clear = function() {
9786 this.context.clearRect(0, 0, this.width, this.height);
9787 };
9788
9789 // Finishes rendering the canvas, including managing the text overlay.
9790
9791 Canvas.prototype.render = function() {
9792
9793 var cache = this._textCache;
9794
9795 // For each text layer, add elements marked as active that haven't
9796 // already been rendered, and remove those that are no longer active.
9797
9798 for (var layerKey in cache) {
9799 if (hasOwnProperty.call(cache, layerKey)) {
9800
9801 var layer = this.getTextLayer(layerKey),
9802 layerCache = cache[layerKey];
9803
9804 layer.hide();
9805
9806 for (var styleKey in layerCache) {
9807 if (hasOwnProperty.call(layerCache, styleKey)) {
9808 var styleCache = layerCache[styleKey];
9809 for (var key in styleCache) {
9810 if (hasOwnProperty.call(styleCache, key)) {
9811
9812 var positions = styleCache[key].positions;
9813
9814 for (var i = 0, position; position = positions[i]; i++) {
9815 if (position.active) {
9816 if (!position.rendered) {
9817 layer.append(position.element);
9818 position.rendered = true;
9819 }
9820 } else {
9821 positions.splice(i--, 1);
9822 if (position.rendered) {
9823 position.element.detach();
9824 }
9825 }
9826 }
9827
9828 if (positions.length == 0) {
9829 delete styleCache[key];
9830 }
9831 }
9832 }
9833 }
9834 }
9835
9836 layer.show();
9837 }
9838 }
9839 };
9840
9841 // Creates (if necessary) and returns the text overlay container.
9842 //
9843 // @param {string} classes String of space-separated CSS classes used to
9844 // uniquely identify the text layer.
9845 // @return {object} The jQuery-wrapped text-layer div.
9846
9847 Canvas.prototype.getTextLayer = function(classes) {
9848
9849 var layer = this.text[classes];
9850
9851 // Create the text layer if it doesn't exist
9852
9853 if (layer == null) {
9854
9855 // Create the text layer container, if it doesn't exist
9856
9857 if (this.textContainer == null) {
9858 this.textContainer = $("<div class='flot-text'></div>")
9859 .css({
9860 position: "absolute",
9861 top: 0,
9862 left: 0,
9863 bottom: 0,
9864 right: 0,
9865 'font-size': "smaller",
9866 color: "#545454"
9867 })
9868 .insertAfter(this.element);
9869 }
9870
9871 layer = this.text[classes] = $("<div></div>")
9872 .addClass(classes)
9873 .css({
9874 position: "absolute",
9875 top: 0,
9876 left: 0,
9877 bottom: 0,
9878 right: 0
9879 })
9880 .appendTo(this.textContainer);
9881 }
9882
9883 return layer;
9884 };
9885
9886 // Creates (if necessary) and returns a text info object.
9887 //
9888 // The object looks like this:
9889 //
9890 // {
9891 // width: Width of the text's wrapper div.
9892 // height: Height of the text's wrapper div.
9893 // element: The jQuery-wrapped HTML div containing the text.
9894 // positions: Array of positions at which this text is drawn.
9895 // }
9896 //
9897 // The positions array contains objects that look like this:
9898 //
9899 // {
9900 // active: Flag indicating whether the text should be visible.
9901 // rendered: Flag indicating whether the text is currently visible.
9902 // element: The jQuery-wrapped HTML div containing the text.
9903 // x: X coordinate at which to draw the text.
9904 // y: Y coordinate at which to draw the text.
9905 // }
9906 //
9907 // Each position after the first receives a clone of the original element.
9908 //
9909 // The idea is that that the width, height, and general 'identity' of the
9910 // text is constant no matter where it is placed; the placements are a
9911 // secondary property.
9912 //
9913 // Canvas maintains a cache of recently-used text info objects; getTextInfo
9914 // either returns the cached element or creates a new entry.
9915 //
9916 // @param {string} layer A string of space-separated CSS classes uniquely
9917 // identifying the layer containing this text.
9918 // @param {string} text Text string to retrieve info for.
9919 // @param {(string|object)=} font Either a string of space-separated CSS
9920 // classes or a font-spec object, defining the text's font and style.
9921 // @param {number=} angle Angle at which to rotate the text, in degrees.
9922 // Angle is currently unused, it will be implemented in the future.
9923 // @param {number=} width Maximum width of the text before it wraps.
9924 // @return {object} a text info object.
9925
9926 Canvas.prototype.getTextInfo = function(layer, text, font, angle, width) {
9927
9928 var textStyle, layerCache, styleCache, info;
9929
9930 // Cast the value to a string, in case we were given a number or such
9931
9932 text = "" + text;
9933
9934 // If the font is a font-spec object, generate a CSS font definition
9935
9936 if (typeof font === "object") {
9937 textStyle = font.style + " " + font.variant + " " + font.weight + " " + font.size + "px/" + font.lineHeight + "px " + font.family;
9938 } else {
9939 textStyle = font;
9940 }
9941
9942 // Retrieve (or create) the cache for the text's layer and styles
9943
9944 layerCache = this._textCache[layer];
9945
9946 if (layerCache == null) {
9947 layerCache = this._textCache[layer] = {};
9948 }
9949
9950 styleCache = layerCache[textStyle];
9951
9952 if (styleCache == null) {
9953 styleCache = layerCache[textStyle] = {};
9954 }
9955
9956 info = styleCache[text];
9957
9958 // If we can't find a matching element in our cache, create a new one
9959
9960 if (info == null) {
9961
9962 var element = $("<div></div>").html(text)
9963 .css({
9964 position: "absolute",
9965 'max-width': width,
9966 top: -9999
9967 })
9968 .appendTo(this.getTextLayer(layer));
9969
9970 if (typeof font === "object") {
9971 element.css({
9972 font: textStyle,
9973 color: font.color
9974 });
9975 } else if (typeof font === "string") {
9976 element.addClass(font);
9977 }
9978
9979 info = styleCache[text] = {
9980 width: element.outerWidth(true),
9981 height: element.outerHeight(true),
9982 element: element,
9983 positions: []
9984 };
9985
9986 element.detach();
9987 }
9988
9989 return info;
9990 };
9991
9992 // Adds a text string to the canvas text overlay.
9993 //
9994 // The text isn't drawn immediately; it is marked as rendering, which will
9995 // result in its addition to the canvas on the next render pass.
9996 //
9997 // @param {string} layer A string of space-separated CSS classes uniquely
9998 // identifying the layer containing this text.
9999 // @param {number} x X coordinate at which to draw the text.
10000 // @param {number} y Y coordinate at which to draw the text.
10001 // @param {string} text Text string to draw.
10002 // @param {(string|object)=} font Either a string of space-separated CSS
10003 // classes or a font-spec object, defining the text's font and style.
10004 // @param {number=} angle Angle at which to rotate the text, in degrees.
10005 // Angle is currently unused, it will be implemented in the future.
10006 // @param {number=} width Maximum width of the text before it wraps.
10007 // @param {string=} halign Horizontal alignment of the text; either "left",
10008 // "center" or "right".
10009 // @param {string=} valign Vertical alignment of the text; either "top",
10010 // "middle" or "bottom".
10011
10012 Canvas.prototype.addText = function(layer, x, y, text, font, angle, width, halign, valign) {
10013
10014 var info = this.getTextInfo(layer, text, font, angle, width),
10015 positions = info.positions;
10016
10017 // Tweak the div's position to match the text's alignment
10018
10019 if (halign == "center") {
10020 x -= info.width / 2;
10021 } else if (halign == "right") {
10022 x -= info.width;
10023 }
10024
10025 if (valign == "middle") {
10026 y -= info.height / 2;
10027 } else if (valign == "bottom") {
10028 y -= info.height;
10029 }
10030
10031 // Determine whether this text already exists at this position.
10032 // If so, mark it for inclusion in the next render pass.
10033
10034 for (var i = 0, position; position = positions[i]; i++) {
10035 if (position.x == x && position.y == y) {
10036 position.active = true;
10037 return;
10038 }
10039 }
10040
10041 // If the text doesn't exist at this position, create a new entry
10042
10043 // For the very first position we'll re-use the original element,
10044 // while for subsequent ones we'll clone it.
10045
10046 position = {
10047 active: true,
10048 rendered: false,
10049 element: positions.length ? info.element.clone() : info.element,
10050 x: x,
10051 y: y
10052 };
10053
10054 positions.push(position);
10055
10056 // Move the element to its final position within the container
10057
10058 position.element.css({
10059 top: Math.round(y),
10060 left: Math.round(x),
10061 'text-align': halign // In case the text wraps
10062 });
10063 };
10064
10065 // Removes one or more text strings from the canvas text overlay.
10066 //
10067 // If no parameters are given, all text within the layer is removed.
10068 //
10069 // Note that the text is not immediately removed; it is simply marked as
10070 // inactive, which will result in its removal on the next render pass.
10071 // This avoids the performance penalty for 'clear and redraw' behavior,
10072 // where we potentially get rid of all text on a layer, but will likely
10073 // add back most or all of it later, as when redrawing axes, for example.
10074 //
10075 // @param {string} layer A string of space-separated CSS classes uniquely
10076 // identifying the layer containing this text.
10077 // @param {number=} x X coordinate of the text.
10078 // @param {number=} y Y coordinate of the text.
10079 // @param {string=} text Text string to remove.
10080 // @param {(string|object)=} font Either a string of space-separated CSS
10081 // classes or a font-spec object, defining the text's font and style.
10082 // @param {number=} angle Angle at which the text is rotated, in degrees.
10083 // Angle is currently unused, it will be implemented in the future.
10084
10085 Canvas.prototype.removeText = function(layer, x, y, text, font, angle) {
10086 if (text == null) {
10087 var layerCache = this._textCache[layer];
10088 if (layerCache != null) {
10089 for (var styleKey in layerCache) {
10090 if (hasOwnProperty.call(layerCache, styleKey)) {
10091 var styleCache = layerCache[styleKey];
10092 for (var key in styleCache) {
10093 if (hasOwnProperty.call(styleCache, key)) {
10094 var positions = styleCache[key].positions;
10095 for (var i = 0, position; position = positions[i]; i++) {
10096 position.active = false;
10097 }
10098 }
10099 }
10100 }
10101 }
10102 }
10103 } else {
10104 var positions = this.getTextInfo(layer, text, font, angle).positions;
10105 for (var i = 0, position; position = positions[i]; i++) {
10106 if (position.x == x && position.y == y) {
10107 position.active = false;
10108 }
10109 }
10110 }
10111 };
10112
10113 ///////////////////////////////////////////////////////////////////////////
10114 // The top-level container for the entire plot.
10115
10116 function Plot(placeholder, data_, options_, plugins) {
10117 // data is on the form:
10118 // [ series1, series2 ... ]
10119 // where series is either just the data as [ [x1, y1], [x2, y2], ... ]
10120 // or { data: [ [x1, y1], [x2, y2], ... ], label: "some label", ... }
10121
10122 var series = [],
10123 options = {
10124 // the color theme used for graphs
10125 colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
10126 legend: {
10127 show: true,
10128 noColumns: 1, // number of colums in legend table
10129 labelFormatter: null, // fn: string -> string
10130 labelBoxBorderColor: "#ccc", // border color for the little label boxes
10131 container: null, // container (as jQuery object) to put legend in, null means default on top of graph
10132 position: "ne", // position of default legend container within plot
10133 margin: 5, // distance from grid edge to default legend container within plot
10134 backgroundColor: null, // null means auto-detect
10135 backgroundOpacity: 0.85, // set to 0 to avoid background
10136 sorted: null // default to no legend sorting
10137 },
10138 xaxis: {
10139 show: null, // null = auto-detect, true = always, false = never
10140 position: "bottom", // or "top"
10141 mode: null, // null or "time"
10142 font: null, // null (derived from CSS in placeholder) or object like { size: 11, lineHeight: 13, style: "italic", weight: "bold", family: "sans-serif", variant: "small-caps" }
10143 color: null, // base color, labels, ticks
10144 tickColor: null, // possibly different color of ticks, e.g. "rgba(0,0,0,0.15)"
10145 transform: null, // null or f: number -> number to transform axis
10146 inverseTransform: null, // if transform is set, this should be the inverse function
10147 min: null, // min. value to show, null means set automatically
10148 max: null, // max. value to show, null means set automatically
10149 autoscaleMargin: null, // margin in % to add if auto-setting min/max
10150 ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
10151 tickFormatter: null, // fn: number -> string
10152 labelWidth: null, // size of tick labels in pixels
10153 labelHeight: null,
10154 reserveSpace: null, // whether to reserve space even if axis isn't shown
10155 tickLength: null, // size in pixels of ticks, or "full" for whole line
10156 alignTicksWithAxis: null, // axis number or null for no sync
10157 tickDecimals: null, // no. of decimals, null means auto
10158 tickSize: null, // number or [number, "unit"]
10159 minTickSize: null // number or [number, "unit"]
10160 },
10161 yaxis: {
10162 autoscaleMargin: 0.02,
10163 position: "left" // or "right"
10164 },
10165 xaxes: [],
10166 yaxes: [],
10167 series: {
10168 points: {
10169 show: false,
10170 radius: 3,
10171 lineWidth: 2, // in pixels
10172 fill: true,
10173 fillColor: "#ffffff",
10174 symbol: "circle" // or callback
10175 },
10176 lines: {
10177 // we don't put in show: false so we can see
10178 // whether lines were actively disabled
10179 lineWidth: 2, // in pixels
10180 fill: false,
10181 fillColor: null,
10182 steps: false
10183 // Omit 'zero', so we can later default its value to
10184 // match that of the 'fill' option.
10185 },
10186 bars: {
10187 show: false,
10188 lineWidth: 2, // in pixels
10189 barWidth: 1, // in units of the x axis
10190 fill: true,
10191 fillColor: null,
10192 align: "left", // "left", "right", or "center"
10193 horizontal: false,
10194 zero: true
10195 },
10196 shadowSize: 3,
10197 highlightColor: null
10198 },
10199 grid: {
10200 show: true,
10201 aboveData: false,
10202 color: "#545454", // primary color used for outline and labels
10203 backgroundColor: null, // null for transparent, else color
10204 borderColor: null, // set if different from the grid color
10205 tickColor: null, // color for the ticks, e.g. "rgba(0,0,0,0.15)"
10206 margin: 0, // distance from the canvas edge to the grid
10207 labelMargin: 5, // in pixels
10208 axisMargin: 8, // in pixels
10209 borderWidth: 2, // in pixels
10210 minBorderMargin: null, // in pixels, null means taken from points radius
10211 markings: null, // array of ranges or fn: axes -> array of ranges
10212 markingsColor: "#f4f4f4",
10213 markingsLineWidth: 2,
10214 // interactive stuff
10215 clickable: false,
10216 hoverable: false,
10217 autoHighlight: true, // highlight in case mouse is near
10218 mouseActiveRadius: 10 // how far the mouse can be away to activate an item
10219 },
10220 interaction: {
10221 redrawOverlayInterval: 1000/60 // time between updates, -1 means in same flow
10222 },
10223 hooks: {}
10224 },
10225 surface = null, // the canvas for the plot itself
10226 overlay = null, // canvas for interactive stuff on top of plot
10227 eventHolder = null, // jQuery object that events should be bound to
10228 ctx = null, octx = null,
10229 xaxes = [], yaxes = [],
10230 plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
10231 plotWidth = 0, plotHeight = 0,
10232 hooks = {
10233 processOptions: [],
10234 processRawData: [],
10235 processDatapoints: [],
10236 processOffset: [],
10237 drawBackground: [],
10238 drawSeries: [],
10239 draw: [],
10240 bindEvents: [],
10241 drawOverlay: [],
10242 shutdown: []
10243 },
10244 plot = this;
10245
10246 // public functions
10247 plot.setData = setData;
10248 plot.setupGrid = setupGrid;
10249 plot.draw = draw;
10250 plot.getPlaceholder = function() { return placeholder; };
10251 plot.getCanvas = function() { return surface.element; };
10252 plot.getPlotOffset = function() { return plotOffset; };
10253 plot.width = function () { return plotWidth; };
10254 plot.height = function () { return plotHeight; };
10255 plot.offset = function () {
10256 var o = eventHolder.offset();
10257 o.left += plotOffset.left;
10258 o.top += plotOffset.top;
10259 return o;
10260 };
10261 plot.getData = function () { return series; };
10262 plot.getAxes = function () {
10263 var res = {}, i;
10264 $.each(xaxes.concat(yaxes), function (_, axis) {
10265 if (axis)
10266 res[axis.direction + (axis.n != 1 ? axis.n : "") + "axis"] = axis;
10267 });
10268 return res;
10269 };
10270 plot.getXAxes = function () { return xaxes; };
10271 plot.getYAxes = function () { return yaxes; };
10272 plot.c2p = canvasToAxisCoords;
10273 plot.p2c = axisToCanvasCoords;
10274 plot.getOptions = function () { return options; };
10275 plot.highlight = highlight;
10276 plot.unhighlight = unhighlight;
10277 plot.triggerRedrawOverlay = triggerRedrawOverlay;
10278 plot.pointOffset = function(point) {
10279 return {
10280 left: parseInt(xaxes[axisNumber(point, "x") - 1].p2c(+point.x) + plotOffset.left, 10),
10281 top: parseInt(yaxes[axisNumber(point, "y") - 1].p2c(+point.y) + plotOffset.top, 10)
10282 };
10283 };
10284 plot.shutdown = shutdown;
10285 plot.destroy = function () {
10286 shutdown();
10287 placeholder.removeData("plot").empty();
10288
10289 series = [];
10290 options = null;
10291 surface = null;
10292 overlay = null;
10293 eventHolder = null;
10294 ctx = null;
10295 octx = null;
10296 xaxes = [];
10297 yaxes = [];
10298 hooks = null;
10299 highlights = [];
10300 plot = null;
10301 };
10302 plot.resize = function () {
10303 var width = placeholder.width(),
10304 height = placeholder.height();
10305 surface.resize(width, height);
10306 overlay.resize(width, height);
10307 };
10308
10309 // public attributes
10310 plot.hooks = hooks;
10311
10312 // initialize
10313 initPlugins(plot);
10314 parseOptions(options_);
10315 setupCanvases();
10316 setData(data_);
10317 setupGrid();
10318 draw();
10319 bindEvents();
10320
10321
10322 function executeHooks(hook, args) {
10323 args = [plot].concat(args);
10324 for (var i = 0; i < hook.length; ++i)
10325 hook[i].apply(this, args);
10326 }
10327
10328 function initPlugins() {
10329
10330 // References to key classes, allowing plugins to modify them
10331
10332 var classes = {
10333 Canvas: Canvas
10334 };
10335
10336 for (var i = 0; i < plugins.length; ++i) {
10337 var p = plugins[i];
10338 p.init(plot, classes);
10339 if (p.options)
10340 $.extend(true, options, p.options);
10341 }
10342 }
10343
10344 function parseOptions(opts) {
10345
10346 $.extend(true, options, opts);
10347
10348 // $.extend merges arrays, rather than replacing them. When less
10349 // colors are provided than the size of the default palette, we
10350 // end up with those colors plus the remaining defaults, which is
10351 // not expected behavior; avoid it by replacing them here.
10352
10353 if (opts && opts.colors) {
10354 options.colors = opts.colors;
10355 }
10356
10357 if (options.xaxis.color == null)
10358 options.xaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
10359 if (options.yaxis.color == null)
10360 options.yaxis.color = $.color.parse(options.grid.color).scale('a', 0.22).toString();
10361
10362 if (options.xaxis.tickColor == null) // grid.tickColor for back-compatibility
10363 options.xaxis.tickColor = options.grid.tickColor || options.xaxis.color;
10364 if (options.yaxis.tickColor == null) // grid.tickColor for back-compatibility
10365 options.yaxis.tickColor = options.grid.tickColor || options.yaxis.color;
10366
10367 if (options.grid.borderColor == null)
10368 options.grid.borderColor = options.grid.color;
10369 if (options.grid.tickColor == null)
10370 options.grid.tickColor = $.color.parse(options.grid.color).scale('a', 0.22).toString();
10371
10372 // Fill in defaults for axis options, including any unspecified
10373 // font-spec fields, if a font-spec was provided.
10374
10375 // If no x/y axis options were provided, create one of each anyway,
10376 // since the rest of the code assumes that they exist.
10377
10378 var i, axisOptions, axisCount,
10379 fontSize = placeholder.css("font-size"),
10380 fontSizeDefault = fontSize ? +fontSize.replace("px", "") : 13,
10381 fontDefaults = {
10382 style: placeholder.css("font-style"),
10383 size: Math.round(0.8 * fontSizeDefault),
10384 variant: placeholder.css("font-variant"),
10385 weight: placeholder.css("font-weight"),
10386 family: placeholder.css("font-family")
10387 };
10388
10389 axisCount = options.xaxes.length || 1;
10390 for (i = 0; i < axisCount; ++i) {
10391
10392 axisOptions = options.xaxes[i];
10393 if (axisOptions && !axisOptions.tickColor) {
10394 axisOptions.tickColor = axisOptions.color;
10395 }
10396
10397 axisOptions = $.extend(true, {}, options.xaxis, axisOptions);
10398 options.xaxes[i] = axisOptions;
10399
10400 if (axisOptions.font) {
10401 axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
10402 if (!axisOptions.font.color) {
10403 axisOptions.font.color = axisOptions.color;
10404 }
10405 if (!axisOptions.font.lineHeight) {
10406 axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
10407 }
10408 }
10409 }
10410
10411 axisCount = options.yaxes.length || 1;
10412 for (i = 0; i < axisCount; ++i) {
10413
10414 axisOptions = options.yaxes[i];
10415 if (axisOptions && !axisOptions.tickColor) {
10416 axisOptions.tickColor = axisOptions.color;
10417 }
10418
10419 axisOptions = $.extend(true, {}, options.yaxis, axisOptions);
10420 options.yaxes[i] = axisOptions;
10421
10422 if (axisOptions.font) {
10423 axisOptions.font = $.extend({}, fontDefaults, axisOptions.font);
10424 if (!axisOptions.font.color) {
10425 axisOptions.font.color = axisOptions.color;
10426 }
10427 if (!axisOptions.font.lineHeight) {
10428 axisOptions.font.lineHeight = Math.round(axisOptions.font.size * 1.15);
10429 }
10430 }
10431 }
10432
10433 // backwards compatibility, to be removed in future
10434 if (options.xaxis.noTicks && options.xaxis.ticks == null)
10435 options.xaxis.ticks = options.xaxis.noTicks;
10436 if (options.yaxis.noTicks && options.yaxis.ticks == null)
10437 options.yaxis.ticks = options.yaxis.noTicks;
10438 if (options.x2axis) {
10439 options.xaxes[1] = $.extend(true, {}, options.xaxis, options.x2axis);
10440 options.xaxes[1].position = "top";
10441 // Override the inherit to allow the axis to auto-scale
10442 if (options.x2axis.min == null) {
10443 options.xaxes[1].min = null;
10444 }
10445 if (options.x2axis.max == null) {
10446 options.xaxes[1].max = null;
10447 }
10448 }
10449 if (options.y2axis) {
10450 options.yaxes[1] = $.extend(true, {}, options.yaxis, options.y2axis);
10451 options.yaxes[1].position = "right";
10452 // Override the inherit to allow the axis to auto-scale
10453 if (options.y2axis.min == null) {
10454 options.yaxes[1].min = null;
10455 }
10456 if (options.y2axis.max == null) {
10457 options.yaxes[1].max = null;
10458 }
10459 }
10460 if (options.grid.coloredAreas)
10461 options.grid.markings = options.grid.coloredAreas;
10462 if (options.grid.coloredAreasColor)
10463 options.grid.markingsColor = options.grid.coloredAreasColor;
10464 if (options.lines)
10465 $.extend(true, options.series.lines, options.lines);
10466 if (options.points)
10467 $.extend(true, options.series.points, options.points);
10468 if (options.bars)
10469 $.extend(true, options.series.bars, options.bars);
10470 if (options.shadowSize != null)
10471 options.series.shadowSize = options.shadowSize;
10472 if (options.highlightColor != null)
10473 options.series.highlightColor = options.highlightColor;
10474
10475 // save options on axes for future reference
10476 for (i = 0; i < options.xaxes.length; ++i)
10477 getOrCreateAxis(xaxes, i + 1).options = options.xaxes[i];
10478 for (i = 0; i < options.yaxes.length; ++i)
10479 getOrCreateAxis(yaxes, i + 1).options = options.yaxes[i];
10480
10481 // add hooks from options
10482 for (var n in hooks)
10483 if (options.hooks[n] && options.hooks[n].length)
10484 hooks[n] = hooks[n].concat(options.hooks[n]);
10485
10486 executeHooks(hooks.processOptions, [options]);
10487 }
10488
10489 function setData(d) {
10490 series = parseData(d);
10491 fillInSeriesOptions();
10492 processData();
10493 }
10494
10495 function parseData(d) {
10496 var res = [];
10497 for (var i = 0; i < d.length; ++i) {
10498 var s = $.extend(true, {}, options.series);
10499
10500 if (d[i].data != null) {
10501 s.data = d[i].data; // move the data instead of deep-copy
10502 delete d[i].data;
10503
10504 $.extend(true, s, d[i]);
10505
10506 d[i].data = s.data;
10507 }
10508 else
10509 s.data = d[i];
10510 res.push(s);
10511 }
10512
10513 return res;
10514 }
10515
10516 function axisNumber(obj, coord) {
10517 var a = obj[coord + "axis"];
10518 if (typeof a == "object") // if we got a real axis, extract number
10519 a = a.n;
10520 if (typeof a != "number")
10521 a = 1; // default to first axis
10522 return a;
10523 }
10524
10525 function allAxes() {
10526 // return flat array without annoying null entries
10527 return $.grep(xaxes.concat(yaxes), function (a) { return a; });
10528 }
10529
10530 function canvasToAxisCoords(pos) {
10531 // return an object with x/y corresponding to all used axes
10532 var res = {}, i, axis;
10533 for (i = 0; i < xaxes.length; ++i) {
10534 axis = xaxes[i];
10535 if (axis && axis.used)
10536 res["x" + axis.n] = axis.c2p(pos.left);
10537 }
10538
10539 for (i = 0; i < yaxes.length; ++i) {
10540 axis = yaxes[i];
10541 if (axis && axis.used)
10542 res["y" + axis.n] = axis.c2p(pos.top);
10543 }
10544
10545 if (res.x1 !== undefined)
10546 res.x = res.x1;
10547 if (res.y1 !== undefined)
10548 res.y = res.y1;
10549
10550 return res;
10551 }
10552
10553 function axisToCanvasCoords(pos) {
10554 // get canvas coords from the first pair of x/y found in pos
10555 var res = {}, i, axis, key;
10556
10557 for (i = 0; i < xaxes.length; ++i) {
10558 axis = xaxes[i];
10559 if (axis && axis.used) {
10560 key = "x" + axis.n;
10561 if (pos[key] == null && axis.n == 1)
10562 key = "x";
10563
10564 if (pos[key] != null) {
10565 res.left = axis.p2c(pos[key]);
10566 break;
10567 }
10568 }
10569 }
10570
10571 for (i = 0; i < yaxes.length; ++i) {
10572 axis = yaxes[i];
10573 if (axis && axis.used) {
10574 key = "y" + axis.n;
10575 if (pos[key] == null && axis.n == 1)
10576 key = "y";
10577
10578 if (pos[key] != null) {
10579 res.top = axis.p2c(pos[key]);
10580 break;
10581 }
10582 }
10583 }
10584
10585 return res;
10586 }
10587
10588 function getOrCreateAxis(axes, number) {
10589 if (!axes[number - 1])
10590 axes[number - 1] = {
10591 n: number, // save the number for future reference
10592 direction: axes == xaxes ? "x" : "y",
10593 options: $.extend(true, {}, axes == xaxes ? options.xaxis : options.yaxis)
10594 };
10595
10596 return axes[number - 1];
10597 }
10598
10599 function fillInSeriesOptions() {
10600
10601 var neededColors = series.length, maxIndex = -1, i;
10602
10603 // Subtract the number of series that already have fixed colors or
10604 // color indexes from the number that we still need to generate.
10605
10606 for (i = 0; i < series.length; ++i) {
10607 var sc = series[i].color;
10608 if (sc != null) {
10609 neededColors--;
10610 if (typeof sc == "number" && sc > maxIndex) {
10611 maxIndex = sc;
10612 }
10613 }
10614 }
10615
10616 // If any of the series have fixed color indexes, then we need to
10617 // generate at least as many colors as the highest index.
10618
10619 if (neededColors <= maxIndex) {
10620 neededColors = maxIndex + 1;
10621 }
10622
10623 // Generate all the colors, using first the option colors and then
10624 // variations on those colors once they're exhausted.
10625
10626 var c, colors = [], colorPool = options.colors,
10627 colorPoolSize = colorPool.length, variation = 0;
10628
10629 for (i = 0; i < neededColors; i++) {
10630
10631 c = $.color.parse(colorPool[i % colorPoolSize] || "#666");
10632
10633 // Each time we exhaust the colors in the pool we adjust
10634 // a scaling factor used to produce more variations on
10635 // those colors. The factor alternates negative/positive
10636 // to produce lighter/darker colors.
10637
10638 // Reset the variation after every few cycles, or else
10639 // it will end up producing only white or black colors.
10640
10641 if (i % colorPoolSize == 0 && i) {
10642 if (variation >= 0) {
10643 if (variation < 0.5) {
10644 variation = -variation - 0.2;
10645 } else variation = 0;
10646 } else variation = -variation;
10647 }
10648
10649 colors[i] = c.scale('rgb', 1 + variation);
10650 }
10651
10652 // Finalize the series options, filling in their colors
10653
10654 var colori = 0, s;
10655 for (i = 0; i < series.length; ++i) {
10656 s = series[i];
10657
10658 // assign colors
10659 if (s.color == null) {
10660 s.color = colors[colori].toString();
10661 ++colori;
10662 }
10663 else if (typeof s.color == "number")
10664 s.color = colors[s.color].toString();
10665
10666 // turn on lines automatically in case nothing is set
10667 if (s.lines.show == null) {
10668 var v, show = true;
10669 for (v in s)
10670 if (s[v] && s[v].show) {
10671 show = false;
10672 break;
10673 }
10674 if (show)
10675 s.lines.show = true;
10676 }
10677
10678 // If nothing was provided for lines.zero, default it to match
10679 // lines.fill, since areas by default should extend to zero.
10680
10681 if (s.lines.zero == null) {
10682 s.lines.zero = !!s.lines.fill;
10683 }
10684
10685 // setup axes
10686 s.xaxis = getOrCreateAxis(xaxes, axisNumber(s, "x"));
10687 s.yaxis = getOrCreateAxis(yaxes, axisNumber(s, "y"));
10688 }
10689 }
10690
10691 function processData() {
10692 var topSentry = Number.POSITIVE_INFINITY,
10693 bottomSentry = Number.NEGATIVE_INFINITY,
10694 fakeInfinity = Number.MAX_VALUE,
10695 i, j, k, m, length,
10696 s, points, ps, x, y, axis, val, f, p,
10697 data, format;
10698
10699 function updateAxis(axis, min, max) {
10700 if (min < axis.datamin && min != -fakeInfinity)
10701 axis.datamin = min;
10702 if (max > axis.datamax && max != fakeInfinity)
10703 axis.datamax = max;
10704 }
10705
10706 $.each(allAxes(), function (_, axis) {
10707 // init axis
10708 axis.datamin = topSentry;
10709 axis.datamax = bottomSentry;
10710 axis.used = false;
10711 });
10712
10713 for (i = 0; i < series.length; ++i) {
10714 s = series[i];
10715 s.datapoints = { points: [] };
10716
10717 executeHooks(hooks.processRawData, [ s, s.data, s.datapoints ]);
10718 }
10719
10720 // first pass: clean and copy data
10721 for (i = 0; i < series.length; ++i) {
10722 s = series[i];
10723
10724 data = s.data;
10725 format = s.datapoints.format;
10726
10727 if (!format) {
10728 format = [];
10729 // find out how to copy
10730 format.push({ x: true, number: true, required: true });
10731 format.push({ y: true, number: true, required: true });
10732
10733 if (s.bars.show || (s.lines.show && s.lines.fill)) {
10734 var autoscale = !!((s.bars.show && s.bars.zero) || (s.lines.show && s.lines.zero));
10735 format.push({ y: true, number: true, required: false, defaultValue: 0, autoscale: autoscale });
10736 if (s.bars.horizontal) {
10737 delete format[format.length - 1].y;
10738 format[format.length - 1].x = true;
10739 }
10740 }
10741
10742 s.datapoints.format = format;
10743 }
10744
10745 if (s.datapoints.pointsize != null)
10746 continue; // already filled in
10747
10748 s.datapoints.pointsize = format.length;
10749
10750 ps = s.datapoints.pointsize;
10751 points = s.datapoints.points;
10752
10753 var insertSteps = s.lines.show && s.lines.steps;
10754 s.xaxis.used = s.yaxis.used = true;
10755
10756 for (j = k = 0; j < data.length; ++j, k += ps) {
10757 p = data[j];
10758
10759 var nullify = p == null;
10760 if (!nullify) {
10761 for (m = 0; m < ps; ++m) {
10762 val = p[m];
10763 f = format[m];
10764
10765 if (f) {
10766 if (f.number && val != null) {
10767 val = +val; // convert to number
10768 if (isNaN(val))
10769 val = null;
10770 else if (val == Infinity)
10771 val = fakeInfinity;
10772 else if (val == -Infinity)
10773 val = -fakeInfinity;
10774 }
10775
10776 if (val == null) {
10777 if (f.required)
10778 nullify = true;
10779
10780 if (f.defaultValue != null)
10781 val = f.defaultValue;
10782 }
10783 }
10784
10785 points[k + m] = val;
10786 }
10787 }
10788
10789 if (nullify) {
10790 for (m = 0; m < ps; ++m) {
10791 val = points[k + m];
10792 if (val != null) {
10793 f = format[m];
10794 // extract min/max info
10795 if (f.autoscale !== false) {
10796 if (f.x) {
10797 updateAxis(s.xaxis, val, val);
10798 }
10799 if (f.y) {
10800 updateAxis(s.yaxis, val, val);
10801 }
10802 }
10803 }
10804 points[k + m] = null;
10805 }
10806 }
10807 else {
10808 // a little bit of line specific stuff that
10809 // perhaps shouldn't be here, but lacking
10810 // better means...
10811 if (insertSteps && k > 0
10812 && points[k - ps] != null
10813 && points[k - ps] != points[k]
10814 && points[k - ps + 1] != points[k + 1]) {
10815 // copy the point to make room for a middle point
10816 for (m = 0; m < ps; ++m)
10817 points[k + ps + m] = points[k + m];
10818
10819 // middle point has same y
10820 points[k + 1] = points[k - ps + 1];
10821
10822 // we've added a point, better reflect that
10823 k += ps;
10824 }
10825 }
10826 }
10827 }
10828
10829 // give the hooks a chance to run
10830 for (i = 0; i < series.length; ++i) {
10831 s = series[i];
10832
10833 executeHooks(hooks.processDatapoints, [ s, s.datapoints]);
10834 }
10835
10836 // second pass: find datamax/datamin for auto-scaling
10837 for (i = 0; i < series.length; ++i) {
10838 s = series[i];
10839 points = s.datapoints.points;
10840 ps = s.datapoints.pointsize;
10841 format = s.datapoints.format;
10842
10843 var xmin = topSentry, ymin = topSentry,
10844 xmax = bottomSentry, ymax = bottomSentry;
10845
10846 for (j = 0; j < points.length; j += ps) {
10847 if (points[j] == null)
10848 continue;
10849
10850 for (m = 0; m < ps; ++m) {
10851 val = points[j + m];
10852 f = format[m];
10853 if (!f || f.autoscale === false || val == fakeInfinity || val == -fakeInfinity)
10854 continue;
10855
10856 if (f.x) {
10857 if (val < xmin)
10858 xmin = val;
10859 if (val > xmax)
10860 xmax = val;
10861 }
10862 if (f.y) {
10863 if (val < ymin)
10864 ymin = val;
10865 if (val > ymax)
10866 ymax = val;
10867 }
10868 }
10869 }
10870
10871 if (s.bars.show) {
10872 // make sure we got room for the bar on the dancing floor
10873 var delta;
10874
10875 switch (s.bars.align) {
10876 case "left":
10877 delta = 0;
10878 break;
10879 case "right":
10880 delta = -s.bars.barWidth;
10881 break;
10882 default:
10883 delta = -s.bars.barWidth / 2;
10884 }
10885
10886 if (s.bars.horizontal) {
10887 ymin += delta;
10888 ymax += delta + s.bars.barWidth;
10889 }
10890 else {
10891 xmin += delta;
10892 xmax += delta + s.bars.barWidth;
10893 }
10894 }
10895
10896 updateAxis(s.xaxis, xmin, xmax);
10897 updateAxis(s.yaxis, ymin, ymax);
10898 }
10899
10900 $.each(allAxes(), function (_, axis) {
10901 if (axis.datamin == topSentry)
10902 axis.datamin = null;
10903 if (axis.datamax == bottomSentry)
10904 axis.datamax = null;
10905 });
10906 }
10907
10908 function setupCanvases() {
10909
10910 // Make sure the placeholder is clear of everything except canvases
10911 // from a previous plot in this container that we'll try to re-use.
10912
10913 placeholder.css("padding", 0) // padding messes up the positioning
10914 .children().filter(function(){
10915 return !$(this).hasClass("flot-overlay") && !$(this).hasClass('flot-base');
10916 }).remove();
10917
10918 if (placeholder.css("position") == 'static')
10919 placeholder.css("position", "relative"); // for positioning labels and overlay
10920
10921 surface = new Canvas("flot-base", placeholder);
10922 overlay = new Canvas("flot-overlay", placeholder); // overlay canvas for interactive features
10923
10924 ctx = surface.context;
10925 octx = overlay.context;
10926
10927 // define which element we're listening for events on
10928 eventHolder = $(overlay.element).unbind();
10929
10930 // If we're re-using a plot object, shut down the old one
10931
10932 var existing = placeholder.data("plot");
10933
10934 if (existing) {
10935 existing.shutdown();
10936 overlay.clear();
10937 }
10938
10939 // save in case we get replotted
10940 placeholder.data("plot", plot);
10941 }
10942
10943 function bindEvents() {
10944 // bind events
10945 if (options.grid.hoverable) {
10946 eventHolder.mousemove(onMouseMove);
10947
10948 // Use bind, rather than .mouseleave, because we officially
10949 // still support jQuery 1.2.6, which doesn't define a shortcut
10950 // for mouseenter or mouseleave. This was a bug/oversight that
10951 // was fixed somewhere around 1.3.x. We can return to using
10952 // .mouseleave when we drop support for 1.2.6.
10953
10954 eventHolder.bind("mouseleave", onMouseLeave);
10955 }
10956
10957 if (options.grid.clickable)
10958 eventHolder.click(onClick);
10959
10960 executeHooks(hooks.bindEvents, [eventHolder]);
10961 }
10962
10963 function shutdown() {
10964 if (redrawTimeout)
10965 clearTimeout(redrawTimeout);
10966
10967 eventHolder.unbind("mousemove", onMouseMove);
10968 eventHolder.unbind("mouseleave", onMouseLeave);
10969 eventHolder.unbind("click", onClick);
10970
10971 executeHooks(hooks.shutdown, [eventHolder]);
10972 }
10973
10974 function setTransformationHelpers(axis) {
10975 // set helper functions on the axis, assumes plot area
10976 // has been computed already
10977
10978 function identity(x) { return x; }
10979
10980 var s, m, t = axis.options.transform || identity,
10981 it = axis.options.inverseTransform;
10982
10983 // precompute how much the axis is scaling a point
10984 // in canvas space
10985 if (axis.direction == "x") {
10986 s = axis.scale = plotWidth / Math.abs(t(axis.max) - t(axis.min));
10987 m = Math.min(t(axis.max), t(axis.min));
10988 }
10989 else {
10990 s = axis.scale = plotHeight / Math.abs(t(axis.max) - t(axis.min));
10991 s = -s;
10992 m = Math.max(t(axis.max), t(axis.min));
10993 }
10994
10995 // data point to canvas coordinate
10996 if (t == identity) // slight optimization
10997 axis.p2c = function (p) { return (p - m) * s; };
10998 else
10999 axis.p2c = function (p) { return (t(p) - m) * s; };
11000 // canvas coordinate to data point
11001 if (!it)
11002 axis.c2p = function (c) { return m + c / s; };
11003 else
11004 axis.c2p = function (c) { return it(m + c / s); };
11005 }
11006
11007 function measureTickLabels(axis) {
11008
11009 var opts = axis.options,
11010 ticks = axis.ticks || [],
11011 labelWidth = opts.labelWidth || 0,
11012 labelHeight = opts.labelHeight || 0,
11013 maxWidth = labelWidth || (axis.direction == "x" ? Math.floor(surface.width / (ticks.length || 1)) : null),
11014 legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
11015 layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
11016 font = opts.font || "flot-tick-label tickLabel";
11017
11018 for (var i = 0; i < ticks.length; ++i) {
11019
11020 var t = ticks[i];
11021
11022 if (!t.label)
11023 continue;
11024
11025 var info = surface.getTextInfo(layer, t.label, font, null, maxWidth);
11026
11027 labelWidth = Math.max(labelWidth, info.width);
11028 labelHeight = Math.max(labelHeight, info.height);
11029 }
11030
11031 axis.labelWidth = opts.labelWidth || labelWidth;
11032 axis.labelHeight = opts.labelHeight || labelHeight;
11033 }
11034
11035 function allocateAxisBoxFirstPhase(axis) {
11036 // find the bounding box of the axis by looking at label
11037 // widths/heights and ticks, make room by diminishing the
11038 // plotOffset; this first phase only looks at one
11039 // dimension per axis, the other dimension depends on the
11040 // other axes so will have to wait
11041
11042 var lw = axis.labelWidth,
11043 lh = axis.labelHeight,
11044 pos = axis.options.position,
11045 isXAxis = axis.direction === "x",
11046 tickLength = axis.options.tickLength,
11047 axisMargin = options.grid.axisMargin,
11048 padding = options.grid.labelMargin,
11049 innermost = true,
11050 outermost = true,
11051 first = true,
11052 found = false;
11053
11054 // Determine the axis's position in its direction and on its side
11055
11056 $.each(isXAxis ? xaxes : yaxes, function(i, a) {
11057 if (a && (a.show || a.reserveSpace)) {
11058 if (a === axis) {
11059 found = true;
11060 } else if (a.options.position === pos) {
11061 if (found) {
11062 outermost = false;
11063 } else {
11064 innermost = false;
11065 }
11066 }
11067 if (!found) {
11068 first = false;
11069 }
11070 }
11071 });
11072
11073 // The outermost axis on each side has no margin
11074
11075 if (outermost) {
11076 axisMargin = 0;
11077 }
11078
11079 // The ticks for the first axis in each direction stretch across
11080
11081 if (tickLength == null) {
11082 tickLength = first ? "full" : 5;
11083 }
11084
11085 if (!isNaN(+tickLength))
11086 padding += +tickLength;
11087
11088 if (isXAxis) {
11089 lh += padding;
11090
11091 if (pos == "bottom") {
11092 plotOffset.bottom += lh + axisMargin;
11093 axis.box = { top: surface.height - plotOffset.bottom, height: lh };
11094 }
11095 else {
11096 axis.box = { top: plotOffset.top + axisMargin, height: lh };
11097 plotOffset.top += lh + axisMargin;
11098 }
11099 }
11100 else {
11101 lw += padding;
11102
11103 if (pos == "left") {
11104 axis.box = { left: plotOffset.left + axisMargin, width: lw };
11105 plotOffset.left += lw + axisMargin;
11106 }
11107 else {
11108 plotOffset.right += lw + axisMargin;
11109 axis.box = { left: surface.width - plotOffset.right, width: lw };
11110 }
11111 }
11112
11113 // save for future reference
11114 axis.position = pos;
11115 axis.tickLength = tickLength;
11116 axis.box.padding = padding;
11117 axis.innermost = innermost;
11118 }
11119
11120 function allocateAxisBoxSecondPhase(axis) {
11121 // now that all axis boxes have been placed in one
11122 // dimension, we can set the remaining dimension coordinates
11123 if (axis.direction == "x") {
11124 axis.box.left = plotOffset.left - axis.labelWidth / 2;
11125 axis.box.width = surface.width - plotOffset.left - plotOffset.right + axis.labelWidth;
11126 }
11127 else {
11128 axis.box.top = plotOffset.top - axis.labelHeight / 2;
11129 axis.box.height = surface.height - plotOffset.bottom - plotOffset.top + axis.labelHeight;
11130 }
11131 }
11132
11133 function adjustLayoutForThingsStickingOut() {
11134 // possibly adjust plot offset to ensure everything stays
11135 // inside the canvas and isn't clipped off
11136
11137 var minMargin = options.grid.minBorderMargin,
11138 axis, i;
11139
11140 // check stuff from the plot (FIXME: this should just read
11141 // a value from the series, otherwise it's impossible to
11142 // customize)
11143 if (minMargin == null) {
11144 minMargin = 0;
11145 for (i = 0; i < series.length; ++i)
11146 minMargin = Math.max(minMargin, 2 * (series[i].points.radius + series[i].points.lineWidth/2));
11147 }
11148
11149 var margins = {
11150 left: minMargin,
11151 right: minMargin,
11152 top: minMargin,
11153 bottom: minMargin
11154 };
11155
11156 // check axis labels, note we don't check the actual
11157 // labels but instead use the overall width/height to not
11158 // jump as much around with replots
11159 $.each(allAxes(), function (_, axis) {
11160 if (axis.reserveSpace && axis.ticks && axis.ticks.length) {
11161 if (axis.direction === "x") {
11162 margins.left = Math.max(margins.left, axis.labelWidth / 2);
11163 margins.right = Math.max(margins.right, axis.labelWidth / 2);
11164 } else {
11165 margins.bottom = Math.max(margins.bottom, axis.labelHeight / 2);
11166 margins.top = Math.max(margins.top, axis.labelHeight / 2);
11167 }
11168 }
11169 });
11170
11171 plotOffset.left = Math.ceil(Math.max(margins.left, plotOffset.left));
11172 plotOffset.right = Math.ceil(Math.max(margins.right, plotOffset.right));
11173 plotOffset.top = Math.ceil(Math.max(margins.top, plotOffset.top));
11174 plotOffset.bottom = Math.ceil(Math.max(margins.bottom, plotOffset.bottom));
11175 }
11176
11177 function setupGrid() {
11178 var i, axes = allAxes(), showGrid = options.grid.show;
11179
11180 // Initialize the plot's offset from the edge of the canvas
11181
11182 for (var a in plotOffset) {
11183 var margin = options.grid.margin || 0;
11184 plotOffset[a] = typeof margin == "number" ? margin : margin[a] || 0;
11185 }
11186
11187 executeHooks(hooks.processOffset, [plotOffset]);
11188
11189 // If the grid is visible, add its border width to the offset
11190
11191 for (var a in plotOffset) {
11192 if(typeof(options.grid.borderWidth) == "object") {
11193 plotOffset[a] += showGrid ? options.grid.borderWidth[a] : 0;
11194 }
11195 else {
11196 plotOffset[a] += showGrid ? options.grid.borderWidth : 0;
11197 }
11198 }
11199
11200 $.each(axes, function (_, axis) {
11201 var axisOpts = axis.options;
11202 axis.show = axisOpts.show == null ? axis.used : axisOpts.show;
11203 axis.reserveSpace = axisOpts.reserveSpace == null ? axis.show : axisOpts.reserveSpace;
11204 setRange(axis);
11205 });
11206
11207 if (showGrid) {
11208
11209 var allocatedAxes = $.grep(axes, function (axis) {
11210 return axis.show || axis.reserveSpace;
11211 });
11212
11213 $.each(allocatedAxes, function (_, axis) {
11214 // make the ticks
11215 setupTickGeneration(axis);
11216 setTicks(axis);
11217 snapRangeToTicks(axis, axis.ticks);
11218 // find labelWidth/Height for axis
11219 measureTickLabels(axis);
11220 });
11221
11222 // with all dimensions calculated, we can compute the
11223 // axis bounding boxes, start from the outside
11224 // (reverse order)
11225 for (i = allocatedAxes.length - 1; i >= 0; --i)
11226 allocateAxisBoxFirstPhase(allocatedAxes[i]);
11227
11228 // make sure we've got enough space for things that
11229 // might stick out
11230 adjustLayoutForThingsStickingOut();
11231
11232 $.each(allocatedAxes, function (_, axis) {
11233 allocateAxisBoxSecondPhase(axis);
11234 });
11235 }
11236
11237 plotWidth = surface.width - plotOffset.left - plotOffset.right;
11238 plotHeight = surface.height - plotOffset.bottom - plotOffset.top;
11239
11240 // now we got the proper plot dimensions, we can compute the scaling
11241 $.each(axes, function (_, axis) {
11242 setTransformationHelpers(axis);
11243 });
11244
11245 if (showGrid) {
11246 drawAxisLabels();
11247 }
11248
11249 insertLegend();
11250 }
11251
11252 function setRange(axis) {
11253 var opts = axis.options,
11254 min = +(opts.min != null ? opts.min : axis.datamin),
11255 max = +(opts.max != null ? opts.max : axis.datamax),
11256 delta = max - min;
11257
11258 if (delta == 0.0) {
11259 // degenerate case
11260 var widen = max == 0 ? 1 : 0.01;
11261
11262 if (opts.min == null)
11263 min -= widen;
11264 // always widen max if we couldn't widen min to ensure we
11265 // don't fall into min == max which doesn't work
11266 if (opts.max == null || opts.min != null)
11267 max += widen;
11268 }
11269 else {
11270 // consider autoscaling
11271 var margin = opts.autoscaleMargin;
11272 if (margin != null) {
11273 if (opts.min == null) {
11274 min -= delta * margin;
11275 // make sure we don't go below zero if all values
11276 // are positive
11277 if (min < 0 && axis.datamin != null && axis.datamin >= 0)
11278 min = 0;
11279 }
11280 if (opts.max == null) {
11281 max += delta * margin;
11282 if (max > 0 && axis.datamax != null && axis.datamax <= 0)
11283 max = 0;
11284 }
11285 }
11286 }
11287 axis.min = min;
11288 axis.max = max;
11289 }
11290
11291 function setupTickGeneration(axis) {
11292 var opts = axis.options;
11293
11294 // estimate number of ticks
11295 var noTicks;
11296 if (typeof opts.ticks == "number" && opts.ticks > 0)
11297 noTicks = opts.ticks;
11298 else
11299 // heuristic based on the model a*sqrt(x) fitted to
11300 // some data points that seemed reasonable
11301 noTicks = 0.3 * Math.sqrt(axis.direction == "x" ? surface.width : surface.height);
11302
11303 var delta = (axis.max - axis.min) / noTicks,
11304 dec = -Math.floor(Math.log(delta) / Math.LN10),
11305 maxDec = opts.tickDecimals;
11306
11307 if (maxDec != null && dec > maxDec) {
11308 dec = maxDec;
11309 }
11310
11311 var magn = Math.pow(10, -dec),
11312 norm = delta / magn, // norm is between 1.0 and 10.0
11313 size;
11314
11315 if (norm < 1.5) {
11316 size = 1;
11317 } else if (norm < 3) {
11318 size = 2;
11319 // special case for 2.5, requires an extra decimal
11320 if (norm > 2.25 && (maxDec == null || dec + 1 <= maxDec)) {
11321 size = 2.5;
11322 ++dec;
11323 }
11324 } else if (norm < 7.5) {
11325 size = 5;
11326 } else {
11327 size = 10;
11328 }
11329
11330 size *= magn;
11331
11332 if (opts.minTickSize != null && size < opts.minTickSize) {
11333 size = opts.minTickSize;
11334 }
11335
11336 axis.delta = delta;
11337 axis.tickDecimals = Math.max(0, maxDec != null ? maxDec : dec);
11338 axis.tickSize = opts.tickSize || size;
11339
11340 // Time mode was moved to a plug-in in 0.8, and since so many people use it
11341 // we'll add an especially friendly reminder to make sure they included it.
11342
11343 if (opts.mode == "time" && !axis.tickGenerator) {
11344 throw new Error("Time mode requires the flot.time plugin.");
11345 }
11346
11347 // Flot supports base-10 axes; any other mode else is handled by a plug-in,
11348 // like flot.time.js.
11349
11350 if (!axis.tickGenerator) {
11351
11352 axis.tickGenerator = function (axis) {
11353
11354 var ticks = [],
11355 start = floorInBase(axis.min, axis.tickSize),
11356 i = 0,
11357 v = Number.NaN,
11358 prev;
11359
11360 do {
11361 prev = v;
11362 v = start + i * axis.tickSize;
11363 ticks.push(v);
11364 ++i;
11365 } while (v < axis.max && v != prev);
11366 return ticks;
11367 };
11368
11369 axis.tickFormatter = function (value, axis) {
11370
11371 var factor = axis.tickDecimals ? Math.pow(10, axis.tickDecimals) : 1;
11372 var formatted = "" + Math.round(value * factor) / factor;
11373
11374 // If tickDecimals was specified, ensure that we have exactly that
11375 // much precision; otherwise default to the value's own precision.
11376
11377 if (axis.tickDecimals != null) {
11378 var decimal = formatted.indexOf(".");
11379 var precision = decimal == -1 ? 0 : formatted.length - decimal - 1;
11380 if (precision < axis.tickDecimals) {
11381 return (precision ? formatted : formatted + ".") + ("" + factor).substr(1, axis.tickDecimals - precision);
11382 }
11383 }
11384
11385 return formatted;
11386 };
11387 }
11388
11389 if ($.isFunction(opts.tickFormatter))
11390 axis.tickFormatter = function (v, axis) { return "" + opts.tickFormatter(v, axis); };
11391
11392 if (opts.alignTicksWithAxis != null) {
11393 var otherAxis = (axis.direction == "x" ? xaxes : yaxes)[opts.alignTicksWithAxis - 1];
11394 if (otherAxis && otherAxis.used && otherAxis != axis) {
11395 // consider snapping min/max to outermost nice ticks
11396 var niceTicks = axis.tickGenerator(axis);
11397 if (niceTicks.length > 0) {
11398 if (opts.min == null)
11399 axis.min = Math.min(axis.min, niceTicks[0]);
11400 if (opts.max == null && niceTicks.length > 1)
11401 axis.max = Math.max(axis.max, niceTicks[niceTicks.length - 1]);
11402 }
11403
11404 axis.tickGenerator = function (axis) {
11405 // copy ticks, scaled to this axis
11406 var ticks = [], v, i;
11407 for (i = 0; i < otherAxis.ticks.length; ++i) {
11408 v = (otherAxis.ticks[i].v - otherAxis.min) / (otherAxis.max - otherAxis.min);
11409 v = axis.min + v * (axis.max - axis.min);
11410 ticks.push(v);
11411 }
11412 return ticks;
11413 };
11414
11415 // we might need an extra decimal since forced
11416 // ticks don't necessarily fit naturally
11417 if (!axis.mode && opts.tickDecimals == null) {
11418 var extraDec = Math.max(0, -Math.floor(Math.log(axis.delta) / Math.LN10) + 1),
11419 ts = axis.tickGenerator(axis);
11420
11421 // only proceed if the tick interval rounded
11422 // with an extra decimal doesn't give us a
11423 // zero at end
11424 if (!(ts.length > 1 && /\..*0$/.test((ts[1] - ts[0]).toFixed(extraDec))))
11425 axis.tickDecimals = extraDec;
11426 }
11427 }
11428 }
11429 }
11430
11431 function setTicks(axis) {
11432 var oticks = axis.options.ticks, ticks = [];
11433 if (oticks == null || (typeof oticks == "number" && oticks > 0))
11434 ticks = axis.tickGenerator(axis);
11435 else if (oticks) {
11436 if ($.isFunction(oticks))
11437 // generate the ticks
11438 ticks = oticks(axis);
11439 else
11440 ticks = oticks;
11441 }
11442
11443 // clean up/labelify the supplied ticks, copy them over
11444 var i, v;
11445 axis.ticks = [];
11446 for (i = 0; i < ticks.length; ++i) {
11447 var label = null;
11448 var t = ticks[i];
11449 if (typeof t == "object") {
11450 v = +t[0];
11451 if (t.length > 1)
11452 label = t[1];
11453 }
11454 else
11455 v = +t;
11456 if (label == null)
11457 label = axis.tickFormatter(v, axis);
11458 if (!isNaN(v))
11459 axis.ticks.push({ v: v, label: label });
11460 }
11461 }
11462
11463 function snapRangeToTicks(axis, ticks) {
11464 if (axis.options.autoscaleMargin && ticks.length > 0) {
11465 // snap to ticks
11466 if (axis.options.min == null)
11467 axis.min = Math.min(axis.min, ticks[0].v);
11468 if (axis.options.max == null && ticks.length > 1)
11469 axis.max = Math.max(axis.max, ticks[ticks.length - 1].v);
11470 }
11471 }
11472
11473 function draw() {
11474
11475 surface.clear();
11476
11477 executeHooks(hooks.drawBackground, [ctx]);
11478
11479 var grid = options.grid;
11480
11481 // draw background, if any
11482 if (grid.show && grid.backgroundColor)
11483 drawBackground();
11484
11485 if (grid.show && !grid.aboveData) {
11486 drawGrid();
11487 }
11488
11489 for (var i = 0; i < series.length; ++i) {
11490 executeHooks(hooks.drawSeries, [ctx, series[i]]);
11491 drawSeries(series[i]);
11492 }
11493
11494 executeHooks(hooks.draw, [ctx]);
11495
11496 if (grid.show && grid.aboveData) {
11497 drawGrid();
11498 }
11499
11500 surface.render();
11501
11502 // A draw implies that either the axes or data have changed, so we
11503 // should probably update the overlay highlights as well.
11504
11505 triggerRedrawOverlay();
11506 }
11507
11508 function extractRange(ranges, coord) {
11509 var axis, from, to, key, axes = allAxes();
11510
11511 for (var i = 0; i < axes.length; ++i) {
11512 axis = axes[i];
11513 if (axis.direction == coord) {
11514 key = coord + axis.n + "axis";
11515 if (!ranges[key] && axis.n == 1)
11516 key = coord + "axis"; // support x1axis as xaxis
11517 if (ranges[key]) {
11518 from = ranges[key].from;
11519 to = ranges[key].to;
11520 break;
11521 }
11522 }
11523 }
11524
11525 // backwards-compat stuff - to be removed in future
11526 if (!ranges[key]) {
11527 axis = coord == "x" ? xaxes[0] : yaxes[0];
11528 from = ranges[coord + "1"];
11529 to = ranges[coord + "2"];
11530 }
11531
11532 // auto-reverse as an added bonus
11533 if (from != null && to != null && from > to) {
11534 var tmp = from;
11535 from = to;
11536 to = tmp;
11537 }
11538
11539 return { from: from, to: to, axis: axis };
11540 }
11541
11542 function drawBackground() {
11543 ctx.save();
11544 ctx.translate(plotOffset.left, plotOffset.top);
11545
11546 ctx.fillStyle = getColorOrGradient(options.grid.backgroundColor, plotHeight, 0, "rgba(255, 255, 255, 0)");
11547 ctx.fillRect(0, 0, plotWidth, plotHeight);
11548 ctx.restore();
11549 }
11550
11551 function drawGrid() {
11552 var i, axes, bw, bc;
11553
11554 ctx.save();
11555 ctx.translate(plotOffset.left, plotOffset.top);
11556
11557 // draw markings
11558 var markings = options.grid.markings;
11559 if (markings) {
11560 if ($.isFunction(markings)) {
11561 axes = plot.getAxes();
11562 // xmin etc. is backwards compatibility, to be
11563 // removed in the future
11564 axes.xmin = axes.xaxis.min;
11565 axes.xmax = axes.xaxis.max;
11566 axes.ymin = axes.yaxis.min;
11567 axes.ymax = axes.yaxis.max;
11568
11569 markings = markings(axes);
11570 }
11571
11572 for (i = 0; i < markings.length; ++i) {
11573 var m = markings[i],
11574 xrange = extractRange(m, "x"),
11575 yrange = extractRange(m, "y");
11576
11577 // fill in missing
11578 if (xrange.from == null)
11579 xrange.from = xrange.axis.min;
11580 if (xrange.to == null)
11581 xrange.to = xrange.axis.max;
11582 if (yrange.from == null)
11583 yrange.from = yrange.axis.min;
11584 if (yrange.to == null)
11585 yrange.to = yrange.axis.max;
11586
11587 // clip
11588 if (xrange.to < xrange.axis.min || xrange.from > xrange.axis.max ||
11589 yrange.to < yrange.axis.min || yrange.from > yrange.axis.max)
11590 continue;
11591
11592 xrange.from = Math.max(xrange.from, xrange.axis.min);
11593 xrange.to = Math.min(xrange.to, xrange.axis.max);
11594 yrange.from = Math.max(yrange.from, yrange.axis.min);
11595 yrange.to = Math.min(yrange.to, yrange.axis.max);
11596
11597 var xequal = xrange.from === xrange.to,
11598 yequal = yrange.from === yrange.to;
11599
11600 if (xequal && yequal) {
11601 continue;
11602 }
11603
11604 // then draw
11605 xrange.from = Math.floor(xrange.axis.p2c(xrange.from));
11606 xrange.to = Math.floor(xrange.axis.p2c(xrange.to));
11607 yrange.from = Math.floor(yrange.axis.p2c(yrange.from));
11608 yrange.to = Math.floor(yrange.axis.p2c(yrange.to));
11609
11610 if (xequal || yequal) {
11611 var lineWidth = m.lineWidth || options.grid.markingsLineWidth,
11612 subPixel = lineWidth % 2 ? 0.5 : 0;
11613 ctx.beginPath();
11614 ctx.strokeStyle = m.color || options.grid.markingsColor;
11615 ctx.lineWidth = lineWidth;
11616 if (xequal) {
11617 ctx.moveTo(xrange.to + subPixel, yrange.from);
11618 ctx.lineTo(xrange.to + subPixel, yrange.to);
11619 } else {
11620 ctx.moveTo(xrange.from, yrange.to + subPixel);
11621 ctx.lineTo(xrange.to, yrange.to + subPixel);
11622 }
11623 ctx.stroke();
11624 } else {
11625 ctx.fillStyle = m.color || options.grid.markingsColor;
11626 ctx.fillRect(xrange.from, yrange.to,
11627 xrange.to - xrange.from,
11628 yrange.from - yrange.to);
11629 }
11630 }
11631 }
11632
11633 // draw the ticks
11634 axes = allAxes();
11635 bw = options.grid.borderWidth;
11636
11637 for (var j = 0; j < axes.length; ++j) {
11638 var axis = axes[j], box = axis.box,
11639 t = axis.tickLength, x, y, xoff, yoff;
11640 if (!axis.show || axis.ticks.length == 0)
11641 continue;
11642
11643 ctx.lineWidth = 1;
11644
11645 // find the edges
11646 if (axis.direction == "x") {
11647 x = 0;
11648 if (t == "full")
11649 y = (axis.position == "top" ? 0 : plotHeight);
11650 else
11651 y = box.top - plotOffset.top + (axis.position == "top" ? box.height : 0);
11652 }
11653 else {
11654 y = 0;
11655 if (t == "full")
11656 x = (axis.position == "left" ? 0 : plotWidth);
11657 else
11658 x = box.left - plotOffset.left + (axis.position == "left" ? box.width : 0);
11659 }
11660
11661 // draw tick bar
11662 if (!axis.innermost) {
11663 ctx.strokeStyle = axis.options.color;
11664 ctx.beginPath();
11665 xoff = yoff = 0;
11666 if (axis.direction == "x")
11667 xoff = plotWidth + 1;
11668 else
11669 yoff = plotHeight + 1;
11670
11671 if (ctx.lineWidth == 1) {
11672 if (axis.direction == "x") {
11673 y = Math.floor(y) + 0.5;
11674 } else {
11675 x = Math.floor(x) + 0.5;
11676 }
11677 }
11678
11679 ctx.moveTo(x, y);
11680 ctx.lineTo(x + xoff, y + yoff);
11681 ctx.stroke();
11682 }
11683
11684 // draw ticks
11685
11686 ctx.strokeStyle = axis.options.tickColor;
11687
11688 ctx.beginPath();
11689 for (i = 0; i < axis.ticks.length; ++i) {
11690 var v = axis.ticks[i].v;
11691
11692 xoff = yoff = 0;
11693
11694 if (isNaN(v) || v < axis.min || v > axis.max
11695 // skip those lying on the axes if we got a border
11696 || (t == "full"
11697 && ((typeof bw == "object" && bw[axis.position] > 0) || bw > 0)
11698 && (v == axis.min || v == axis.max)))
11699 continue;
11700
11701 if (axis.direction == "x") {
11702 x = axis.p2c(v);
11703 yoff = t == "full" ? -plotHeight : t;
11704
11705 if (axis.position == "top")
11706 yoff = -yoff;
11707 }
11708 else {
11709 y = axis.p2c(v);
11710 xoff = t == "full" ? -plotWidth : t;
11711
11712 if (axis.position == "left")
11713 xoff = -xoff;
11714 }
11715
11716 if (ctx.lineWidth == 1) {
11717 if (axis.direction == "x")
11718 x = Math.floor(x) + 0.5;
11719 else
11720 y = Math.floor(y) + 0.5;
11721 }
11722
11723 ctx.moveTo(x, y);
11724 ctx.lineTo(x + xoff, y + yoff);
11725 }
11726
11727 ctx.stroke();
11728 }
11729
11730
11731 // draw border
11732 if (bw) {
11733 // If either borderWidth or borderColor is an object, then draw the border
11734 // line by line instead of as one rectangle
11735 bc = options.grid.borderColor;
11736 if(typeof bw == "object" || typeof bc == "object") {
11737 if (typeof bw !== "object") {
11738 bw = {top: bw, right: bw, bottom: bw, left: bw};
11739 }
11740 if (typeof bc !== "object") {
11741 bc = {top: bc, right: bc, bottom: bc, left: bc};
11742 }
11743
11744 if (bw.top > 0) {
11745 ctx.strokeStyle = bc.top;
11746 ctx.lineWidth = bw.top;
11747 ctx.beginPath();
11748 ctx.moveTo(0 - bw.left, 0 - bw.top/2);
11749 ctx.lineTo(plotWidth, 0 - bw.top/2);
11750 ctx.stroke();
11751 }
11752
11753 if (bw.right > 0) {
11754 ctx.strokeStyle = bc.right;
11755 ctx.lineWidth = bw.right;
11756 ctx.beginPath();
11757 ctx.moveTo(plotWidth + bw.right / 2, 0 - bw.top);
11758 ctx.lineTo(plotWidth + bw.right / 2, plotHeight);
11759 ctx.stroke();
11760 }
11761
11762 if (bw.bottom > 0) {
11763 ctx.strokeStyle = bc.bottom;
11764 ctx.lineWidth = bw.bottom;
11765 ctx.beginPath();
11766 ctx.moveTo(plotWidth + bw.right, plotHeight + bw.bottom / 2);
11767 ctx.lineTo(0, plotHeight + bw.bottom / 2);
11768 ctx.stroke();
11769 }
11770
11771 if (bw.left > 0) {
11772 ctx.strokeStyle = bc.left;
11773 ctx.lineWidth = bw.left;
11774 ctx.beginPath();
11775 ctx.moveTo(0 - bw.left/2, plotHeight + bw.bottom);
11776 ctx.lineTo(0- bw.left/2, 0);
11777 ctx.stroke();
11778 }
11779 }
11780 else {
11781 ctx.lineWidth = bw;
11782 ctx.strokeStyle = options.grid.borderColor;
11783 ctx.strokeRect(-bw/2, -bw/2, plotWidth + bw, plotHeight + bw);
11784 }
11785 }
11786
11787 ctx.restore();
11788 }
11789
11790 function drawAxisLabels() {
11791
11792 $.each(allAxes(), function (_, axis) {
11793 var box = axis.box,
11794 legacyStyles = axis.direction + "Axis " + axis.direction + axis.n + "Axis",
11795 layer = "flot-" + axis.direction + "-axis flot-" + axis.direction + axis.n + "-axis " + legacyStyles,
11796 font = axis.options.font || "flot-tick-label tickLabel",
11797 tick, x, y, halign, valign;
11798
11799 // Remove text before checking for axis.show and ticks.length;
11800 // otherwise plugins, like flot-tickrotor, that draw their own
11801 // tick labels will end up with both theirs and the defaults.
11802
11803 surface.removeText(layer);
11804
11805 if (!axis.show || axis.ticks.length == 0)
11806 return;
11807
11808 for (var i = 0; i < axis.ticks.length; ++i) {
11809
11810 tick = axis.ticks[i];
11811 if (!tick.label || tick.v < axis.min || tick.v > axis.max)
11812 continue;
11813
11814 if (axis.direction == "x") {
11815 halign = "center";
11816 x = plotOffset.left + axis.p2c(tick.v);
11817 if (axis.position == "bottom") {
11818 y = box.top + box.padding;
11819 } else {
11820 y = box.top + box.height - box.padding;
11821 valign = "bottom";
11822 }
11823 } else {
11824 valign = "middle";
11825 y = plotOffset.top + axis.p2c(tick.v);
11826 if (axis.position == "left") {
11827 x = box.left + box.width - box.padding;
11828 halign = "right";
11829 } else {
11830 x = box.left + box.padding;
11831 }
11832 }
11833
11834 surface.addText(layer, x, y, tick.label, font, null, null, halign, valign);
11835 }
11836 });
11837 }
11838
11839 function drawSeries(series) {
11840 if (series.lines.show)
11841 drawSeriesLines(series);
11842 if (series.bars.show)
11843 drawSeriesBars(series);
11844 if (series.points.show)
11845 drawSeriesPoints(series);
11846 }
11847
11848 function drawSeriesLines(series) {
11849 function plotLine(datapoints, xoffset, yoffset, axisx, axisy) {
11850 var points = datapoints.points,
11851 ps = datapoints.pointsize,
11852 prevx = null, prevy = null;
11853
11854 ctx.beginPath();
11855 for (var i = ps; i < points.length; i += ps) {
11856 var x1 = points[i - ps], y1 = points[i - ps + 1],
11857 x2 = points[i], y2 = points[i + 1];
11858
11859 if (x1 == null || x2 == null)
11860 continue;
11861
11862 // clip with ymin
11863 if (y1 <= y2 && y1 < axisy.min) {
11864 if (y2 < axisy.min)
11865 continue; // line segment is outside
11866 // compute new intersection point
11867 x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
11868 y1 = axisy.min;
11869 }
11870 else if (y2 <= y1 && y2 < axisy.min) {
11871 if (y1 < axisy.min)
11872 continue;
11873 x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
11874 y2 = axisy.min;
11875 }
11876
11877 // clip with ymax
11878 if (y1 >= y2 && y1 > axisy.max) {
11879 if (y2 > axisy.max)
11880 continue;
11881 x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
11882 y1 = axisy.max;
11883 }
11884 else if (y2 >= y1 && y2 > axisy.max) {
11885 if (y1 > axisy.max)
11886 continue;
11887 x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
11888 y2 = axisy.max;
11889 }
11890
11891 // clip with xmin
11892 if (x1 <= x2 && x1 < axisx.min) {
11893 if (x2 < axisx.min)
11894 continue;
11895 y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
11896 x1 = axisx.min;
11897 }
11898 else if (x2 <= x1 && x2 < axisx.min) {
11899 if (x1 < axisx.min)
11900 continue;
11901 y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
11902 x2 = axisx.min;
11903 }
11904
11905 // clip with xmax
11906 if (x1 >= x2 && x1 > axisx.max) {
11907 if (x2 > axisx.max)
11908 continue;
11909 y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
11910 x1 = axisx.max;
11911 }
11912 else if (x2 >= x1 && x2 > axisx.max) {
11913 if (x1 > axisx.max)
11914 continue;
11915 y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
11916 x2 = axisx.max;
11917 }
11918
11919 if (x1 != prevx || y1 != prevy)
11920 ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
11921
11922 prevx = x2;
11923 prevy = y2;
11924 ctx.lineTo(axisx.p2c(x2) + xoffset, axisy.p2c(y2) + yoffset);
11925 }
11926 ctx.stroke();
11927 }
11928
11929 function plotLineArea(datapoints, axisx, axisy) {
11930 var points = datapoints.points,
11931 ps = datapoints.pointsize,
11932 bottom = Math.min(Math.max(0, axisy.min), axisy.max),
11933 i = 0, top, areaOpen = false,
11934 ypos = 1, segmentStart = 0, segmentEnd = 0;
11935
11936 // we process each segment in two turns, first forward
11937 // direction to sketch out top, then once we hit the
11938 // end we go backwards to sketch the bottom
11939 while (true) {
11940 if (ps > 0 && i > points.length + ps)
11941 break;
11942
11943 i += ps; // ps is negative if going backwards
11944
11945 var x1 = points[i - ps],
11946 y1 = points[i - ps + ypos],
11947 x2 = points[i], y2 = points[i + ypos];
11948
11949 if (areaOpen) {
11950 if (ps > 0 && x1 != null && x2 == null) {
11951 // at turning point
11952 segmentEnd = i;
11953 ps = -ps;
11954 ypos = 2;
11955 continue;
11956 }
11957
11958 if (ps < 0 && i == segmentStart + ps) {
11959 // done with the reverse sweep
11960 ctx.fill();
11961 areaOpen = false;
11962 ps = -ps;
11963 ypos = 1;
11964 i = segmentStart = segmentEnd + ps;
11965 continue;
11966 }
11967 }
11968
11969 if (x1 == null || x2 == null)
11970 continue;
11971
11972 // clip x values
11973
11974 // clip with xmin
11975 if (x1 <= x2 && x1 < axisx.min) {
11976 if (x2 < axisx.min)
11977 continue;
11978 y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
11979 x1 = axisx.min;
11980 }
11981 else if (x2 <= x1 && x2 < axisx.min) {
11982 if (x1 < axisx.min)
11983 continue;
11984 y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
11985 x2 = axisx.min;
11986 }
11987
11988 // clip with xmax
11989 if (x1 >= x2 && x1 > axisx.max) {
11990 if (x2 > axisx.max)
11991 continue;
11992 y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
11993 x1 = axisx.max;
11994 }
11995 else if (x2 >= x1 && x2 > axisx.max) {
11996 if (x1 > axisx.max)
11997 continue;
11998 y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
11999 x2 = axisx.max;
12000 }
12001
12002 if (!areaOpen) {
12003 // open area
12004 ctx.beginPath();
12005 ctx.moveTo(axisx.p2c(x1), axisy.p2c(bottom));
12006 areaOpen = true;
12007 }
12008
12009 // now first check the case where both is outside
12010 if (y1 >= axisy.max && y2 >= axisy.max) {
12011 ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.max));
12012 ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.max));
12013 continue;
12014 }
12015 else if (y1 <= axisy.min && y2 <= axisy.min) {
12016 ctx.lineTo(axisx.p2c(x1), axisy.p2c(axisy.min));
12017 ctx.lineTo(axisx.p2c(x2), axisy.p2c(axisy.min));
12018 continue;
12019 }
12020
12021 // else it's a bit more complicated, there might
12022 // be a flat maxed out rectangle first, then a
12023 // triangular cutout or reverse; to find these
12024 // keep track of the current x values
12025 var x1old = x1, x2old = x2;
12026
12027 // clip the y values, without shortcutting, we
12028 // go through all cases in turn
12029
12030 // clip with ymin
12031 if (y1 <= y2 && y1 < axisy.min && y2 >= axisy.min) {
12032 x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
12033 y1 = axisy.min;
12034 }
12035 else if (y2 <= y1 && y2 < axisy.min && y1 >= axisy.min) {
12036 x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
12037 y2 = axisy.min;
12038 }
12039
12040 // clip with ymax
12041 if (y1 >= y2 && y1 > axisy.max && y2 <= axisy.max) {
12042 x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
12043 y1 = axisy.max;
12044 }
12045 else if (y2 >= y1 && y2 > axisy.max && y1 <= axisy.max) {
12046 x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
12047 y2 = axisy.max;
12048 }
12049
12050 // if the x value was changed we got a rectangle
12051 // to fill
12052 if (x1 != x1old) {
12053 ctx.lineTo(axisx.p2c(x1old), axisy.p2c(y1));
12054 // it goes to (x1, y1), but we fill that below
12055 }
12056
12057 // fill triangular section, this sometimes result
12058 // in redundant points if (x1, y1) hasn't changed
12059 // from previous line to, but we just ignore that
12060 ctx.lineTo(axisx.p2c(x1), axisy.p2c(y1));
12061 ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
12062
12063 // fill the other rectangle if it's there
12064 if (x2 != x2old) {
12065 ctx.lineTo(axisx.p2c(x2), axisy.p2c(y2));
12066 ctx.lineTo(axisx.p2c(x2old), axisy.p2c(y2));
12067 }
12068 }
12069 }
12070
12071 ctx.save();
12072 ctx.translate(plotOffset.left, plotOffset.top);
12073 ctx.lineJoin = "round";
12074
12075 var lw = series.lines.lineWidth,
12076 sw = series.shadowSize;
12077 // FIXME: consider another form of shadow when filling is turned on
12078 if (lw > 0 && sw > 0) {
12079 // draw shadow as a thick and thin line with transparency
12080 ctx.lineWidth = sw;
12081 ctx.strokeStyle = "rgba(0,0,0,0.1)";
12082 // position shadow at angle from the mid of line
12083 var angle = Math.PI/18;
12084 plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2), series.xaxis, series.yaxis);
12085 ctx.lineWidth = sw/2;
12086 plotLine(series.datapoints, Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4), series.xaxis, series.yaxis);
12087 }
12088
12089 ctx.lineWidth = lw;
12090 ctx.strokeStyle = series.color;
12091 var fillStyle = getFillStyle(series.lines, series.color, 0, plotHeight);
12092 if (fillStyle) {
12093 ctx.fillStyle = fillStyle;
12094 plotLineArea(series.datapoints, series.xaxis, series.yaxis);
12095 }
12096
12097 if (lw > 0)
12098 plotLine(series.datapoints, 0, 0, series.xaxis, series.yaxis);
12099 ctx.restore();
12100 }
12101
12102 function drawSeriesPoints(series) {
12103 function plotPoints(datapoints, radius, fillStyle, offset, shadow, axisx, axisy, symbol) {
12104 var points = datapoints.points, ps = datapoints.pointsize;
12105
12106 for (var i = 0; i < points.length; i += ps) {
12107 var x = points[i], y = points[i + 1];
12108 if (x == null || x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
12109 continue;
12110
12111 ctx.beginPath();
12112 x = axisx.p2c(x);
12113 y = axisy.p2c(y) + offset;
12114 if (symbol == "circle")
12115 ctx.arc(x, y, radius, 0, shadow ? Math.PI : Math.PI * 2, false);
12116 else
12117 symbol(ctx, x, y, radius, shadow);
12118 ctx.closePath();
12119
12120 if (fillStyle) {
12121 ctx.fillStyle = fillStyle;
12122 ctx.fill();
12123 }
12124 ctx.stroke();
12125 }
12126 }
12127
12128 ctx.save();
12129 ctx.translate(plotOffset.left, plotOffset.top);
12130
12131 var lw = series.points.lineWidth,
12132 sw = series.shadowSize,
12133 radius = series.points.radius,
12134 symbol = series.points.symbol;
12135
12136 // If the user sets the line width to 0, we change it to a very
12137 // small value. A line width of 0 seems to force the default of 1.
12138 // Doing the conditional here allows the shadow setting to still be
12139 // optional even with a lineWidth of 0.
12140
12141 if( lw == 0 )
12142 lw = 0.0001;
12143
12144 if (lw > 0 && sw > 0) {
12145 // draw shadow in two steps
12146 var w = sw / 2;
12147 ctx.lineWidth = w;
12148 ctx.strokeStyle = "rgba(0,0,0,0.1)";
12149 plotPoints(series.datapoints, radius, null, w + w/2, true,
12150 series.xaxis, series.yaxis, symbol);
12151
12152 ctx.strokeStyle = "rgba(0,0,0,0.2)";
12153 plotPoints(series.datapoints, radius, null, w/2, true,
12154 series.xaxis, series.yaxis, symbol);
12155 }
12156
12157 ctx.lineWidth = lw;
12158 ctx.strokeStyle = series.color;
12159 plotPoints(series.datapoints, radius,
12160 getFillStyle(series.points, series.color), 0, false,
12161 series.xaxis, series.yaxis, symbol);
12162 ctx.restore();
12163 }
12164
12165 function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth) {
12166 var left, right, bottom, top,
12167 drawLeft, drawRight, drawTop, drawBottom,
12168 tmp;
12169
12170 // in horizontal mode, we start the bar from the left
12171 // instead of from the bottom so it appears to be
12172 // horizontal rather than vertical
12173 if (horizontal) {
12174 drawBottom = drawRight = drawTop = true;
12175 drawLeft = false;
12176 left = b;
12177 right = x;
12178 top = y + barLeft;
12179 bottom = y + barRight;
12180
12181 // account for negative bars
12182 if (right < left) {
12183 tmp = right;
12184 right = left;
12185 left = tmp;
12186 drawLeft = true;
12187 drawRight = false;
12188 }
12189 }
12190 else {
12191 drawLeft = drawRight = drawTop = true;
12192 drawBottom = false;
12193 left = x + barLeft;
12194 right = x + barRight;
12195 bottom = b;
12196 top = y;
12197
12198 // account for negative bars
12199 if (top < bottom) {
12200 tmp = top;
12201 top = bottom;
12202 bottom = tmp;
12203 drawBottom = true;
12204 drawTop = false;
12205 }
12206 }
12207
12208 // clip
12209 if (right < axisx.min || left > axisx.max ||
12210 top < axisy.min || bottom > axisy.max)
12211 return;
12212
12213 if (left < axisx.min) {
12214 left = axisx.min;
12215 drawLeft = false;
12216 }
12217
12218 if (right > axisx.max) {
12219 right = axisx.max;
12220 drawRight = false;
12221 }
12222
12223 if (bottom < axisy.min) {
12224 bottom = axisy.min;
12225 drawBottom = false;
12226 }
12227
12228 if (top > axisy.max) {
12229 top = axisy.max;
12230 drawTop = false;
12231 }
12232
12233 left = axisx.p2c(left);
12234 bottom = axisy.p2c(bottom);
12235 right = axisx.p2c(right);
12236 top = axisy.p2c(top);
12237
12238 // fill the bar
12239 if (fillStyleCallback) {
12240 c.fillStyle = fillStyleCallback(bottom, top);
12241 c.fillRect(left, top, right - left, bottom - top)
12242 }
12243
12244 // draw outline
12245 if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
12246 c.beginPath();
12247
12248 // FIXME: inline moveTo is buggy with excanvas
12249 c.moveTo(left, bottom);
12250 if (drawLeft)
12251 c.lineTo(left, top);
12252 else
12253 c.moveTo(left, top);
12254 if (drawTop)
12255 c.lineTo(right, top);
12256 else
12257 c.moveTo(right, top);
12258 if (drawRight)
12259 c.lineTo(right, bottom);
12260 else
12261 c.moveTo(right, bottom);
12262 if (drawBottom)
12263 c.lineTo(left, bottom);
12264 else
12265 c.moveTo(left, bottom);
12266 c.stroke();
12267 }
12268 }
12269
12270 function drawSeriesBars(series) {
12271 function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
12272 var points = datapoints.points, ps = datapoints.pointsize;
12273
12274 for (var i = 0; i < points.length; i += ps) {
12275 if (points[i] == null)
12276 continue;
12277 drawBar(points[i], points[i + 1], points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, series.bars.lineWidth);
12278 }
12279 }
12280
12281 ctx.save();
12282 ctx.translate(plotOffset.left, plotOffset.top);
12283
12284 // FIXME: figure out a way to add shadows (for instance along the right edge)
12285 ctx.lineWidth = series.bars.lineWidth;
12286 ctx.strokeStyle = series.color;
12287
12288 var barLeft;
12289
12290 switch (series.bars.align) {
12291 case "left":
12292 barLeft = 0;
12293 break;
12294 case "right":
12295 barLeft = -series.bars.barWidth;
12296 break;
12297 default:
12298 barLeft = -series.bars.barWidth / 2;
12299 }
12300
12301 var fillStyleCallback = series.bars.fill ? function (bottom, top) { return getFillStyle(series.bars, series.color, bottom, top); } : null;
12302 plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis);
12303 ctx.restore();
12304 }
12305
12306 function getFillStyle(filloptions, seriesColor, bottom, top) {
12307 var fill = filloptions.fill;
12308 if (!fill)
12309 return null;
12310
12311 if (filloptions.fillColor)
12312 return getColorOrGradient(filloptions.fillColor, bottom, top, seriesColor);
12313
12314 var c = $.color.parse(seriesColor);
12315 c.a = typeof fill == "number" ? fill : 0.4;
12316 c.normalize();
12317 return c.toString();
12318 }
12319
12320 function insertLegend() {
12321
12322 if (options.legend.container != null) {
12323 $(options.legend.container).html("");
12324 } else {
12325 placeholder.find(".legend").remove();
12326 }
12327
12328 if (!options.legend.show) {
12329 return;
12330 }
12331
12332 var fragments = [], entries = [], rowStarted = false,
12333 lf = options.legend.labelFormatter, s, label;
12334
12335 // Build a list of legend entries, with each having a label and a color
12336
12337 for (var i = 0; i < series.length; ++i) {
12338 s = series[i];
12339 if (s.label) {
12340 label = lf ? lf(s.label, s) : s.label;
12341 if (label) {
12342 entries.push({
12343 label: label,
12344 color: s.color
12345 });
12346 }
12347 }
12348 }
12349
12350 // Sort the legend using either the default or a custom comparator
12351
12352 if (options.legend.sorted) {
12353 if ($.isFunction(options.legend.sorted)) {
12354 entries.sort(options.legend.sorted);
12355 } else if (options.legend.sorted == "reverse") {
12356 entries.reverse();
12357 } else {
12358 var ascending = options.legend.sorted != "descending";
12359 entries.sort(function(a, b) {
12360 return a.label == b.label ? 0 : (
12361 (a.label < b.label) != ascending ? 1 : -1 // Logical XOR
12362 );
12363 });
12364 }
12365 }
12366
12367 // Generate markup for the list of entries, in their final order
12368
12369 for (var i = 0; i < entries.length; ++i) {
12370
12371 var entry = entries[i];
12372
12373 if (i % options.legend.noColumns == 0) {
12374 if (rowStarted)
12375 fragments.push('</tr>');
12376 fragments.push('<tr>');
12377 rowStarted = true;
12378 }
12379
12380 fragments.push(
12381 '<td class="legendColorBox"><div style="border:1px solid ' + options.legend.labelBoxBorderColor + ';padding:1px"><div style="width:4px;height:0;border:5px solid ' + entry.color + ';overflow:hidden"></div></div></td>' +
12382 '<td class="legendLabel">' + entry.label + '</td>'
12383 );
12384 }
12385
12386 if (rowStarted)
12387 fragments.push('</tr>');
12388
12389 if (fragments.length == 0)
12390 return;
12391
12392 var table = '<table style="font-size:smaller;color:' + options.grid.color + '">' + fragments.join("") + '</table>';
12393 if (options.legend.container != null)
12394 $(options.legend.container).html(table);
12395 else {
12396 var pos = "",
12397 p = options.legend.position,
12398 m = options.legend.margin;
12399 if (m[0] == null)
12400 m = [m, m];
12401 if (p.charAt(0) == "n")
12402 pos += 'top:' + (m[1] + plotOffset.top) + 'px;';
12403 else if (p.charAt(0) == "s")
12404 pos += 'bottom:' + (m[1] + plotOffset.bottom) + 'px;';
12405 if (p.charAt(1) == "e")
12406 pos += 'right:' + (m[0] + plotOffset.right) + 'px;';
12407 else if (p.charAt(1) == "w")
12408 pos += 'left:' + (m[0] + plotOffset.left) + 'px;';
12409 var legend = $('<div class="legend">' + table.replace('style="', 'style="position:absolute;' + pos +';') + '</div>').appendTo(placeholder);
12410 if (options.legend.backgroundOpacity != 0.0) {
12411 // put in the transparent background
12412 // separately to avoid blended labels and
12413 // label boxes
12414 var c = options.legend.backgroundColor;
12415 if (c == null) {
12416 c = options.grid.backgroundColor;
12417 if (c && typeof c == "string")
12418 c = $.color.parse(c);
12419 else
12420 c = $.color.extract(legend, 'background-color');
12421 c.a = 1;
12422 c = c.toString();
12423 }
12424 var div = legend.children();
12425 $('<div style="position:absolute;width:' + div.width() + 'px;height:' + div.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').prependTo(legend).css('opacity', options.legend.backgroundOpacity);
12426 }
12427 }
12428 }
12429
12430
12431 // interactive features
12432
12433 var highlights = [],
12434 redrawTimeout = null;
12435
12436 // returns the data item the mouse is over, or null if none is found
12437 function findNearbyItem(mouseX, mouseY, seriesFilter) {
12438 var maxDistance = options.grid.mouseActiveRadius,
12439 smallestDistance = maxDistance * maxDistance + 1,
12440 item = null, foundPoint = false, i, j, ps;
12441
12442 for (i = series.length - 1; i >= 0; --i) {
12443 if (!seriesFilter(series[i]))
12444 continue;
12445
12446 var s = series[i],
12447 axisx = s.xaxis,
12448 axisy = s.yaxis,
12449 points = s.datapoints.points,
12450 mx = axisx.c2p(mouseX), // precompute some stuff to make the loop faster
12451 my = axisy.c2p(mouseY),
12452 maxx = maxDistance / axisx.scale,
12453 maxy = maxDistance / axisy.scale;
12454
12455 ps = s.datapoints.pointsize;
12456 // with inverse transforms, we can't use the maxx/maxy
12457 // optimization, sadly
12458 if (axisx.options.inverseTransform)
12459 maxx = Number.MAX_VALUE;
12460 if (axisy.options.inverseTransform)
12461 maxy = Number.MAX_VALUE;
12462
12463 if (s.lines.show || s.points.show) {
12464 for (j = 0; j < points.length; j += ps) {
12465 var x = points[j], y = points[j + 1];
12466 if (x == null)
12467 continue;
12468
12469 // For points and lines, the cursor must be within a
12470 // certain distance to the data point
12471 if (x - mx > maxx || x - mx < -maxx ||
12472 y - my > maxy || y - my < -maxy)
12473 continue;
12474
12475 // We have to calculate distances in pixels, not in
12476 // data units, because the scales of the axes may be different
12477 var dx = Math.abs(axisx.p2c(x) - mouseX),
12478 dy = Math.abs(axisy.p2c(y) - mouseY),
12479 dist = dx * dx + dy * dy; // we save the sqrt
12480
12481 // use <= to ensure last point takes precedence
12482 // (last generally means on top of)
12483 if (dist < smallestDistance) {
12484 smallestDistance = dist;
12485 item = [i, j / ps];
12486 }
12487 }
12488 }
12489
12490 if (s.bars.show && !item) { // no other point can be nearby
12491
12492 var barLeft, barRight;
12493
12494 switch (s.bars.align) {
12495 case "left":
12496 barLeft = 0;
12497 break;
12498 case "right":
12499 barLeft = -s.bars.barWidth;
12500 break;
12501 default:
12502 barLeft = -s.bars.barWidth / 2;
12503 }
12504
12505 barRight = barLeft + s.bars.barWidth;
12506
12507 for (j = 0; j < points.length; j += ps) {
12508 var x = points[j], y = points[j + 1], b = points[j + 2];
12509 if (x == null)
12510 continue;
12511
12512 // for a bar graph, the cursor must be inside the bar
12513 if (series[i].bars.horizontal ?
12514 (mx <= Math.max(b, x) && mx >= Math.min(b, x) &&
12515 my >= y + barLeft && my <= y + barRight) :
12516 (mx >= x + barLeft && mx <= x + barRight &&
12517 my >= Math.min(b, y) && my <= Math.max(b, y)))
12518 item = [i, j / ps];
12519 }
12520 }
12521 }
12522
12523 if (item) {
12524 i = item[0];
12525 j = item[1];
12526 ps = series[i].datapoints.pointsize;
12527
12528 return { datapoint: series[i].datapoints.points.slice(j * ps, (j + 1) * ps),
12529 dataIndex: j,
12530 series: series[i],
12531 seriesIndex: i };
12532 }
12533
12534 return null;
12535 }
12536
12537 function onMouseMove(e) {
12538 if (options.grid.hoverable)
12539 triggerClickHoverEvent("plothover", e,
12540 function (s) { return s["hoverable"] != false; });
12541 }
12542
12543 function onMouseLeave(e) {
12544 if (options.grid.hoverable)
12545 triggerClickHoverEvent("plothover", e,
12546 function (s) { return false; });
12547 }
12548
12549 function onClick(e) {
12550 triggerClickHoverEvent("plotclick", e,
12551 function (s) { return s["clickable"] != false; });
12552 }
12553
12554 // trigger click or hover event (they send the same parameters
12555 // so we share their code)
12556 function triggerClickHoverEvent(eventname, event, seriesFilter) {
12557 var offset = eventHolder.offset(),
12558 canvasX = event.pageX - offset.left - plotOffset.left,
12559 canvasY = event.pageY - offset.top - plotOffset.top,
12560 pos = canvasToAxisCoords({ left: canvasX, top: canvasY });
12561
12562 pos.pageX = event.pageX;
12563 pos.pageY = event.pageY;
12564
12565 var item = findNearbyItem(canvasX, canvasY, seriesFilter);
12566
12567 if (item) {
12568 // fill in mouse pos for any listeners out there
12569 item.pageX = parseInt(item.series.xaxis.p2c(item.datapoint[0]) + offset.left + plotOffset.left, 10);
12570 item.pageY = parseInt(item.series.yaxis.p2c(item.datapoint[1]) + offset.top + plotOffset.top, 10);
12571 }
12572
12573 if (options.grid.autoHighlight) {
12574 // clear auto-highlights
12575 for (var i = 0; i < highlights.length; ++i) {
12576 var h = highlights[i];
12577 if (h.auto == eventname &&
12578 !(item && h.series == item.series &&
12579 h.point[0] == item.datapoint[0] &&
12580 h.point[1] == item.datapoint[1]))
12581 unhighlight(h.series, h.point);
12582 }
12583
12584 if (item)
12585 highlight(item.series, item.datapoint, eventname);
12586 }
12587
12588 placeholder.trigger(eventname, [ pos, item ]);
12589 }
12590
12591 function triggerRedrawOverlay() {
12592 var t = options.interaction.redrawOverlayInterval;
12593 if (t == -1) { // skip event queue
12594 drawOverlay();
12595 return;
12596 }
12597
12598 if (!redrawTimeout)
12599 redrawTimeout = setTimeout(drawOverlay, t);
12600 }
12601
12602 function drawOverlay() {
12603 redrawTimeout = null;
12604
12605 // draw highlights
12606 octx.save();
12607 overlay.clear();
12608 octx.translate(plotOffset.left, plotOffset.top);
12609
12610 var i, hi;
12611 for (i = 0; i < highlights.length; ++i) {
12612 hi = highlights[i];
12613
12614 if (hi.series.bars.show)
12615 drawBarHighlight(hi.series, hi.point);
12616 else
12617 drawPointHighlight(hi.series, hi.point);
12618 }
12619 octx.restore();
12620
12621 executeHooks(hooks.drawOverlay, [octx]);
12622 }
12623
12624 function highlight(s, point, auto) {
12625 if (typeof s == "number")
12626 s = series[s];
12627
12628 if (typeof point == "number") {
12629 var ps = s.datapoints.pointsize;
12630 point = s.datapoints.points.slice(ps * point, ps * (point + 1));
12631 }
12632
12633 var i = indexOfHighlight(s, point);
12634 if (i == -1) {
12635 highlights.push({ series: s, point: point, auto: auto });
12636
12637 triggerRedrawOverlay();
12638 }
12639 else if (!auto)
12640 highlights[i].auto = false;
12641 }
12642
12643 function unhighlight(s, point) {
12644 if (s == null && point == null) {
12645 highlights = [];
12646 triggerRedrawOverlay();
12647 return;
12648 }
12649
12650 if (typeof s == "number")
12651 s = series[s];
12652
12653 if (typeof point == "number") {
12654 var ps = s.datapoints.pointsize;
12655 point = s.datapoints.points.slice(ps * point, ps * (point + 1));
12656 }
12657
12658 var i = indexOfHighlight(s, point);
12659 if (i != -1) {
12660 highlights.splice(i, 1);
12661
12662 triggerRedrawOverlay();
12663 }
12664 }
12665
12666 function indexOfHighlight(s, p) {
12667 for (var i = 0; i < highlights.length; ++i) {
12668 var h = highlights[i];
12669 if (h.series == s && h.point[0] == p[0]
12670 && h.point[1] == p[1])
12671 return i;
12672 }
12673 return -1;
12674 }
12675
12676 function drawPointHighlight(series, point) {
12677 var x = point[0], y = point[1],
12678 axisx = series.xaxis, axisy = series.yaxis,
12679 highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString();
12680
12681 if (x < axisx.min || x > axisx.max || y < axisy.min || y > axisy.max)
12682 return;
12683
12684 var pointRadius = series.points.radius + series.points.lineWidth / 2;
12685 octx.lineWidth = pointRadius;
12686 octx.strokeStyle = highlightColor;
12687 var radius = 1.5 * pointRadius;
12688 x = axisx.p2c(x);
12689 y = axisy.p2c(y);
12690
12691 octx.beginPath();
12692 if (series.points.symbol == "circle")
12693 octx.arc(x, y, radius, 0, 2 * Math.PI, false);
12694 else
12695 series.points.symbol(octx, x, y, radius, false);
12696 octx.closePath();
12697 octx.stroke();
12698 }
12699
12700 function drawBarHighlight(series, point) {
12701 var highlightColor = (typeof series.highlightColor === "string") ? series.highlightColor : $.color.parse(series.color).scale('a', 0.5).toString(),
12702 fillStyle = highlightColor,
12703 barLeft;
12704
12705 switch (series.bars.align) {
12706 case "left":
12707 barLeft = 0;
12708 break;
12709 case "right":
12710 barLeft = -series.bars.barWidth;
12711 break;
12712 default:
12713 barLeft = -series.bars.barWidth / 2;
12714 }
12715
12716 octx.lineWidth = series.bars.lineWidth;
12717 octx.strokeStyle = highlightColor;
12718
12719 drawBar(point[0], point[1], point[2] || 0, barLeft, barLeft + series.bars.barWidth,
12720 function () { return fillStyle; }, series.xaxis, series.yaxis, octx, series.bars.horizontal, series.bars.lineWidth);
12721 }
12722
12723 function getColorOrGradient(spec, bottom, top, defaultColor) {
12724 if (typeof spec == "string")
12725 return spec;
12726 else {
12727 // assume this is a gradient spec; IE currently only
12728 // supports a simple vertical gradient properly, so that's
12729 // what we support too
12730 var gradient = ctx.createLinearGradient(0, top, 0, bottom);
12731
12732 for (var i = 0, l = spec.colors.length; i < l; ++i) {
12733 var c = spec.colors[i];
12734 if (typeof c != "string") {
12735 var co = $.color.parse(defaultColor);
12736 if (c.brightness != null)
12737 co = co.scale('rgb', c.brightness);
12738 if (c.opacity != null)
12739 co.a *= c.opacity;
12740 c = co.toString();
12741 }
12742 gradient.addColorStop(i / (l - 1), c);
12743 }
12744
12745 return gradient;
12746 }
12747 }
12748 }
12749
12750 // Add the plot function to the top level of the jQuery object
12751
12752 $.plot = function(placeholder, data, options) {
12753 //var t0 = new Date();
12754 var plot = new Plot($(placeholder), data, options, $.plot.plugins);
12755 //(window.console ? console.log : alert)("time used (msecs): " + ((new Date()).getTime() - t0.getTime()));
12756 return plot;
12757 };
12758
12759 $.plot.version = "0.8.3";
12760
12761 $.plot.plugins = [];
12762
12763 // Also add the plot function as a chainable property
12764
12765 $.fn.plot = function(data, options) {
12766 return this.each(function() {
12767 $.plot(this, data, options);
12768 });
12769 };
12770
12771 // round to nearby lower multiple of base
12772 function floorInBase(n, base) {
12773 return base * Math.floor(n / base);
12774 }
12775
12776})(jQuery);
12777</script>
12778 <script type="application/javascript">/* Flot plugin for rendering pie charts.
12779
12780Copyright (c) 2007-2014 IOLA and Ole Laursen.
12781Licensed under the MIT license.
12782
12783The plugin assumes that each series has a single data value, and that each
12784value is a positive integer or zero. Negative numbers don't make sense for a
12785pie chart, and have unpredictable results. The values do NOT need to be
12786passed in as percentages; the plugin will calculate the total and per-slice
12787percentages internally.
12788
12789* Created by Brian Medendorp
12790
12791* Updated with contributions from btburnett3, Anthony Aragues and Xavi Ivars
12792
12793The plugin supports these options:
12794
12795 series: {
12796 pie: {
12797 show: true/false
12798 radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
12799 innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
12800 startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
12801 tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
12802 offset: {
12803 top: integer value to move the pie up or down
12804 left: integer value to move the pie left or right, or 'auto'
12805 },
12806 stroke: {
12807 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
12808 width: integer pixel width of the stroke
12809 },
12810 label: {
12811 show: true/false, or 'auto'
12812 formatter: a user-defined function that modifies the text/style of the label text
12813 radius: 0-1 for percentage of fullsize, or a specified pixel length
12814 background: {
12815 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
12816 opacity: 0-1
12817 },
12818 threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
12819 },
12820 combine: {
12821 threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
12822 color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
12823 label: any text value of what the combined slice should be labeled
12824 }
12825 highlight: {
12826 opacity: 0-1
12827 }
12828 }
12829 }
12830
12831More detail and specific examples can be found in the included HTML file.
12832
12833*/
12834
12835(function($) {
12836
12837 // Maximum redraw attempts when fitting labels within the plot
12838
12839 var REDRAW_ATTEMPTS = 10;
12840
12841 // Factor by which to shrink the pie when fitting labels within the plot
12842
12843 var REDRAW_SHRINK = 0.95;
12844
12845 function init(plot) {
12846
12847 var canvas = null,
12848 target = null,
12849 options = null,
12850 maxRadius = null,
12851 centerLeft = null,
12852 centerTop = null,
12853 processed = false,
12854 ctx = null;
12855
12856 // interactive variables
12857
12858 var highlights = [];
12859
12860 // add hook to determine if pie plugin in enabled, and then perform necessary operations
12861
12862 plot.hooks.processOptions.push(function(plot, options) {
12863 if (options.series.pie.show) {
12864
12865 options.grid.show = false;
12866
12867 // set labels.show
12868
12869 if (options.series.pie.label.show == "auto") {
12870 if (options.legend.show) {
12871 options.series.pie.label.show = false;
12872 } else {
12873 options.series.pie.label.show = true;
12874 }
12875 }
12876
12877 // set radius
12878
12879 if (options.series.pie.radius == "auto") {
12880 if (options.series.pie.label.show) {
12881 options.series.pie.radius = 3/4;
12882 } else {
12883 options.series.pie.radius = 1;
12884 }
12885 }
12886
12887 // ensure sane tilt
12888
12889 if (options.series.pie.tilt > 1) {
12890 options.series.pie.tilt = 1;
12891 } else if (options.series.pie.tilt < 0) {
12892 options.series.pie.tilt = 0;
12893 }
12894 }
12895 });
12896
12897 plot.hooks.bindEvents.push(function(plot, eventHolder) {
12898 var options = plot.getOptions();
12899 if (options.series.pie.show) {
12900 if (options.grid.hoverable) {
12901 eventHolder.unbind("mousemove").mousemove(onMouseMove);
12902 }
12903 if (options.grid.clickable) {
12904 eventHolder.unbind("click").click(onClick);
12905 }
12906 }
12907 });
12908
12909 plot.hooks.processDatapoints.push(function(plot, series, data, datapoints) {
12910 var options = plot.getOptions();
12911 if (options.series.pie.show) {
12912 processDatapoints(plot, series, data, datapoints);
12913 }
12914 });
12915
12916 plot.hooks.drawOverlay.push(function(plot, octx) {
12917 var options = plot.getOptions();
12918 if (options.series.pie.show) {
12919 drawOverlay(plot, octx);
12920 }
12921 });
12922
12923 plot.hooks.draw.push(function(plot, newCtx) {
12924 var options = plot.getOptions();
12925 if (options.series.pie.show) {
12926 draw(plot, newCtx);
12927 }
12928 });
12929
12930 function processDatapoints(plot, series, datapoints) {
12931 if (!processed) {
12932 processed = true;
12933 canvas = plot.getCanvas();
12934 target = $(canvas).parent();
12935 options = plot.getOptions();
12936 plot.setData(combine(plot.getData()));
12937 }
12938 }
12939
12940 function combine(data) {
12941
12942 var total = 0,
12943 combined = 0,
12944 numCombined = 0,
12945 color = options.series.pie.combine.color,
12946 newdata = [];
12947
12948 // Fix up the raw data from Flot, ensuring the data is numeric
12949
12950 for (var i = 0; i < data.length; ++i) {
12951
12952 var value = data[i].data;
12953
12954 // If the data is an array, we'll assume that it's a standard
12955 // Flot x-y pair, and are concerned only with the second value.
12956
12957 // Note how we use the original array, rather than creating a
12958 // new one; this is more efficient and preserves any extra data
12959 // that the user may have stored in higher indexes.
12960
12961 if ($.isArray(value) && value.length == 1) {
12962 value = value[0];
12963 }
12964
12965 if ($.isArray(value)) {
12966 // Equivalent to $.isNumeric() but compatible with jQuery < 1.7
12967 if (!isNaN(parseFloat(value[1])) && isFinite(value[1])) {
12968 value[1] = +value[1];
12969 } else {
12970 value[1] = 0;
12971 }
12972 } else if (!isNaN(parseFloat(value)) && isFinite(value)) {
12973 value = [1, +value];
12974 } else {
12975 value = [1, 0];
12976 }
12977
12978 data[i].data = [value];
12979 }
12980
12981 // Sum up all the slices, so we can calculate percentages for each
12982
12983 for (var i = 0; i < data.length; ++i) {
12984 total += data[i].data[0][1];
12985 }
12986
12987 // Count the number of slices with percentages below the combine
12988 // threshold; if it turns out to be just one, we won't combine.
12989
12990 for (var i = 0; i < data.length; ++i) {
12991 var value = data[i].data[0][1];
12992 if (value / total <= options.series.pie.combine.threshold) {
12993 combined += value;
12994 numCombined++;
12995 if (!color) {
12996 color = data[i].color;
12997 }
12998 }
12999 }
13000
13001 for (var i = 0; i < data.length; ++i) {
13002 var value = data[i].data[0][1];
13003 if (numCombined < 2 || value / total > options.series.pie.combine.threshold) {
13004 newdata.push(
13005 $.extend(data[i], { /* extend to allow keeping all other original data values
13006 and using them e.g. in labelFormatter. */
13007 data: [[1, value]],
13008 color: data[i].color,
13009 label: data[i].label,
13010 angle: value * Math.PI * 2 / total,
13011 percent: value / (total / 100)
13012 })
13013 );
13014 }
13015 }
13016
13017 if (numCombined > 1) {
13018 newdata.push({
13019 data: [[1, combined]],
13020 color: color,
13021 label: options.series.pie.combine.label,
13022 angle: combined * Math.PI * 2 / total,
13023 percent: combined / (total / 100)
13024 });
13025 }
13026
13027 return newdata;
13028 }
13029
13030 function draw(plot, newCtx) {
13031
13032 if (!target) {
13033 return; // if no series were passed
13034 }
13035
13036 var canvasWidth = plot.getPlaceholder().width(),
13037 canvasHeight = plot.getPlaceholder().height(),
13038 legendWidth = target.children().filter(".legend").children().width() || 0;
13039
13040 ctx = newCtx;
13041
13042 // WARNING: HACK! REWRITE THIS CODE AS SOON AS POSSIBLE!
13043
13044 // When combining smaller slices into an 'other' slice, we need to
13045 // add a new series. Since Flot gives plugins no way to modify the
13046 // list of series, the pie plugin uses a hack where the first call
13047 // to processDatapoints results in a call to setData with the new
13048 // list of series, then subsequent processDatapoints do nothing.
13049
13050 // The plugin-global 'processed' flag is used to control this hack;
13051 // it starts out false, and is set to true after the first call to
13052 // processDatapoints.
13053
13054 // Unfortunately this turns future setData calls into no-ops; they
13055 // call processDatapoints, the flag is true, and nothing happens.
13056
13057 // To fix this we'll set the flag back to false here in draw, when
13058 // all series have been processed, so the next sequence of calls to
13059 // processDatapoints once again starts out with a slice-combine.
13060 // This is really a hack; in 0.9 we need to give plugins a proper
13061 // way to modify series before any processing begins.
13062
13063 processed = false;
13064
13065 // calculate maximum radius and center point
13066
13067 maxRadius = Math.min(canvasWidth, canvasHeight / options.series.pie.tilt) / 2;
13068 centerTop = canvasHeight / 2 + options.series.pie.offset.top;
13069 centerLeft = canvasWidth / 2;
13070
13071 if (options.series.pie.offset.left == "auto") {
13072 if (options.legend.position.match("w")) {
13073 centerLeft += legendWidth / 2;
13074 } else {
13075 centerLeft -= legendWidth / 2;
13076 }
13077 if (centerLeft < maxRadius) {
13078 centerLeft = maxRadius;
13079 } else if (centerLeft > canvasWidth - maxRadius) {
13080 centerLeft = canvasWidth - maxRadius;
13081 }
13082 } else {
13083 centerLeft += options.series.pie.offset.left;
13084 }
13085
13086 var slices = plot.getData(),
13087 attempts = 0;
13088
13089 // Keep shrinking the pie's radius until drawPie returns true,
13090 // indicating that all the labels fit, or we try too many times.
13091
13092 do {
13093 if (attempts > 0) {
13094 maxRadius *= REDRAW_SHRINK;
13095 }
13096 attempts += 1;
13097 clear();
13098 if (options.series.pie.tilt <= 0.8) {
13099 drawShadow();
13100 }
13101 } while (!drawPie() && attempts < REDRAW_ATTEMPTS)
13102
13103 if (attempts >= REDRAW_ATTEMPTS) {
13104 clear();
13105 target.prepend("<div class='error'>Could not draw pie with labels contained inside canvas</div>");
13106 }
13107
13108 if (plot.setSeries && plot.insertLegend) {
13109 plot.setSeries(slices);
13110 plot.insertLegend();
13111 }
13112
13113 // we're actually done at this point, just defining internal functions at this point
13114
13115 function clear() {
13116 ctx.clearRect(0, 0, canvasWidth, canvasHeight);
13117 target.children().filter(".pieLabel, .pieLabelBackground").remove();
13118 }
13119
13120 function drawShadow() {
13121
13122 var shadowLeft = options.series.pie.shadow.left;
13123 var shadowTop = options.series.pie.shadow.top;
13124 var edge = 10;
13125 var alpha = options.series.pie.shadow.alpha;
13126 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
13127
13128 if (radius >= canvasWidth / 2 - shadowLeft || radius * options.series.pie.tilt >= canvasHeight / 2 - shadowTop || radius <= edge) {
13129 return; // shadow would be outside canvas, so don't draw it
13130 }
13131
13132 ctx.save();
13133 ctx.translate(shadowLeft,shadowTop);
13134 ctx.globalAlpha = alpha;
13135 ctx.fillStyle = "#000";
13136
13137 // center and rotate to starting position
13138
13139 ctx.translate(centerLeft,centerTop);
13140 ctx.scale(1, options.series.pie.tilt);
13141
13142 //radius -= edge;
13143
13144 for (var i = 1; i <= edge; i++) {
13145 ctx.beginPath();
13146 ctx.arc(0, 0, radius, 0, Math.PI * 2, false);
13147 ctx.fill();
13148 radius -= i;
13149 }
13150
13151 ctx.restore();
13152 }
13153
13154 function drawPie() {
13155
13156 var startAngle = Math.PI * options.series.pie.startAngle;
13157 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
13158
13159 // center and rotate to starting position
13160
13161 ctx.save();
13162 ctx.translate(centerLeft,centerTop);
13163 ctx.scale(1, options.series.pie.tilt);
13164 //ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
13165
13166 // draw slices
13167
13168 ctx.save();
13169 var currentAngle = startAngle;
13170 for (var i = 0; i < slices.length; ++i) {
13171 slices[i].startAngle = currentAngle;
13172 drawSlice(slices[i].angle, slices[i].color, true);
13173 }
13174 ctx.restore();
13175
13176 // draw slice outlines
13177
13178 if (options.series.pie.stroke.width > 0) {
13179 ctx.save();
13180 ctx.lineWidth = options.series.pie.stroke.width;
13181 currentAngle = startAngle;
13182 for (var i = 0; i < slices.length; ++i) {
13183 drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
13184 }
13185 ctx.restore();
13186 }
13187
13188 // draw donut hole
13189
13190 drawDonutHole(ctx);
13191
13192 ctx.restore();
13193
13194 // Draw the labels, returning true if they fit within the plot
13195
13196 if (options.series.pie.label.show) {
13197 return drawLabels();
13198 } else return true;
13199
13200 function drawSlice(angle, color, fill) {
13201
13202 if (angle <= 0 || isNaN(angle)) {
13203 return;
13204 }
13205
13206 if (fill) {
13207 ctx.fillStyle = color;
13208 } else {
13209 ctx.strokeStyle = color;
13210 ctx.lineJoin = "round";
13211 }
13212
13213 ctx.beginPath();
13214 if (Math.abs(angle - Math.PI * 2) > 0.000000001) {
13215 ctx.moveTo(0, 0); // Center of the pie
13216 }
13217
13218 //ctx.arc(0, 0, radius, 0, angle, false); // This doesn't work properly in Opera
13219 ctx.arc(0, 0, radius,currentAngle, currentAngle + angle / 2, false);
13220 ctx.arc(0, 0, radius,currentAngle + angle / 2, currentAngle + angle, false);
13221 ctx.closePath();
13222 //ctx.rotate(angle); // This doesn't work properly in Opera
13223 currentAngle += angle;
13224
13225 if (fill) {
13226 ctx.fill();
13227 } else {
13228 ctx.stroke();
13229 }
13230 }
13231
13232 function drawLabels() {
13233
13234 var currentAngle = startAngle;
13235 var radius = options.series.pie.label.radius > 1 ? options.series.pie.label.radius : maxRadius * options.series.pie.label.radius;
13236
13237 for (var i = 0; i < slices.length; ++i) {
13238 if (slices[i].percent >= options.series.pie.label.threshold * 100) {
13239 if (!drawLabel(slices[i], currentAngle, i)) {
13240 return false;
13241 }
13242 }
13243 currentAngle += slices[i].angle;
13244 }
13245
13246 return true;
13247
13248 function drawLabel(slice, startAngle, index) {
13249
13250 if (slice.data[0][1] == 0) {
13251 return true;
13252 }
13253
13254 // format label text
13255
13256 var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
13257
13258 if (lf) {
13259 text = lf(slice.label, slice);
13260 } else {
13261 text = slice.label;
13262 }
13263
13264 if (plf) {
13265 text = plf(text, slice);
13266 }
13267
13268 var halfAngle = ((startAngle + slice.angle) + startAngle) / 2;
13269 var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
13270 var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
13271
13272 var html = "<span class='pieLabel' id='pieLabel" + index + "' style='position:absolute;top:" + y + "px;left:" + x + "px;'>" + text + "</span>";
13273 target.append(html);
13274
13275 var label = target.children("#pieLabel" + index);
13276 var labelTop = (y - label.height() / 2);
13277 var labelLeft = (x - label.width() / 2);
13278
13279 label.css("top", labelTop);
13280 label.css("left", labelLeft);
13281
13282 // check to make sure that the label is not outside the canvas
13283
13284 if (0 - labelTop > 0 || 0 - labelLeft > 0 || canvasHeight - (labelTop + label.height()) < 0 || canvasWidth - (labelLeft + label.width()) < 0) {
13285 return false;
13286 }
13287
13288 if (options.series.pie.label.background.opacity != 0) {
13289
13290 // put in the transparent background separately to avoid blended labels and label boxes
13291
13292 var c = options.series.pie.label.background.color;
13293
13294 if (c == null) {
13295 c = slice.color;
13296 }
13297
13298 var pos = "top:" + labelTop + "px;left:" + labelLeft + "px;";
13299 $("<div class='pieLabelBackground' style='position:absolute;width:" + label.width() + "px;height:" + label.height() + "px;" + pos + "background-color:" + c + ";'></div>")
13300 .css("opacity", options.series.pie.label.background.opacity)
13301 .insertBefore(label);
13302 }
13303
13304 return true;
13305 } // end individual label function
13306 } // end drawLabels function
13307 } // end drawPie function
13308 } // end draw function
13309
13310 // Placed here because it needs to be accessed from multiple locations
13311
13312 function drawDonutHole(layer) {
13313 if (options.series.pie.innerRadius > 0) {
13314
13315 // subtract the center
13316
13317 layer.save();
13318 var innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
13319 layer.globalCompositeOperation = "destination-out"; // this does not work with excanvas, but it will fall back to using the stroke color
13320 layer.beginPath();
13321 layer.fillStyle = options.series.pie.stroke.color;
13322 layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
13323 layer.fill();
13324 layer.closePath();
13325 layer.restore();
13326
13327 // add inner stroke
13328
13329 layer.save();
13330 layer.beginPath();
13331 layer.strokeStyle = options.series.pie.stroke.color;
13332 layer.arc(0, 0, innerRadius, 0, Math.PI * 2, false);
13333 layer.stroke();
13334 layer.closePath();
13335 layer.restore();
13336
13337 // TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
13338 }
13339 }
13340
13341 //-- Additional Interactive related functions --
13342
13343 function isPointInPoly(poly, pt) {
13344 for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
13345 ((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
13346 && (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
13347 && (c = !c);
13348 return c;
13349 }
13350
13351 function findNearbySlice(mouseX, mouseY) {
13352
13353 var slices = plot.getData(),
13354 options = plot.getOptions(),
13355 radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius,
13356 x, y;
13357
13358 for (var i = 0; i < slices.length; ++i) {
13359
13360 var s = slices[i];
13361
13362 if (s.pie.show) {
13363
13364 ctx.save();
13365 ctx.beginPath();
13366 ctx.moveTo(0, 0); // Center of the pie
13367 //ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
13368 ctx.arc(0, 0, radius, s.startAngle, s.startAngle + s.angle / 2, false);
13369 ctx.arc(0, 0, radius, s.startAngle + s.angle / 2, s.startAngle + s.angle, false);
13370 ctx.closePath();
13371 x = mouseX - centerLeft;
13372 y = mouseY - centerTop;
13373
13374 if (ctx.isPointInPath) {
13375 if (ctx.isPointInPath(mouseX - centerLeft, mouseY - centerTop)) {
13376 ctx.restore();
13377 return {
13378 datapoint: [s.percent, s.data],
13379 dataIndex: 0,
13380 series: s,
13381 seriesIndex: i
13382 };
13383 }
13384 } else {
13385
13386 // excanvas for IE doesn;t support isPointInPath, this is a workaround.
13387
13388 var p1X = radius * Math.cos(s.startAngle),
13389 p1Y = radius * Math.sin(s.startAngle),
13390 p2X = radius * Math.cos(s.startAngle + s.angle / 4),
13391 p2Y = radius * Math.sin(s.startAngle + s.angle / 4),
13392 p3X = radius * Math.cos(s.startAngle + s.angle / 2),
13393 p3Y = radius * Math.sin(s.startAngle + s.angle / 2),
13394 p4X = radius * Math.cos(s.startAngle + s.angle / 1.5),
13395 p4Y = radius * Math.sin(s.startAngle + s.angle / 1.5),
13396 p5X = radius * Math.cos(s.startAngle + s.angle),
13397 p5Y = radius * Math.sin(s.startAngle + s.angle),
13398 arrPoly = [[0, 0], [p1X, p1Y], [p2X, p2Y], [p3X, p3Y], [p4X, p4Y], [p5X, p5Y]],
13399 arrPoint = [x, y];
13400
13401 // TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
13402
13403 if (isPointInPoly(arrPoly, arrPoint)) {
13404 ctx.restore();
13405 return {
13406 datapoint: [s.percent, s.data],
13407 dataIndex: 0,
13408 series: s,
13409 seriesIndex: i
13410 };
13411 }
13412 }
13413
13414 ctx.restore();
13415 }
13416 }
13417
13418 return null;
13419 }
13420
13421 function onMouseMove(e) {
13422 triggerClickHoverEvent("plothover", e);
13423 }
13424
13425 function onClick(e) {
13426 triggerClickHoverEvent("plotclick", e);
13427 }
13428
13429 // trigger click or hover event (they send the same parameters so we share their code)
13430
13431 function triggerClickHoverEvent(eventname, e) {
13432
13433 var offset = plot.offset();
13434 var canvasX = parseInt(e.pageX - offset.left);
13435 var canvasY = parseInt(e.pageY - offset.top);
13436 var item = findNearbySlice(canvasX, canvasY);
13437
13438 if (options.grid.autoHighlight) {
13439
13440 // clear auto-highlights
13441
13442 for (var i = 0; i < highlights.length; ++i) {
13443 var h = highlights[i];
13444 if (h.auto == eventname && !(item && h.series == item.series)) {
13445 unhighlight(h.series);
13446 }
13447 }
13448 }
13449
13450 // highlight the slice
13451
13452 if (item) {
13453 highlight(item.series, eventname);
13454 }
13455
13456 // trigger any hover bind events
13457
13458 var pos = { pageX: e.pageX, pageY: e.pageY };
13459 target.trigger(eventname, [pos, item]);
13460 }
13461
13462 function highlight(s, auto) {
13463 //if (typeof s == "number") {
13464 // s = series[s];
13465 //}
13466
13467 var i = indexOfHighlight(s);
13468
13469 if (i == -1) {
13470 highlights.push({ series: s, auto: auto });
13471 plot.triggerRedrawOverlay();
13472 } else if (!auto) {
13473 highlights[i].auto = false;
13474 }
13475 }
13476
13477 function unhighlight(s) {
13478 if (s == null) {
13479 highlights = [];
13480 plot.triggerRedrawOverlay();
13481 }
13482
13483 //if (typeof s == "number") {
13484 // s = series[s];
13485 //}
13486
13487 var i = indexOfHighlight(s);
13488
13489 if (i != -1) {
13490 highlights.splice(i, 1);
13491 plot.triggerRedrawOverlay();
13492 }
13493 }
13494
13495 function indexOfHighlight(s) {
13496 for (var i = 0; i < highlights.length; ++i) {
13497 var h = highlights[i];
13498 if (h.series == s)
13499 return i;
13500 }
13501 return -1;
13502 }
13503
13504 function drawOverlay(plot, octx) {
13505
13506 var options = plot.getOptions();
13507
13508 var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
13509
13510 octx.save();
13511 octx.translate(centerLeft, centerTop);
13512 octx.scale(1, options.series.pie.tilt);
13513
13514 for (var i = 0; i < highlights.length; ++i) {
13515 drawHighlight(highlights[i].series);
13516 }
13517
13518 drawDonutHole(octx);
13519
13520 octx.restore();
13521
13522 function drawHighlight(series) {
13523
13524 if (series.angle <= 0 || isNaN(series.angle)) {
13525 return;
13526 }
13527
13528 //octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
13529 octx.fillStyle = "rgba(255, 255, 255, " + options.series.pie.highlight.opacity + ")"; // this is temporary until we have access to parseColor
13530 octx.beginPath();
13531 if (Math.abs(series.angle - Math.PI * 2) > 0.000000001) {
13532 octx.moveTo(0, 0); // Center of the pie
13533 }
13534 octx.arc(0, 0, radius, series.startAngle, series.startAngle + series.angle / 2, false);
13535 octx.arc(0, 0, radius, series.startAngle + series.angle / 2, series.startAngle + series.angle, false);
13536 octx.closePath();
13537 octx.fill();
13538 }
13539 }
13540 } // end init (plugin body)
13541
13542 // define pie specific options and their default values
13543
13544 var options = {
13545 series: {
13546 pie: {
13547 show: false,
13548 radius: "auto", // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
13549 innerRadius: 0, /* for donut */
13550 startAngle: 3/2,
13551 tilt: 1,
13552 shadow: {
13553 left: 5, // shadow left offset
13554 top: 15, // shadow top offset
13555 alpha: 0.02 // shadow alpha
13556 },
13557 offset: {
13558 top: 0,
13559 left: "auto"
13560 },
13561 stroke: {
13562 color: "#fff",
13563 width: 1
13564 },
13565 label: {
13566 show: "auto",
13567 formatter: function(label, slice) {
13568 return "<div style='font-size:x-small;text-align:center;padding:2px;color:" + slice.color + ";'>" + label + "<br/>" + Math.round(slice.percent) + "%</div>";
13569 }, // formatter function
13570 radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
13571 background: {
13572 color: null,
13573 opacity: 0
13574 },
13575 threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
13576 },
13577 combine: {
13578 threshold: -1, // percentage at which to combine little slices into one larger slice
13579 color: null, // color to give the new slice (auto-generated if null)
13580 label: "Other" // label to give the new slice
13581 },
13582 highlight: {
13583 //color: "#fff", // will add this functionality once parseColor is available
13584 opacity: 0.5
13585 }
13586 }
13587 }
13588 };
13589
13590 $.plot.plugins.push({
13591 init: init,
13592 options: options,
13593 name: "pie",
13594 version: "1.1"
13595 });
13596
13597})(jQuery);
13598</script>
13599 <script type="application/javascript">/* Flot plugin for automatically redrawing plots as the placeholder resizes.
13600
13601Copyright (c) 2007-2014 IOLA and Ole Laursen.
13602Licensed under the MIT license.
13603
13604It works by listening for changes on the placeholder div (through the jQuery
13605resize event plugin) - if the size changes, it will redraw the plot.
13606
13607There are no options. If you need to disable the plugin for some plots, you
13608can just fix the size of their placeholders.
13609
13610*/
13611
13612/* Inline dependency:
13613 * jQuery resize event - v1.1 - 3/14/2010
13614 * http://benalman.com/projects/jquery-resize-plugin/
13615 *
13616 * Copyright (c) 2010 "Cowboy" Ben Alman
13617 * Dual licensed under the MIT and GPL licenses.
13618 * http://benalman.com/about/license/
13619 */
13620(function($,e,t){"$:nomunge";var i=[],n=$.resize=$.extend($.resize,{}),a,r=false,s="setTimeout",u="resize",m=u+"-special-event",o="pendingDelay",l="activeDelay",f="throttleWindow";n[o]=200;n[l]=20;n[f]=true;$.event.special[u]={setup:function(){if(!n[f]&&this[s]){return false}var e=$(this);i.push(this);e.data(m,{w:e.width(),h:e.height()});if(i.length===1){a=t;h()}},teardown:function(){if(!n[f]&&this[s]){return false}var e=$(this);for(var t=i.length-1;t>=0;t--){if(i[t]==this){i.splice(t,1);break}}e.removeData(m);if(!i.length){if(r){cancelAnimationFrame(a)}else{clearTimeout(a)}a=null}},add:function(e){if(!n[f]&&this[s]){return false}var i;function a(e,n,a){var r=$(this),s=r.data(m)||{};s.w=n!==t?n:r.width();s.h=a!==t?a:r.height();i.apply(this,arguments)}if($.isFunction(e)){i=e;return a}else{i=e.handler;e.handler=a}}};function h(t){if(r===true){r=t||1}for(var s=i.length-1;s>=0;s--){var l=$(i[s]);if(l[0]==e||l.is(":visible")){var f=l.width(),c=l.height(),d=l.data(m);if(d&&(f!==d.w||c!==d.h)){l.trigger(u,[d.w=f,d.h=c]);r=t||true}}else{d=l.data(m);d.w=0;d.h=0}}if(a!==null){if(r&&(t==null||t-r<1e3)){a=e.requestAnimationFrame(h)}else{a=setTimeout(h,n[o]);r=false}}}if(!e.requestAnimationFrame){e.requestAnimationFrame=function(){return e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(t,i){return e.setTimeout(function(){t((new Date).getTime())},n[l])}}()}if(!e.cancelAnimationFrame){e.cancelAnimationFrame=function(){return e.webkitCancelRequestAnimationFrame||e.mozCancelRequestAnimationFrame||e.oCancelRequestAnimationFrame||e.msCancelRequestAnimationFrame||clearTimeout}()}})(jQuery,this);
13621
13622(function ($) {
13623 var options = { }; // no options
13624
13625 function init(plot) {
13626 function onResize() {
13627 var placeholder = plot.getPlaceholder();
13628
13629 // somebody might have hidden us and we can't plot
13630 // when we don't have the dimensions
13631 if (placeholder.width() == 0 || placeholder.height() == 0)
13632 return;
13633
13634 plot.resize();
13635 plot.setupGrid();
13636 plot.draw();
13637 }
13638
13639 function bindEvents(plot, eventHolder) {
13640 plot.getPlaceholder().resize(onResize);
13641 }
13642
13643 function shutdown(plot, eventHolder) {
13644 plot.getPlaceholder().unbind("resize", onResize);
13645 }
13646
13647 plot.hooks.bindEvents.push(bindEvents);
13648 plot.hooks.shutdown.push(shutdown);
13649 }
13650
13651 $.plot.plugins.push({
13652 init: init,
13653 options: options,
13654 name: 'resize',
13655 version: '1.0'
13656 });
13657})(jQuery);
13658</script>
13659 <script type="application/javascript">
13660 $(document).ready(function() {
13661 var row = 0;
13662 var MINOR_AUTHOR_PERCENTAGE = 1.00;
13663 var isReversed = false;
13664
13665 var colorRows = function() {
13666 $(this).removeClass("odd");
13667
13668 if (row++ % 2 == 1) {
13669 $(this).addClass("odd");
13670 }
13671
13672 if(this == $(this).parent().find("tr:visible").get(-1)) {
13673 row = 0;
13674 }
13675 }
13676
13677 // Fix header and set it to the right width.
13678 var remainingHeaderWidth = ($("div.logo").width() - 4) - ($("div.logo img").innerWidth() + 48)
13679 $("div.logo p").css("width", remainingHeaderWidth);
13680
13681 var filterResponsibilities = function() {
13682 $("table#blame tbody tr td:last-child").filter(function() {
13683 return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
13684 }).parent().find("td:first-child").each(function() {
13685 $("div#responsibilities div h3:contains(\"" + $(this).text() + "\")").parent().hide();
13686 });
13687 }
13688
13689 var filterTimeLine = function() {
13690 $("div#timeline table.git tbody tr").filter(function() {
13691 return $(this).find("td:has(div)").length == 0;
13692 }).hide();
13693 }
13694
13695 $("table#changes tbody tr td:last-child").filter(function() {
13696 return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
13697 }).parent().hide();
13698
13699 $("table#blame tbody tr td:last-child").filter(function() {
13700 return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
13701 }).parent().hide();
13702
13703 $("table.git tbody tr:visible").each(colorRows);
13704
13705 $("table#changes, table#blame").tablesorter({
13706 sortList: [[0,0]],
13707 headers: {
13708 0: { sorter: "text" }
13709 }
13710 }).bind("sortEnd", function() {
13711 $(this).find("tbody tr:visible").each(colorRows);
13712 });
13713
13714 $("table#changes thead tr th, table#blame thead tr th").click(function() {
13715 $(this).parent().find("th strong").remove();
13716 var parentIndex = $(this).index();
13717
13718 if (this.isReversed) {
13719 $(this).append("<strong> ∧</strong>");
13720 } else {
13721 $(this).append("<strong> ∨</strong>");
13722 }
13723 this.isReversed = !this.isReversed;
13724 });
13725
13726 $("table#changes thead tr th:first-child, table#blame thead tr th:first-child").each(function() {
13727 this.isReversed = true;
13728 $(this).append("<strong> ∨</strong>");
13729 });
13730
13731 $("table.git tfoot tr td:first-child").filter(function() {
13732 this.hiddenCount = $(this).parent().parent().parent().find("tbody tr:hidden").length;
13733 return this.hiddenCount > 0;
13734 }).each(function() {
13735 $(this).addClass("hoverable");
13736 this.innerHTML = "Show minor authors (" + this.hiddenCount + ") ∨";
13737 }).click(function() {
13738 this.clicked = !this.clicked;
13739
13740 if (this.clicked) {
13741 this.innerHTML = "Hide minor authors (" + this.hiddenCount + ") ∧";
13742 $(this).parent().parent().parent().find("tbody tr").show().each(colorRows);
13743 } else {
13744 this.innerHTML = "Show minor authors (" + this.hiddenCount + ") ∨";
13745 $(this).parent().parent().parent().find("tbody tr td:last-child").filter(function() {
13746 return parseFloat(this.innerHTML) < MINOR_AUTHOR_PERCENTAGE;
13747 }).parent().hide();
13748 $("table.git tbody tr:visible").each(colorRows);
13749 }
13750 });
13751
13752 filterResponsibilities();
13753 var hiddenResponsibilitiesCount = $("div#responsibilities div h3:hidden").length;
13754 if (hiddenResponsibilitiesCount > 0) {
13755 $("div#responsibilities div h3:visible").each(colorRows);
13756 $("div#responsibilities").prepend("<div class=\"button\">Show minor authors (" + hiddenResponsibilitiesCount + ") ∨</div>");
13757
13758 $("div#responsibilities div.button").click(function() {
13759 this.clicked = !this.clicked;
13760 if (this.clicked) {
13761 this.innerHTML = "Hide minor authors (" + hiddenResponsibilitiesCount + ") ∧";
13762 $("div#responsibilities div").show();
13763 } else {
13764 this.innerHTML = "Show minor authors (" + hiddenResponsibilitiesCount + ") ∨";
13765 filterResponsibilities();
13766 }
13767 });
13768 }
13769
13770
13771 filterTimeLine();
13772 var hiddenTimelineCount = $("div#timeline table.git tbody tr:hidden").length;
13773 if (hiddenTimelineCount > 0) {
13774 $("div#timeline table.git tbody tr:visible").each(colorRows);
13775 $("div#timeline").prepend("<div class=\"button\">Show rows with minor work (" + hiddenTimelineCount + ") ∨</div>");
13776
13777 $("div#timeline div.button").click(function() {
13778 this.clicked = !this.clicked;
13779 if (this.clicked) {
13780 this.innerHTML = "Hide rows with minor work (" + hiddenTimelineCount + ") ∧";
13781 $("div#timeline table.git tbody tr").show().each(colorRows);
13782 } else {
13783 this.innerHTML = "Show rows with minor work (" + hiddenTimelineCount + ") ∨";
13784 filterTimeLine();
13785 $("div#timeline table.git tbody tr:visible").each(colorRows);
13786 }
13787 });
13788 }
13789
13790 $("#blame_chart, #changes_chart").bind("plothover", function(event, pos, obj) {
13791 if (obj) {
13792 var selection = "table tbody tr td:contains(\"" + obj.series.label + "\")";
13793 var element = $(this).parent().find(selection);
13794
13795 if (element) {
13796 if (this.hoveredElement && this.hoveredElement.html() != element.parent().html()) {
13797 this.hoveredElement.removeClass("piehover");
13798 }
13799
13800 element.parent().addClass("piehover");
13801 this.hoveredElement = element.parent();
13802 }
13803 } else if (this.hoveredElement) {
13804 this.hoveredElement.removeClass("piehover");
13805 }
13806 });
13807
13808 // Make sure the two pie charts use the same colors.
13809
13810 var author_colors = {};
13811 $.each(changes_plot.getData(), function(i, v) {
13812 author_colors[v["label"]] = v["color"];
13813 });
13814
13815 $.each(blame_plot.getData(), function(i, v) {
13816 if (author_colors[v["label"]] != undefined) {
13817 v["color"] = author_colors[v["label"]];
13818 }
13819 });
13820
13821 blame_plot.setupGrid();
13822 blame_plot.draw();
13823
13824 // Color in metrics levels.
13825
13826 $("div#metrics div div").each(function() {
13827 var rgb = $(this).css("background-color").match(/\d+/g);
13828 rgb[0] = parseInt(rgb[0]);
13829 rgb[1] = parseInt(rgb[1]);
13830 rgb[2] = parseInt(rgb[2]);
13831
13832 if ($(this).hasClass("minimal")) {
13833 rgb[0] -= 10;
13834 rgb[1] += 10;
13835 rgb[2] -= 10;
13836 } else if ($(this).hasClass("minor")) {
13837 rgb[1] += 10;
13838 } else if ($(this).hasClass("medium")) {
13839 rgb[0] += 10;
13840 rgb[1] += 10;
13841 } else if ($(this).hasClass("bad")) {
13842 rgb[0] += 10;
13843 rgb[1] -= 10;
13844 rgb[2] -= 10;
13845 } else if ($(this).hasClass("severe")) {
13846 rgb[0] += 20;
13847 rgb[1] -= 20;
13848 rgb[2] -= 20;
13849 }
13850
13851 $(this).css("background-color", "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")");
13852 });
13853 });
13854 </script>
13855 <style type="text/css">
13856 body {
13857 background: -webkit-linear-gradient(left, #8f8a9a, #dad2d7, #8f8a9a);
13858 background: -moz-linear-gradient(left, #8f8a9a, #dad2d7, #8f8a9a);
13859 }
13860 html, body {
13861 margin: 0;
13862 font-family: "Arial";
13863 }
13864 body > div {
13865 margin: 0 auto;
13866 width: 58em;
13867 }
13868 div.box {
13869 border: 4px solid #ddd;
13870 background-color: #eee;
13871 margin: 0.75em;
13872 padding: 5px;
13873 font-size: small;
13874 border-radius: 15px;
13875 -moz-border-radius: 15px;
13876 box-shadow: 1px 1px 3px #666;
13877 -moz-box-shadow: 1px 1px 3px #666;
13878 }
13879 div.logo p {
13880 width: 60em;
13881 display:inline-block;
13882 vertical-align:middle;
13883 }
13884 div.logo img {
13885 vertical-align:middle;
13886 padding: 2px 10px 2px 2px;
13887 }
13888 body > div {
13889 display: block-inline;
13890 }
13891 body > div > div > div {
13892 position: relative;
13893 width: 100%;
13894 min-height: 140px;
13895 }
13896 table.git {
13897 font-size: small;
13898 width: 65%;
13899 padding-right: 5px;
13900 }
13901 table.full {
13902 width: 100%;
13903 }
13904 table.git th, table.git tfoot tr td {
13905 padding: 0.3em;
13906 background-color: #ddcece;
13907 border-radius: 8px 8px 0px 0px;
13908 -moz-border-radius: 8px 8px 0px 0px;
13909 }
13910 table#changes thead tr th, table#blame thead tr th, table.git tfoot tr td {
13911 border: 1px solid #eee;
13912 text-align: center;
13913 }
13914 table.git tfoot tr td {
13915 border-radius: 0px 0px 8px 8px;
13916 -moz-border-radius: 0px 0px 8px 8px;
13917 text-align: center;
13918 }
13919 table.git td, table.git th, table#timeline td, table#timeline th {
13920 padding: 0.35em;
13921 height: 2em;
13922 }
13923 table.git td div.insert {
13924 background-color: #7a7;
13925 }
13926 table.git td div.remove {
13927 background-color: #c66;
13928 }
13929 table.git td div.insert, table.git td div.remove {
13930 height: 100%;
13931 float: left;
13932 }
13933 table.git tr.odd {
13934 background-color: #dbdbdb;
13935 }
13936 table.git tr.piehover {
13937 background-color: #dddece;
13938 }
13939 div.chart {
13940 position: absolute;
13941 top: 5px;
13942 bottom: 5px;
13943 right: 0px;
13944 width: 35%;
13945 min-height: 100px;
13946 max-height: 210px;
13947 font-size: x-small;
13948 }
13949 p.error {
13950 color: #700;
13951 }
13952 table#changes thead tr th:hover, table#blame thead tr th:hover,
13953 table#changes tfoot tr td.hoverable:hover, table#blame tfoot tr td.hoverable:hover,
13954 div.button:hover, div#responsibilities div.button:hover {
13955 background-color: #eddede;
13956 border: 1px solid #bbb;
13957 cursor: hand;
13958 }
13959 div#responsibilities div, div#responsibilities div div, div#metrics div, div#metrics div div {
13960 min-height: 0px;
13961 padding: 0.5em 0.2em;
13962 width: auto;
13963 }
13964 div#metrics div {
13965 background-color: #eee;
13966 }
13967 div#responsibilities div.odd, div#metrics div.odd {
13968 background-color: #dbdbdb;
13969 }
13970 div#responsibilities p {
13971 margin-bottom: 0px;
13972 }
13973 td img, h3 img {
13974 border-radius: 3px 3px 3px 3px;
13975 -moz-border-radius: 3px 3px 3px 3px;
13976 vertical-align: middle;
13977 margin-right: 0.4em;
13978 opacity: 0.85;
13979 }
13980 td img {
13981 width: 20px;
13982 height: 20px;
13983 }
13984 h3 img {
13985 width: 32px;
13986 height: 32px;
13987 }
13988 h3, h4 {
13989 border-radius: 8px 8px 8px 8px;
13990 -moz-border-radius: 8px 8px 8px 8px;
13991 background-color: #ddcece;
13992 margin-bottom: 0.2em;
13993 margin-top: 0.6em;
13994 }
13995 h4 {
13996 margin-top: 0.2em;
13997 padding: 0.5em;
13998 }
13999 div.button, div#responsibilities div.button {
14000 border-radius: 8px 8px 8px 8px;
14001 -moz-border-radius: 8px 8px 8px 8px;
14002 border: 1px solid #eee;
14003 float: right;
14004 width: auto;
14005 padding: 0.5em;
14006 background-color: #ddcece;
14007 min-height: 0;
14008 }
14009 </style>
14010 </head>
14011 <body>
14012 <div><div class="box logo">
14013 <a href="https://github.com/ejwa/gitinspector"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABBCAYAAABGmyOTAAAACXBIWXMAABcRAAAXEQHKJvM/AAAWnElEQVR4nO1caXRcxZX+blW9XrVZsmVsg8E2m8GAwBgHs4NDIBgDSQwJSxKGQIYEOMxhzuRkGUaTbQJJhmGADCEJmQFCAJMAtiFwwhZ2g1ds2bIt27Isa1erW72+re78eGqpu9WtljfwnDPfOT6W3ruvXtX3bt26de8tEcYG/R/8Off3wusugAwAGwDjACDvBcygljsu9a3Y3RnsiNoBCSOgglKGSMqKMKmggiIWMmz4JUkoYpKBAElyoZSQighKSJKSIUFQUkgpCEoI73clpIRmRYKEIChBkARIhlACUFJAMEERQRJDCgjFBCkIkogUCBIMSYASAgLw+iMIkokVAZKIBIEUMyQRpCSuhONWRuOZda/u6Prvu1as2wggCSAGwDlgBHJjo3iz6c3QpGjv5azkaZZm5WomImIg/3MxNA/9wCPXKP+LEhg65z5x4RdnznleUP7zXKAhPFqbxg1i1u7kmvCkuoqrNvXEfn/VUx8sBdAHIIL9JJEAj7zI2tenuRR/2NU4ymWIbL+zA8njaugaeaTxiKgnqEdaZzDl3ydwTmOsARCBeeibgMAo/BgHCEnNHTR9wvyXd/Z+/65Xmj4C0AVgoFBu2+3XVcVam53Tl69OlWtTMIOAJsUi+QAzTXeZ5RjyYw5slI4VgsrYnXL39xNhQVOd7sF1CyZV3gjAB6ACgCqUy/S1fVu66QffaDx/1L1CCDxyi+praj2TwDM0s8i5l6Nyo8dF5bSkPBmeFh9k0gpRZeu6sKSTagwDAAQAo1BGJ+N+x9apCxrfLDu9VXtyQIVNZ7ZLBE8by06fIvdHdE+Pvolyujk8fT8xUPSCI+tqnmvpigIQhXe1qxMMntX01YXXIhQasb0MAlzE2/qWfeYvKwcBQPkHIUlQoCgt5Zf6vPtUyBQX+Rg56qxRbGU4OPYvvw+MyqAYpXlZaKV2k2Wem+na/eWRR7zZqUFBbfMqAB6BSdiqZpyvLdOnsZfJT9n+5aKsVmhargP+D3VNDQL+alBlJZndbcjEYjBjJqZMMdqzssqPoIKA52IWeUd5+1dkeh6i9m+8GFp9dzb9w5JaZdvzHTtVF/T5t4RCev2JL261cmWVNJISRDzkabE3z0tizAGXtX9FvsYnb//Ghx3/dMNN0Q2rLzRd1wfA1cCXHFD3uivP/F3D8+9/mJUTFvuLLtV7a/8EQUsirQS5isgVIJfGaKM42aXtHwFMAAuCzv7LXivTz73Gumsv+NzA+lWfjWac/raY+VLnoPXnhOm8roAArMFbXpp/dFVWVkkHhX5fsQ6V7KTJcBLEnaZEjyFIKyFl0HUrK1wc7gIy62mPaSLHmMoutC8pBWyDgkTCEVLCYI4F0o5WzIb3eHkTvDdwUonTEqaVfqt94AeNLZHB7PWPLm+4QpuZm1yh5wF4DQCUhFZakyjG0Vj2z9Ka9mhn006mFZ2J5IY3Wvt3d9pB82dnHn5YlU8elwiJ86rSelLYdY93mIVmiOyWBBiP/WMaYCdgBv3mLlM/1bw7tqpx5Y7Nv1k4p37O9OqTqDp4SzJpqpqEmWQXFVlN3EsiCUXklW2poCHdJadOyTS2RIav15w0LxNf8y7S0MaILLRSGNc8GBYxXQebbef1R3dEfvvc9u7dAFIAHGCQP/9893YA25v/7qy3rWrjmyKS6gmm7XMcBmuwyH1Pvv3Ln779wq3arcXLNy9v+nWfafbDcxvsm1/d2A9gM4BntnzrwpvjhvxWZTS1Cy4m8tj2uxiKEgjCRgU02Fbgh5uvP/9lPTjYrmYdc3ps45qFFlMUcEZsoJCjpnAZ+8e0k3nVPZs6f//c9u498MgbFR46/tF34xV+8yF9WE3GUvIDQdBZrStu/0YQZSe4R9PrV63Y8HCfaUYARIfekYfjfvX6b0LVlb8zD6uVErCGGNlvm3jK6etfyGheqa3UkZmerjusTPL+ZNO6L9pWRq3sGHjs6vfbh9VSCDePwDHtH4G4X7t9r/cnlr2zJ9qDkdhaURxx3/tpkPqTWV/ZL4EkgXjU1C343QWrdFDhrre3/x7exxlEoZOVg5n3v/KgL+SrSQV8qwSV/TbjAjVCn/XatntbouZPuyx3mfAHX/w4Yv65OWPcefEZJ63JlVWuEEYxl7yY/QOALsfZ/cimji0oQ14Ws+57acOuf/z8Nxw5uJpcfS68OB2Xcl9SYBVh9WpLLJWCp1VWUcEcyIDxX2pq7VlyZ7d2meX+Liobv3r+N6z+/lNOe3HD7QA2ZK+/ccmc663ujiuWnz3zrsvf2dEGAIL06ClcBNmQlogI2dafsFx4WlFSM3JhBHw7nIpgpwCV0JAR++cEVKa5P74RHnFlPxAAQNOHRtA43nXRNy75cs2ZVhVpnlB4vcavdrBlhTS5J2WvedHiHIxl/zQBSabE3naIfMpGyHAQAzyyuKR2MAmOZpw4cuKOZRHwR4Rt11rQrQQ6bG/7l0XzbYunCsMIOH39IW0ofPzFeTNjsRjaYhnMvfjSWnN708VOKsOwOZp9RrlgJcE0tAsZNZ7sDwRiwUw1fpHdOouhf2W1UAg52U45O41C4oq7Mua06mAdvDhd2XgcPKHDoFQXezE+2teFxOro+K6bSs8A2GXWDlvmA2FBOLYmQPGP/uYIYk47ekdVQKwefreAVKD8oZSyfwTSFYQJ86dPrFzZ1heDZwfHJJAbl/j6pTjcjiV79VCku5T9YzAJ8NTqkL8G3mI9rils+MR5aZtXGkKc7Xh5gn2zf37fk/5w+GhIH6QUcC0bg/EkOns68V7nIAWF0TsnUP/a1U3bhu2yIO2Us4F5o610dODmk6edCSAEwI8i8bRc9Pn8l2Q0NgVZL9BgoUdN32zOhanTNifuttyXrn1uzQvwXJdkuTFz4xIfCXlbZ3P7Lg0O7I8bc/If33v/hMf/9rgIVO5wLdPXvVv/8TPPr3z8qvfaHv/5zuhj846YEJ81y/jeG+cfNRzAEhpyZJrkx++KujRTQcceEQ7M+c55x88BUA0gjBIkRn9+wwzho680v9+8S4JmlPIxGEz95E7sD/iXL35h3cMA+uERWNY8DFQE/j2p8XJNLLFwHxzporAiXRek+nvPrp/Umzeu2ppgiEzzDBPi9Ow1QV6qcFgLijWYG74ySIRmpDMnXDa99pr7L2m4AEAdgCoU2Kv+X16/wCb6yc71be8fC/52Qbpg2P4xmHrhTuzxB5YvfmH9WOQpeOF3AoAPGi+t6r3nuscsosN3vfpxxCfEUV57+0+inRwMJJIZ68SlTXkuVF1dfRKsuSfuDK/QisB5Ay9l/3IkOMhUNXUgNRM1/s/96KLZsX9+bfMqeG6HAwBdP/rSIjh0Uf+arU2TU5lrdW76NMf+MYO6HLOuz+dfvvjZVUXJG7j32oa0ad/S2jO4aUt3dMesuqrph9VXn1JbEbqsczD9VPfbG1qODgc+7/X9wGigINFb5VfHrbt6/gUNz6x8AwCaGpf4rE17LoEgtz6ATVlZpcFKAjm5yHy2Sr3EB4RkLFGnNUuMrMgeNFdafZGtNWnzOqvEtGIGRRRPjPhCyxY/t6YoeYn7vt5gs366fU/0IbKdY06dPul8khItXbGmxLvND8wxaMHR4UD9UEcPWDTG1v6nFWdOdWOx2z685MQvCnac5KotYWlZh9ma113ybltTVlYRQ+rRgxxzSzfk5jMBkCSyxA23QSDhgkTu6pSb/2CAetmu61OhZYv+VFzzEvd9vcGV4un169t+fHhn/zVK0ASXOU1E6gRgIQcUlero/uKMlz7qWn72zLuh9Y1VfmNqld+o7xqId/eknJds4f5PrqwiCAUSVMpej5m+ZM6SVPABtBiObBdEuRmgLteq6zN8yxaXIW/t+tZh8gBAEIWG2jjoGNqq/Ws5OaUJMqsp7IXfx5gKBfkPAkHz6LQgSBAo7zoRWGumiI/rIiI8NnmCnl29fufd0zsj12bJO5BTdDzY9r2bP5feufliNjMTg4dNdY791bKbmr4y70SI8Dmweh/NLjCKwEpDF/NDSro0NGIyScvhmFrOAEloaJGXpWdQN9t1ERlatqjEgjFM3setnyp5zXdedWFizTs3aa0tCIlMV0caANhXc7Ld1b6oN2GuArAKAEThKoy9KN8gIsqxgSPXoQWGNJAZ5ALUo626Xmksu+wQJw8AMq1tZ1muznDdlDtVRWiTZq+bDnMzg9WA68zMygpiWXInUq58gxlU3AaSgGDSADRAAwZq+0KhZYuXF/fzEg/d3KBHyLtBCZrA3hblEycPAFzHlKx1On4cokIogMnrRyZxDJhd0xkJqAjQUDRmdPlZAYreJxLDzvfwYLWjpZOxCQTqtNK1XUKV1ryHbm7QjvPs2hHyqj4t4rKQWm/yGXLKxKZMIwk5hYhE821X3mTHE5fFbbdvanVwZVZWMOvi8dTiP+faPzCYiMWowWqHKR1N+vqUmNhOYsVlpRaMIfJWfdx697RDhDwAOOWMTc+mLLyViUZnW5G+I5xEoj61dfPllm3LHXHriYWvNfdnZdWwBgIAEYPHiNUVjo5AECxQeFmy0Abd1ENYevUrmx9DCZunHfvZtR/vuvvIzoHr5SFCHuCF9IHmXzx17jGzbcc5Z0tPwj16QmWGbf3ijevaormyipgke/WjhdyUrWUhJkEsRw2aGJKUeGThs6sfhafBeeQl7//GXIf0k6vX7xwiD9WHCnm5WHDhRTLV3mafPLm70pgw0aZgSGJda56MYpDSDJ2zkhSr0ypKJkn4cvzAkYADy6Un/PqtbgAS3iYkjzyX9JOr1+z81yN7Dl3ytv795Xf0vv/WuWDtJ8DvJpMZLeiy9VecuvSUF9Y+m5UTGO3G5KKo/WOAoqTrM0JGXtna0QQgDi+4CgCY9NM/d2IkIJqvedBPrlp/aJPXdOuVlyZaW86LO3rP7njmQS3VrV3JzB+066bdTPrLH1w0Z3JWVtHwKjx2ICbX/kVcq6bDpRcX/aXplxhn7C56/9fmuqSf/Gjtjh/N6I4esuQBQLpzd0PGtG1m51+ufL81a/O2r108N8Fm8vaWgYF5AFYAXo200sTjtn8JxzWifrVu0V+a/gNezjaGcuT94mtz2eEnP1qz/cdHdQ9cdyiTBwBw7EpNlL7gzda8BUOEAhJgNa1+wrAfqIBRifWCgeWX78YUu2/2pf8AIA0v5D5mHTE/s0R2b04tbW/r+c7M3tjX6RBabUtDx/2CZq36wrzvGn4fAECQIDs+OJMY8Ql+48K1VzQsgFCxUQHVHBSzf0IEDPXDdzZugRdAzRR/NOe5q5e67d+7YlNd3+AcJanSPUTrAXOhlW+37ZpznVh0rgmA8j0UjUTiRABIuxxTDFLDBZYlkLV/LKDSmjvgBU8Z4zykIpRsE3XVGn2xfR7UJ4kznl/zBIAnxiPrTWEeSaWOVb7LDK18w5Uge5EXpmrHkFt84+nRXsAFlMUUCBLvdbJ/LGy7a8kJWvqmHBv6wxvUCL3zziUN6fjgl3QmY5CQr5z4+OuvZ2UVDdlA76hWaS3UAATDDfuNw2sMg6O2LeElstPlOiSVOiE1kHzbGGOXsy9ozzizYhm76qiqQEuVQLTf1hMlw67xyWj5p0tjsHXXVamB6MlO3Ylvb18yJRhv3X6XmU5WgWERcOurFx2/PrudE1w8+z/K/mX/z8RS3fcsnL0AQBBeSnNMUrp+eN1JwqdsMZg8UvPYOeS9hXa1YwiYCjATGhVticzMAduu2992hWlWadZ9Jy5tsgaVucROpyYO2vqphEUPEVA7mEqdOSybdaRLpTQL9yBB2x2YN7nylmPrQlXw8sKhUh3hZ5ZI6eef7emK/SZsyPnee/Z+BWaCyIDCGc5/14ywb/vsquDHIUEjZ9po/78Rs04Gpajbctc1l3IycU7cdNqFnrY0GJQ9RLBiGXc4rSkIpJwhzaA8V7r48YVaIae6aavvqWsW/NtxE6snAJgMoBJFkuuRNv/DDijubGydw4zAvgymX9O0LYPmSVuiyRO2xdKzNw1mGnYlrRkA0Ga6R2+MpU/LaB5uO245E5oGM6c1xTOnJSAm7ss7BemXJcFIbFh3u5tO1ZDG8gvefNMh22oghqjzGduGZTnHDyylHYUZz/qkU0uRuFx6VcMfr549dSa85PrwIPp/uuSE/l/c8GHGcsORNzc6E3xq3ljtl0LExeTdg4lpWmtUGbI/ZIi4rV2frbW3HmnNttaGzslIGILMkKDBIFGcHKesfS6GhhVNHxpVld+3qyYttatr7znvrZZlACBgbLa0XpFJhIeLLAsd6aFxjqDU8a3ahFnZS5w4fVr11Gc2d+yCVyeTAgDbwQ2w0sucVdvOCElMKmxzvIiYdp0A6elhX0uFkklbw7fZdk8e6xm/pNSMsH/7vrwvFyf/aXVzx29/nE60tsz9cGHkRg2XXFjQLpLV06SBJmSTSt4iQjx6FS5X5mkNJpR2XaDwQa2RiabYEKLG1lz2FGwxEADLcXw+IcwKJZP5dw4uOhpvCQ1s3/azPUufOEYxHEMwAAHNgsjgoOOmPgC86gTFXpFlwTwtd3xrnzjZKxStrvyENoD925pvtfp6Z6a1WMuMltrqaiDgh5sxoTMZVIN3ZWWLTeE87G/V9v4wrUC2qXUw7urKSiniI/yNZlIAriDS4P1fhq2BgfqUZUfOOa/lB150eqw+glSOC5MXTBjX8dWDiBqf7OtIu0e1Jcyjg0qmLWafy1plu5jbGT9gSYKTcXUoautaW8pwWCAWAg8Wb700JMEMGkqXIw/wprBkXcb+FSvFHYofHswTl5MDRrfNbMRttybpOCFFZBFBZ/vjam8XydCQQjoVSkYHLKd+RyJ9HADU+pQ6KuzfawKFEEliPXH1olOeE5RbLwACiaCVSN0+/7XmrcBQZQLnbbH2Kb150HB40NeOINoBIOaibsdg8hiDyAaAer/RVSFlPChFBgCOCvt3hqRIWloHlBBWnU/tU9W+q4w1GZ2ejnQauUm2oaUiIDUPO+5qrKrOA3BqZb/I7jadqXHHrfFJaTGDErYdlkROjU9GAKBCyWT+Cg3UB4yevezfqD6e+tyqvwL463gaUI5lRRH0CaSsUeVrn6b9AwDTdVXGcX1pxw0AgCRyJvnVnmpD7VdcjLWufKctGoWnIw4AcGOjWN/y1tdsx6GxKtsJ7HPao0+f886GAQBQtmmvw4RQvUhb7HjaWFiBVcalOXiYHvK3AWhLOm5ICXL8QpQ9tVQOjkDQst3kjljKgheKswFgxfJHApMnVFxNgF8wAMqfusPPawTJ4Lcx9PdmlFLG72y/fs8UtE6wPpJ5rHMWn6z9yyKsZNk/gDMeMJhiEvVN0fR/wouoJzEUz7x8dUdqxVlH3rspmvJ3xoqHOIVkUgC+esbU4b2wmn7vso5tt332juTE8AP+aHqnz7Srh6oE2Qs7D09dHikMB4ihlaGYUkwoCCRoBhzLhhxayA/GqfK9hSMoMEDu1IjDb9/4StNyeNnEeK7Mond3vTGetu7ZNfLHjhQAHPPgX5dt/dZn25Jh9yfJgDgOJG2wNgCG1iVqnB2XA4asMOKOC8+OjHw2rcHsfjNeG5KO4xzQGOC4kZ0sRHAc7Wqt9e40/fILyzc8AyCBMqdAx4ti5NA718yf35exjaRjKrcEgXABSGB520DL0qaOGEbODWP395dMi/X0TG0diAciSbPk32f5pPBBW6ztV03t/fD6Z2K8hxj/Hwcf/wvPBJZ+9MswgwAAADt0RVh0QXV0aG9yAEFkYW0gV2FsZGVuYmVyZywgYmFzZWQgb24gdGhlIEdpdCBMb2dvIGJ5IEphc29uIExvbmdbAluwAAAAJWlUWHRDb3B5cmlnaHQAAAAAAMKpIDIwMTUgQWRhbSBXYWxkZW5iZXJn6lGO2AAAAsJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkJz8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0nYWRvYmU6bnM6bWV0YS8nIHg6eG1wdGs9J0ltYWdlOjpFeGlmVG9vbCA5LjI3Jz4KPHJkZjpSREYgeG1sbnM6cmRmPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjJz4KCiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0nJwogIHhtbG5zOmNjPSdodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMnPgogIDxjYzpsaWNlbnNlPkNyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24gMy4wIFVucG9ydGVkIExpY2Vuc2UuIFRoaXMgbGljZW5zZSBsZXRzIG90aGVycyBkaXN0cmlidXRlLCByZW1peCwgdHdlYWssIGFuZCBidWlsZCB1cG9uIHlvdXIgd29yaywgZXZlbiBjb21tZXJjaWFsbHksIGFzIGxvbmcgYXMgdGhleSBjcmVkaXQgeW91IGZvciB0aGUgb3JpZ2luYWwgY3JlYXRpb24uIFRoaXMgaXMgdGhlIG1vc3QgYWNjb21tb2RhdGluZyBvZiB0aGUgQ0MgbGljZW5zZXMgb2ZmZXJlZC4gUmVjb21tZW5kZWQgZm9yIG1heGltdW0gZGlzc2VtaW5hdGlvbiBhbmQgdXNlIG9mIGxpY2Vuc2VkIG1hdGVyaWFscy48L2NjOmxpY2Vuc2U+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic/PtC9Q+UAAAAASUVORK5CYII=" /></a>
14014 <p>Statistical information for the repository 'Aligot' was gathered on 2017/04/14.<br>The output has been generated by <a href="https://github.com/ejwa/gitinspector">gitinspector</a> 0.5.0dev. The statistical analysis tool for git repositories.</p>
14015 </div></div>
14016
14017<div><div class="box"><p>The following historical commit information, by author, was found.</p><div><table id="changes" class="git"><thead><tr> <th>Author</th> <th>Commits</th> <th>Insertions</th> <th>Deletions</th> <th>% of changes</th></tr></thead><tbody><tr ><td>Alexandre Chanson</td><td>61</td><td>2594</td><td>931</td><td>37.51</td></tr><tr class="odd"><td>Blackcraps</td><td>7</td><td>206</td><td>20</td><td>2.41</td></tr><tr ><td>Christopher</td><td>18</td><td>688</td><td>307</td><td>10.59</td></tr><tr class="odd"><td>Clꮥnt</td><td>28</td><td>1348</td><td>727</td><td>22.08</td></tr><tr ><td>KasakiGogensu</td><td>8</td><td>218</td><td>135</td><td>3.76</td></tr><tr class="odd"><td>ben</td><td>31</td><td>1983</td><td>240</td><td>23.66</td></tr><tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table><div class="chart" id="changes_chart"></div></div><script type="text/javascript"> changes_plot = $.plot($("#changes_chart"), [{label: "Alexandre Chanson", data: 37.51}, {label: "Blackcraps", data: 2.41}, {label: "Christopher", data: 10.59}, {label: "Cl\u00e9ment", data: 22.08}, {label: "KasakiGogensu", data: 3.76}, {label: "ben", data: 23.66}], { series: { pie: { innerRadius: 0.4, show: true, combine: { threshold: 0.01, label: "Minor Authors" } } }, grid: { hoverable: true } });</script></div></div>
14018<div><div class="box"><p>Below are the number of rows from each author that have survived and are still intact in the current revision.</p><div><table id="blame" class="git"><thead><tr> <th>Author</th> <th>Rows</th> <th>Stability</th> <th>Age</th> <th>% in comments</th> </tr></thead><tbody><tr ><td>Alexandre Chanson</td><td>1762</td><td>67.9</td><td>1.1</td><td>7.04</td><td style="display: none">38.61</td></tr><tr class="odd"><td>Blackcraps</td><td>42</td><td>20.4</td><td>3.9</td><td>0.00</td><td style="display: none">0.92</td></tr><tr ><td>Christopher</td><td>230</td><td>33.4</td><td>1.0</td><td>8.70</td><td style="display: none">5.04</td></tr><tr class="odd"><td>Clꮥnt</td><td>715</td><td>53.0</td><td>2.1</td><td>16.50</td><td style="display: none">15.67</td></tr><tr ><td>KasakiGogensu</td><td>147</td><td>67.4</td><td>2.2</td><td>36.73</td><td style="display: none">3.22</td></tr><tr class="odd"><td>ben</td><td>1668</td><td>84.1</td><td>1.2</td><td>13.07</td><td style="display: none">36.55</td></tr><tfoot><tr> <td colspan="5"> </td> </tr></tfoot></tbody></table><div class="chart" id="blame_chart"></div></div><script type="text/javascript"> blame_plot = $.plot($("#blame_chart"), [{label: "Alexandre Chanson", data: 38.61}, {label: "Blackcraps", data: 0.92}, {label: "Christopher", data: 5.04}, {label: "Cl\u00e9ment", data: 15.67}, {label: "KasakiGogensu", data: 3.22}, {label: "ben", data: 36.55}], { series: { pie: { innerRadius: 0.4, show: true, combine: { threshold: 0.01, label: "Minor Authors" } } }, grid: { hoverable: true } });</script></div></div>
14019<div><div id="timeline" class="box"><p>The following history timeline has been gathered from the repository.</p>
14020<table class="git full"><thead><tr><th>Author</th><th>2017W08</th><th>2017W11</th><th>2017W12</th><th>2017W13</th><th>2017W14</th><th>2017W15</th></tr></thead><tbody><tr><td>Alexandre Chanson</td><td></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td>.</td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td></tr><tr class="odd"><td>Blackcraps</td><td></td><td><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td></td><td></td><td></td><td></td></tr><tr><td>Christopher</td><td></td><td><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="insert"> </div></td><td></td><td><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td></tr><tr class="odd"><td>Clꮥnt</td><td></td><td></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td></td><td><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td></tr><tr><td>KasakiGogensu</td><td></td><td></td><td><div class="remove"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div></td><td></td><td></td></tr><tr class="odd"><td>ben</td><td><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td><div class="insert"> </div><div class="insert"> </div></td><td><div class="remove"> </div><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td><td></td><td><div class="remove"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div><div class="insert"> </div></td></tr><tfoot><tr><td><strong>Modified Rows:</strong></td><td>5</td><td>1148</td><td>1285</td><td>2646</td><td>1170</td><td>3143</td></tr></tfoot></tbody></table>
14021</div></div>
14022<div><div class="box" id="metrics"><div><h4>The following files have an elevated cyclomatic complexity (in order of severity)</h4><div class="minimal">src/graphics/Window.java (51 in cyclomatic complexity)</div></div></div></div>
14023<div><div class="box" id="responsibilities"><p>The following responsibilities, by author, were found in the current revision of the repository (comments are excluded from the line count, if possible).</p><div><h3>Alexandre Chanson is mostly responsible for</h3><div>src/editor/MainWindowCtl.java (301 eloc)</div><div class="odd">src/editor/mainWindow.fxml (167 eloc)</div><div>src/generator/LevelGen.java (127 eloc)</div><div class="odd">src/editor/FxUtils.java (77 eloc)</div><div>src/core/Loader.java (76 eloc)</div><div class="odd">src/core/Weapon.java (75 eloc)</div><div>src/core/Engine.java (75 eloc)</div><div class="odd">src/core/Level.java (68 eloc)</div><div>src/demo/ProceduralDemo.java (59 eloc)</div><div class="odd">src/core/Player.java (56 eloc)</div></div><div><h3>Blackcraps is mostly responsible for</h3><div>src/physics/RigidBody.java (22 eloc)</div><div class="odd">src/physics/Vector2D.java (20 eloc)</div></div><div><h3>Christopher is mostly responsible for</h3><div>src/graphics/gui/Button.java (87 eloc)</div><div class="odd">src/graphics/Window.java (42 eloc)</div><div>src/graphics/gui/KeyboardInputHandler.java (18 eloc)</div><div class="odd">src/graphics/gui/KeyBoardHandler.java (18 eloc)</div><div>src/graphics/gui/MouseInputHandler.java (9 eloc)</div><div class="odd">src/graphics/gui/MouseHandler.java (9 eloc)</div><div>src/graphics/Texture.java (7 eloc)</div><div class="odd">src/demo/PhysicDemo.java (6 eloc)</div><div>src/graphics/gui/ServiceProvider.java (5 eloc)</div><div class="odd">src/demo/MainFrame.java (5 eloc)</div></div><div><h3>Clꮥnt is mostly responsible for</h3><div>src/graphics/Window.java (182 eloc)</div><div class="odd">src/graphics/gui/GridLayout.java (76 eloc)</div><div>src/animation/Sequence.java (74 eloc)</div><div class="odd">src/graphics/Texture.java (63 eloc)</div><div>src/graphics/gui/GridLayoutConstraints.java (38 eloc)</div><div class="odd">src/demo/MainFrame.java (32 eloc)</div><div>src/graphics/gui/AbsoluteLayoutConstraints.java (31 eloc)</div><div class="odd">src/graphics/gui/AbsoluteLayout.java (22 eloc)</div><div>src/graphics/gui/GUIComponent.java (16 eloc)</div><div class="odd">src/animation/AnimationID.java (14 eloc)</div></div><div><h3>KasakiGogensu is mostly responsible for</h3><div>src/graphics/Window.java (86 eloc)</div><div class="odd">src/demo/MainFrame.java (4 eloc)</div><div>src/demo/PhysicDemo.java (3 eloc)</div></div><div><h3>ben is mostly responsible for</h3><div>src/gamelauncher/credits.fxml (153 eloc)</div><div class="odd">src/particles/Particle.java (136 eloc)</div><div>src/gamelauncher/launcher.fxml (133 eloc)</div><div class="odd">src/physics/Vector2D.java (124 eloc)</div><div>src/physics/CollisionSolver.java (123 eloc)</div><div class="odd">src/demo/FSMDemo.java (121 eloc)</div><div>src/physics/RigidBody.java (85 eloc)</div><div class="odd">src/FSM/FiniteStateMachine.java (80 eloc)</div><div>src/gamelauncher/controls.fxml (77 eloc)</div><div class="odd">src/gamelauncher/GameLauncherController.java (72 eloc)</div></div></div></div>
14024<div><div class="box"><p>The extensions below were found in the repository history (extensions used during statistical analysis are marked).</p><p>* <strong>css</strong> <strong>fxml</strong> <strong>groovy</strong> <strong>java</strong> json lvl txt uml xml yml </p></div></div>
14025 </body>
14026</html>