History

ModbusAdapters: Fixed security issues in Modbus configuration screen GSF.Web: Cleaned up new embedded resources

J. Ritchie Carroll committed 9 years ago

Commit 970956d1 parent 230686d36 files · +72369 −1648Browse files

Showing the changes to Source/Libraries/GSF.Web/Shared/Scripts (4 of 36 files). Show all

Unified · Split

4 files
  1. Source/Libraries/GSF.Web/Shared/Scripts/angular.js +31768 −0
  2. Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.client.js +5 −5
  3. Source/Libraries/GSF.Web/Shared/Scripts/jquery-ui.js +16617 −0
  4. Source/Libraries/GSF.Web/Shared/Scripts/primeui.js +13963 −0
Source/Libraries/GSF.Web/Shared/Scripts/angular.js+31768−0
@@ -0,0 +1,31768 @@
1/**
2 * @license AngularJS v1.5.8
3 * (c) 2010-2016 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window) {'use strict';
7
8/**
9 * @description
10 *
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
13 *
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16 *
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
21 * take.
22 *
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
25 *
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
31 *
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36 */
37
38function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
40 return function() {
41 var SKIP_INDEXES = 2;
42
43 var templateArgs = arguments,
44 code = templateArgs[0],
45 message = '[' + (module ? module + ':' : '') + code + '] ',
46 template = templateArgs[1],
47 paramPrefix, i;
48
49 message += template.replace(/\{\d+\}/g, function(match) {
50 var index = +match.slice(1, -1),
51 shiftedIndex = index + SKIP_INDEXES;
52
53 if (shiftedIndex < templateArgs.length) {
54 return toDebugString(templateArgs[shiftedIndex]);
55 }
56
57 return match;
58 });
59
60 message += '\nhttp://errors.angularjs.org/1.5.8/' +
61 (module ? module + '/' : '') + code;
62
63 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65 encodeURIComponent(toDebugString(templateArgs[i]));
66 }
67
68 return new ErrorConstructor(message);
69 };
70}
71
72/* We need to tell jshint what variables are being exported */
73/* global angular: true,
74 msie: true,
75 jqLite: true,
76 jQuery: true,
77 slice: true,
78 splice: true,
79 push: true,
80 toString: true,
81 ngMinErr: true,
82 angularModule: true,
83 uid: true,
84 REGEX_STRING_REGEXP: true,
85 VALIDITY_STATE_PROPERTY: true,
86
87 lowercase: true,
88 uppercase: true,
89 manualLowercase: true,
90 manualUppercase: true,
91 nodeName_: true,
92 isArrayLike: true,
93 forEach: true,
94 forEachSorted: true,
95 reverseParams: true,
96 nextUid: true,
97 setHashKey: true,
98 extend: true,
99 toInt: true,
100 inherit: true,
101 merge: true,
102 noop: true,
103 identity: true,
104 valueFn: true,
105 isUndefined: true,
106 isDefined: true,
107 isObject: true,
108 isBlankObject: true,
109 isString: true,
110 isNumber: true,
111 isDate: true,
112 isArray: true,
113 isFunction: true,
114 isRegExp: true,
115 isWindow: true,
116 isScope: true,
117 isFile: true,
118 isFormData: true,
119 isBlob: true,
120 isBoolean: true,
121 isPromiseLike: true,
122 trim: true,
123 escapeForRegexp: true,
124 isElement: true,
125 makeMap: true,
126 includes: true,
127 arrayRemove: true,
128 copy: true,
129 equals: true,
130 csp: true,
131 jq: true,
132 concat: true,
133 sliceArgs: true,
134 bind: true,
135 toJsonReplacer: true,
136 toJson: true,
137 fromJson: true,
138 convertTimezoneToLocal: true,
139 timezoneToOffset: true,
140 startingTag: true,
141 tryDecodeURIComponent: true,
142 parseKeyValue: true,
143 toKeyValue: true,
144 encodeUriSegment: true,
145 encodeUriQuery: true,
146 angularInit: true,
147 bootstrap: true,
148 getTestability: true,
149 snake_case: true,
150 bindJQuery: true,
151 assertArg: true,
152 assertArgFn: true,
153 assertNotHasOwnProperty: true,
154 getter: true,
155 getBlockNodes: true,
156 hasOwnProperty: true,
157 createMap: true,
158
159 NODE_TYPE_ELEMENT: true,
160 NODE_TYPE_ATTRIBUTE: true,
161 NODE_TYPE_TEXT: true,
162 NODE_TYPE_COMMENT: true,
163 NODE_TYPE_DOCUMENT: true,
164 NODE_TYPE_DOCUMENT_FRAGMENT: true,
165*/
166
167////////////////////////////////////
168
169/**
170 * @ngdoc module
171 * @name ng
172 * @module ng
173 * @installation
174 * @description
175 *
176 * # ng (core module)
177 * The ng module is loaded by default when an AngularJS application is started. The module itself
178 * contains the essential components for an AngularJS application to function. The table below
179 * lists a high level breakdown of each of the services/factories, filters, directives and testing
180 * components available within this core module.
181 *
182 * <div doc-module-components="ng"></div>
183 */
184
185var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
186
187// The name of a form control's ValidityState property.
188// This is used so that it's possible for internal tests to create mock ValidityStates.
189var VALIDITY_STATE_PROPERTY = 'validity';
190
191var hasOwnProperty = Object.prototype.hasOwnProperty;
192
193var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
194var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
195
196
197var manualLowercase = function(s) {
198 /* jshint bitwise: false */
199 return isString(s)
200 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
201 : s;
202};
203var manualUppercase = function(s) {
204 /* jshint bitwise: false */
205 return isString(s)
206 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
207 : s;
208};
209
210
211// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
212// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
213// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
214if ('i' !== 'I'.toLowerCase()) {
215 lowercase = manualLowercase;
216 uppercase = manualUppercase;
217}
218
219
220var
221 msie, // holds major version number for IE, or NaN if UA is not IE.
222 jqLite, // delay binding since jQuery could be loaded after us.
223 jQuery, // delay binding
224 slice = [].slice,
225 splice = [].splice,
226 push = [].push,
227 toString = Object.prototype.toString,
228 getPrototypeOf = Object.getPrototypeOf,
229 ngMinErr = minErr('ng'),
230
231 /** @name angular */
232 angular = window.angular || (window.angular = {}),
233 angularModule,
234 uid = 0;
235
236/**
237 * documentMode is an IE-only property
238 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
239 */
240msie = window.document.documentMode;
241
242
243/**
244 * @private
245 * @param {*} obj
246 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
247 * String ...)
248 */
249function isArrayLike(obj) {
250
251 // `null`, `undefined` and `window` are not array-like
252 if (obj == null || isWindow(obj)) return false;
253
254 // arrays, strings and jQuery/jqLite objects are array like
255 // * jqLite is either the jQuery or jqLite constructor function
256 // * we have to check the existence of jqLite first as this method is called
257 // via the forEach method when constructing the jqLite object in the first place
258 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
259
260 // Support: iOS 8.2 (not reproducible in simulator)
261 // "length" in obj used to prevent JIT error (gh-11508)
262 var length = "length" in Object(obj) && obj.length;
263
264 // NodeList objects (with `item` method) and
265 // other objects with suitable length characteristics are array-like
266 return isNumber(length) &&
267 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
268
269}
270
271/**
272 * @ngdoc function
273 * @name angular.forEach
274 * @module ng
275 * @kind function
276 *
277 * @description
278 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
279 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
280 * is the value of an object property or an array element, `key` is the object property key or
281 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
282 *
283 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
284 * using the `hasOwnProperty` method.
285 *
286 * Unlike ES262's
287 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
288 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
289 * return the value provided.
290 *
291 ```js
292 var values = {name: 'misko', gender: 'male'};
293 var log = [];
294 angular.forEach(values, function(value, key) {
295 this.push(key + ': ' + value);
296 }, log);
297 expect(log).toEqual(['name: misko', 'gender: male']);
298 ```
299 *
300 * @param {Object|Array} obj Object to iterate over.
301 * @param {Function} iterator Iterator function.
302 * @param {Object=} context Object to become context (`this`) for the iterator function.
303 * @returns {Object|Array} Reference to `obj`.
304 */
305
306function forEach(obj, iterator, context) {
307 var key, length;
308 if (obj) {
309 if (isFunction(obj)) {
310 for (key in obj) {
311 // Need to check if hasOwnProperty exists,
312 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
313 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
314 iterator.call(context, obj[key], key, obj);
315 }
316 }
317 } else if (isArray(obj) || isArrayLike(obj)) {
318 var isPrimitive = typeof obj !== 'object';
319 for (key = 0, length = obj.length; key < length; key++) {
320 if (isPrimitive || key in obj) {
321 iterator.call(context, obj[key], key, obj);
322 }
323 }
324 } else if (obj.forEach && obj.forEach !== forEach) {
325 obj.forEach(iterator, context, obj);
326 } else if (isBlankObject(obj)) {
327 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
328 for (key in obj) {
329 iterator.call(context, obj[key], key, obj);
330 }
331 } else if (typeof obj.hasOwnProperty === 'function') {
332 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
333 for (key in obj) {
334 if (obj.hasOwnProperty(key)) {
335 iterator.call(context, obj[key], key, obj);
336 }
337 }
338 } else {
339 // Slow path for objects which do not have a method `hasOwnProperty`
340 for (key in obj) {
341 if (hasOwnProperty.call(obj, key)) {
342 iterator.call(context, obj[key], key, obj);
343 }
344 }
345 }
346 }
347 return obj;
348}
349
350function forEachSorted(obj, iterator, context) {
351 var keys = Object.keys(obj).sort();
352 for (var i = 0; i < keys.length; i++) {
353 iterator.call(context, obj[keys[i]], keys[i]);
354 }
355 return keys;
356}
357
358
359/**
360 * when using forEach the params are value, key, but it is often useful to have key, value.
361 * @param {function(string, *)} iteratorFn
362 * @returns {function(*, string)}
363 */
364function reverseParams(iteratorFn) {
365 return function(value, key) {iteratorFn(key, value);};
366}
367
368/**
369 * A consistent way of creating unique IDs in angular.
370 *
371 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
372 * we hit number precision issues in JavaScript.
373 *
374 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
375 *
376 * @returns {number} an unique alpha-numeric string
377 */
378function nextUid() {
379 return ++uid;
380}
381
382
383/**
384 * Set or clear the hashkey for an object.
385 * @param obj object
386 * @param h the hashkey (!truthy to delete the hashkey)
387 */
388function setHashKey(obj, h) {
389 if (h) {
390 obj.$$hashKey = h;
391 } else {
392 delete obj.$$hashKey;
393 }
394}
395
396
397function baseExtend(dst, objs, deep) {
398 var h = dst.$$hashKey;
399
400 for (var i = 0, ii = objs.length; i < ii; ++i) {
401 var obj = objs[i];
402 if (!isObject(obj) && !isFunction(obj)) continue;
403 var keys = Object.keys(obj);
404 for (var j = 0, jj = keys.length; j < jj; j++) {
405 var key = keys[j];
406 var src = obj[key];
407
408 if (deep && isObject(src)) {
409 if (isDate(src)) {
410 dst[key] = new Date(src.valueOf());
411 } else if (isRegExp(src)) {
412 dst[key] = new RegExp(src);
413 } else if (src.nodeName) {
414 dst[key] = src.cloneNode(true);
415 } else if (isElement(src)) {
416 dst[key] = src.clone();
417 } else {
418 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
419 baseExtend(dst[key], [src], true);
420 }
421 } else {
422 dst[key] = src;
423 }
424 }
425 }
426
427 setHashKey(dst, h);
428 return dst;
429}
430
431/**
432 * @ngdoc function
433 * @name angular.extend
434 * @module ng
435 * @kind function
436 *
437 * @description
438 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
439 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
440 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
441 *
442 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
443 * {@link angular.merge} for this.
444 *
445 * @param {Object} dst Destination object.
446 * @param {...Object} src Source object(s).
447 * @returns {Object} Reference to `dst`.
448 */
449function extend(dst) {
450 return baseExtend(dst, slice.call(arguments, 1), false);
451}
452
453
454/**
455* @ngdoc function
456* @name angular.merge
457* @module ng
458* @kind function
459*
460* @description
461* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
462* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
463* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
464*
465* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
466* objects, performing a deep copy.
467*
468* @param {Object} dst Destination object.
469* @param {...Object} src Source object(s).
470* @returns {Object} Reference to `dst`.
471*/
472function merge(dst) {
473 return baseExtend(dst, slice.call(arguments, 1), true);
474}
475
476
477
478function toInt(str) {
479 return parseInt(str, 10);
480}
481
482
483function inherit(parent, extra) {
484 return extend(Object.create(parent), extra);
485}
486
487/**
488 * @ngdoc function
489 * @name angular.noop
490 * @module ng
491 * @kind function
492 *
493 * @description
494 * A function that performs no operations. This function can be useful when writing code in the
495 * functional style.
496 ```js
497 function foo(callback) {
498 var result = calculateResult();
499 (callback || angular.noop)(result);
500 }
501 ```
502 */
503function noop() {}
504noop.$inject = [];
505
506
507/**
508 * @ngdoc function
509 * @name angular.identity
510 * @module ng
511 * @kind function
512 *
513 * @description
514 * A function that returns its first argument. This function is useful when writing code in the
515 * functional style.
516 *
517 ```js
518 function transformer(transformationFn, value) {
519 return (transformationFn || angular.identity)(value);
520 };
521
522 // E.g.
523 function getResult(fn, input) {
524 return (fn || angular.identity)(input);
525 };
526
527 getResult(function(n) { return n * 2; }, 21); // returns 42
528 getResult(null, 21); // returns 21
529 getResult(undefined, 21); // returns 21
530 ```
531 *
532 * @param {*} value to be returned.
533 * @returns {*} the value passed in.
534 */
535function identity($) {return $;}
536identity.$inject = [];
537
538
539function valueFn(value) {return function valueRef() {return value;};}
540
541function hasCustomToString(obj) {
542 return isFunction(obj.toString) && obj.toString !== toString;
543}
544
545
546/**
547 * @ngdoc function
548 * @name angular.isUndefined
549 * @module ng
550 * @kind function
551 *
552 * @description
553 * Determines if a reference is undefined.
554 *
555 * @param {*} value Reference to check.
556 * @returns {boolean} True if `value` is undefined.
557 */
558function isUndefined(value) {return typeof value === 'undefined';}
559
560
561/**
562 * @ngdoc function
563 * @name angular.isDefined
564 * @module ng
565 * @kind function
566 *
567 * @description
568 * Determines if a reference is defined.
569 *
570 * @param {*} value Reference to check.
571 * @returns {boolean} True if `value` is defined.
572 */
573function isDefined(value) {return typeof value !== 'undefined';}
574
575
576/**
577 * @ngdoc function
578 * @name angular.isObject
579 * @module ng
580 * @kind function
581 *
582 * @description
583 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
584 * considered to be objects. Note that JavaScript arrays are objects.
585 *
586 * @param {*} value Reference to check.
587 * @returns {boolean} True if `value` is an `Object` but not `null`.
588 */
589function isObject(value) {
590 // http://jsperf.com/isobject4
591 return value !== null && typeof value === 'object';
592}
593
594
595/**
596 * Determine if a value is an object with a null prototype
597 *
598 * @returns {boolean} True if `value` is an `Object` with a null prototype
599 */
600function isBlankObject(value) {
601 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
602}
603
604
605/**
606 * @ngdoc function
607 * @name angular.isString
608 * @module ng
609 * @kind function
610 *
611 * @description
612 * Determines if a reference is a `String`.
613 *
614 * @param {*} value Reference to check.
615 * @returns {boolean} True if `value` is a `String`.
616 */
617function isString(value) {return typeof value === 'string';}
618
619
620/**
621 * @ngdoc function
622 * @name angular.isNumber
623 * @module ng
624 * @kind function
625 *
626 * @description
627 * Determines if a reference is a `Number`.
628 *
629 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
630 *
631 * If you wish to exclude these then you can use the native
632 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
633 * method.
634 *
635 * @param {*} value Reference to check.
636 * @returns {boolean} True if `value` is a `Number`.
637 */
638function isNumber(value) {return typeof value === 'number';}
639
640
641/**
642 * @ngdoc function
643 * @name angular.isDate
644 * @module ng
645 * @kind function
646 *
647 * @description
648 * Determines if a value is a date.
649 *
650 * @param {*} value Reference to check.
651 * @returns {boolean} True if `value` is a `Date`.
652 */
653function isDate(value) {
654 return toString.call(value) === '[object Date]';
655}
656
657
658/**
659 * @ngdoc function
660 * @name angular.isArray
661 * @module ng
662 * @kind function
663 *
664 * @description
665 * Determines if a reference is an `Array`.
666 *
667 * @param {*} value Reference to check.
668 * @returns {boolean} True if `value` is an `Array`.
669 */
670var isArray = Array.isArray;
671
672/**
673 * @ngdoc function
674 * @name angular.isFunction
675 * @module ng
676 * @kind function
677 *
678 * @description
679 * Determines if a reference is a `Function`.
680 *
681 * @param {*} value Reference to check.
682 * @returns {boolean} True if `value` is a `Function`.
683 */
684function isFunction(value) {return typeof value === 'function';}
685
686
687/**
688 * Determines if a value is a regular expression object.
689 *
690 * @private
691 * @param {*} value Reference to check.
692 * @returns {boolean} True if `value` is a `RegExp`.
693 */
694function isRegExp(value) {
695 return toString.call(value) === '[object RegExp]';
696}
697
698
699/**
700 * Checks if `obj` is a window object.
701 *
702 * @private
703 * @param {*} obj Object to check
704 * @returns {boolean} True if `obj` is a window obj.
705 */
706function isWindow(obj) {
707 return obj && obj.window === obj;
708}
709
710
711function isScope(obj) {
712 return obj && obj.$evalAsync && obj.$watch;
713}
714
715
716function isFile(obj) {
717 return toString.call(obj) === '[object File]';
718}
719
720
721function isFormData(obj) {
722 return toString.call(obj) === '[object FormData]';
723}
724
725
726function isBlob(obj) {
727 return toString.call(obj) === '[object Blob]';
728}
729
730
731function isBoolean(value) {
732 return typeof value === 'boolean';
733}
734
735
736function isPromiseLike(obj) {
737 return obj && isFunction(obj.then);
738}
739
740
741var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
742function isTypedArray(value) {
743 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
744}
745
746function isArrayBuffer(obj) {
747 return toString.call(obj) === '[object ArrayBuffer]';
748}
749
750
751var trim = function(value) {
752 return isString(value) ? value.trim() : value;
753};
754
755// Copied from:
756// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
757// Prereq: s is a string.
758var escapeForRegexp = function(s) {
759 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
760 replace(/\x08/g, '\\x08');
761};
762
763
764/**
765 * @ngdoc function
766 * @name angular.isElement
767 * @module ng
768 * @kind function
769 *
770 * @description
771 * Determines if a reference is a DOM element (or wrapped jQuery element).
772 *
773 * @param {*} value Reference to check.
774 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
775 */
776function isElement(node) {
777 return !!(node &&
778 (node.nodeName // We are a direct element.
779 || (node.prop && node.attr && node.find))); // We have an on and find method part of jQuery API.
780}
781
782/**
783 * @param str 'key1,key2,...'
784 * @returns {object} in the form of {key1:true, key2:true, ...}
785 */
786function makeMap(str) {
787 var obj = {}, items = str.split(','), i;
788 for (i = 0; i < items.length; i++) {
789 obj[items[i]] = true;
790 }
791 return obj;
792}
793
794
795function nodeName_(element) {
796 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
797}
798
799function includes(array, obj) {
800 return Array.prototype.indexOf.call(array, obj) != -1;
801}
802
803function arrayRemove(array, value) {
804 var index = array.indexOf(value);
805 if (index >= 0) {
806 array.splice(index, 1);
807 }
808 return index;
809}
810
811/**
812 * @ngdoc function
813 * @name angular.copy
814 * @module ng
815 * @kind function
816 *
817 * @description
818 * Creates a deep copy of `source`, which should be an object or an array.
819 *
820 * * If no destination is supplied, a copy of the object or array is created.
821 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
822 * are deleted and then all elements/properties from the source are copied to it.
823 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
824 * * If `source` is identical to `destination` an exception will be thrown.
825 *
826 * <br />
827 * <div class="alert alert-warning">
828 * Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
829 * and on `destination`) will be ignored.
830 * </div>
831 *
832 * @param {*} source The source that will be used to make a copy.
833 * Can be any type, including primitives, `null`, and `undefined`.
834 * @param {(Object|Array)=} destination Destination into which the source is copied. If
835 * provided, must be of the same type as `source`.
836 * @returns {*} The copy or updated `destination`, if `destination` was specified.
837 *
838 * @example
839 <example module="copyExample">
840 <file name="index.html">
841 <div ng-controller="ExampleController">
842 <form novalidate class="simple-form">
843 <label>Name: <input type="text" ng-model="user.name" /></label><br />
844 <label>Age: <input type="number" ng-model="user.age" /></label><br />
845 Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
846 <label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
847 <button ng-click="reset()">RESET</button>
848 <button ng-click="update(user)">SAVE</button>
849 </form>
850 <pre>form = {{user | json}}</pre>
851 <pre>master = {{master | json}}</pre>
852 </div>
853 </file>
854 <file name="script.js">
855 // Module: copyExample
856 angular.
857 module('copyExample', []).
858 controller('ExampleController', ['$scope', function($scope) {
859 $scope.master = {};
860
861 $scope.reset = function() {
862 // Example with 1 argument
863 $scope.user = angular.copy($scope.master);
864 };
865
866 $scope.update = function(user) {
867 // Example with 2 arguments
868 angular.copy(user, $scope.master);
869 };
870
871 $scope.reset();
872 }]);
873 </file>
874 </example>
875 */
876function copy(source, destination) {
877 var stackSource = [];
878 var stackDest = [];
879
880 if (destination) {
881 if (isTypedArray(destination) || isArrayBuffer(destination)) {
882 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
883 }
884 if (source === destination) {
885 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
886 }
887
888 // Empty the destination object
889 if (isArray(destination)) {
890 destination.length = 0;
891 } else {
892 forEach(destination, function(value, key) {
893 if (key !== '$$hashKey') {
894 delete destination[key];
895 }
896 });
897 }
898
899 stackSource.push(source);
900 stackDest.push(destination);
901 return copyRecurse(source, destination);
902 }
903
904 return copyElement(source);
905
906 function copyRecurse(source, destination) {
907 var h = destination.$$hashKey;
908 var key;
909 if (isArray(source)) {
910 for (var i = 0, ii = source.length; i < ii; i++) {
911 destination.push(copyElement(source[i]));
912 }
913 } else if (isBlankObject(source)) {
914 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
915 for (key in source) {
916 destination[key] = copyElement(source[key]);
917 }
918 } else if (source && typeof source.hasOwnProperty === 'function') {
919 // Slow path, which must rely on hasOwnProperty
920 for (key in source) {
921 if (source.hasOwnProperty(key)) {
922 destination[key] = copyElement(source[key]);
923 }
924 }
925 } else {
926 // Slowest path --- hasOwnProperty can't be called as a method
927 for (key in source) {
928 if (hasOwnProperty.call(source, key)) {
929 destination[key] = copyElement(source[key]);
930 }
931 }
932 }
933 setHashKey(destination, h);
934 return destination;
935 }
936
937 function copyElement(source) {
938 // Simple values
939 if (!isObject(source)) {
940 return source;
941 }
942
943 // Already copied values
944 var index = stackSource.indexOf(source);
945 if (index !== -1) {
946 return stackDest[index];
947 }
948
949 if (isWindow(source) || isScope(source)) {
950 throw ngMinErr('cpws',
951 "Can't copy! Making copies of Window or Scope instances is not supported.");
952 }
953
954 var needsRecurse = false;
955 var destination = copyType(source);
956
957 if (destination === undefined) {
958 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
959 needsRecurse = true;
960 }
961
962 stackSource.push(source);
963 stackDest.push(destination);
964
965 return needsRecurse
966 ? copyRecurse(source, destination)
967 : destination;
968 }
969
970 function copyType(source) {
971 switch (toString.call(source)) {
972 case '[object Int8Array]':
973 case '[object Int16Array]':
974 case '[object Int32Array]':
975 case '[object Float32Array]':
976 case '[object Float64Array]':
977 case '[object Uint8Array]':
978 case '[object Uint8ClampedArray]':
979 case '[object Uint16Array]':
980 case '[object Uint32Array]':
981 return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
982
983 case '[object ArrayBuffer]':
984 //Support: IE10
985 if (!source.slice) {
986 var copied = new ArrayBuffer(source.byteLength);
987 new Uint8Array(copied).set(new Uint8Array(source));
988 return copied;
989 }
990 return source.slice(0);
991
992 case '[object Boolean]':
993 case '[object Number]':
994 case '[object String]':
995 case '[object Date]':
996 return new source.constructor(source.valueOf());
997
998 case '[object RegExp]':
999 var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1000 re.lastIndex = source.lastIndex;
1001 return re;
1002
1003 case '[object Blob]':
1004 return new source.constructor([source], {type: source.type});
1005 }
1006
1007 if (isFunction(source.cloneNode)) {
1008 return source.cloneNode(true);
1009 }
1010 }
1011}
1012
1013
1014/**
1015 * @ngdoc function
1016 * @name angular.equals
1017 * @module ng
1018 * @kind function
1019 *
1020 * @description
1021 * Determines if two objects or two values are equivalent. Supports value types, regular
1022 * expressions, arrays and objects.
1023 *
1024 * Two objects or values are considered equivalent if at least one of the following is true:
1025 *
1026 * * Both objects or values pass `===` comparison.
1027 * * Both objects or values are of the same type and all of their properties are equal by
1028 * comparing them with `angular.equals`.
1029 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1030 * * Both values represent the same regular expression (In JavaScript,
1031 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1032 * representation matches).
1033 *
1034 * During a property comparison, properties of `function` type and properties with names
1035 * that begin with `$` are ignored.
1036 *
1037 * Scope and DOMWindow objects are being compared only by identify (`===`).
1038 *
1039 * @param {*} o1 Object or value to compare.
1040 * @param {*} o2 Object or value to compare.
1041 * @returns {boolean} True if arguments are equal.
1042 *
1043 * @example
1044 <example module="equalsExample" name="equalsExample">
1045 <file name="index.html">
1046 <div ng-controller="ExampleController">
1047 <form novalidate>
1048 <h3>User 1</h3>
1049 Name: <input type="text" ng-model="user1.name">
1050 Age: <input type="number" ng-model="user1.age">
1051
1052 <h3>User 2</h3>
1053 Name: <input type="text" ng-model="user2.name">
1054 Age: <input type="number" ng-model="user2.age">
1055
1056 <div>
1057 <br/>
1058 <input type="button" value="Compare" ng-click="compare()">
1059 </div>
1060 User 1: <pre>{{user1 | json}}</pre>
1061 User 2: <pre>{{user2 | json}}</pre>
1062 Equal: <pre>{{result}}</pre>
1063 </form>
1064 </div>
1065 </file>
1066 <file name="script.js">
1067 angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
1068 $scope.user1 = {};
1069 $scope.user2 = {};
1070 $scope.result;
1071 $scope.compare = function() {
1072 $scope.result = angular.equals($scope.user1, $scope.user2);
1073 };
1074 }]);
1075 </file>
1076 </example>
1077 */
1078function equals(o1, o2) {
1079 if (o1 === o2) return true;
1080 if (o1 === null || o2 === null) return false;
1081 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1082 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1083 if (t1 == t2 && t1 == 'object') {
1084 if (isArray(o1)) {
1085 if (!isArray(o2)) return false;
1086 if ((length = o1.length) == o2.length) {
1087 for (key = 0; key < length; key++) {
1088 if (!equals(o1[key], o2[key])) return false;
1089 }
1090 return true;
1091 }
1092 } else if (isDate(o1)) {
1093 if (!isDate(o2)) return false;
1094 return equals(o1.getTime(), o2.getTime());
1095 } else if (isRegExp(o1)) {
1096 if (!isRegExp(o2)) return false;
1097 return o1.toString() == o2.toString();
1098 } else {
1099 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1100 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1101 keySet = createMap();
1102 for (key in o1) {
1103 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1104 if (!equals(o1[key], o2[key])) return false;
1105 keySet[key] = true;
1106 }
1107 for (key in o2) {
1108 if (!(key in keySet) &&
1109 key.charAt(0) !== '$' &&
1110 isDefined(o2[key]) &&
1111 !isFunction(o2[key])) return false;
1112 }
1113 return true;
1114 }
1115 }
1116 return false;
1117}
1118
1119var csp = function() {
1120 if (!isDefined(csp.rules)) {
1121
1122
1123 var ngCspElement = (window.document.querySelector('[ng-csp]') ||
1124 window.document.querySelector('[data-ng-csp]'));
1125
1126 if (ngCspElement) {
1127 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1128 ngCspElement.getAttribute('data-ng-csp');
1129 csp.rules = {
1130 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1131 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1132 };
1133 } else {
1134 csp.rules = {
1135 noUnsafeEval: noUnsafeEval(),
1136 noInlineStyle: false
1137 };
1138 }
1139 }
1140
1141 return csp.rules;
1142
1143 function noUnsafeEval() {
1144 try {
1145 /* jshint -W031, -W054 */
1146 new Function('');
1147 /* jshint +W031, +W054 */
1148 return false;
1149 } catch (e) {
1150 return true;
1151 }
1152 }
1153};
1154
1155/**
1156 * @ngdoc directive
1157 * @module ng
1158 * @name ngJq
1159 *
1160 * @element ANY
1161 * @param {string=} ngJq the name of the library available under `window`
1162 * to be used for angular.element
1163 * @description
1164 * Use this directive to force the angular.element library. This should be
1165 * used to force either jqLite by leaving ng-jq blank or setting the name of
1166 * the jquery variable under window (eg. jQuery).
1167 *
1168 * Since angular looks for this directive when it is loaded (doesn't wait for the
1169 * DOMContentLoaded event), it must be placed on an element that comes before the script
1170 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1171 * others ignored.
1172 *
1173 * @example
1174 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1175 ```html
1176 <!doctype html>
1177 <html ng-app ng-jq>
1178 ...
1179 ...
1180 </html>
1181 ```
1182 * @example
1183 * This example shows how to use a jQuery based library of a different name.
1184 * The library name must be available at the top most 'window'.
1185 ```html
1186 <!doctype html>
1187 <html ng-app ng-jq="jQueryLib">
1188 ...
1189 ...
1190 </html>
1191 ```
1192 */
1193var jq = function() {
1194 if (isDefined(jq.name_)) return jq.name_;
1195 var el;
1196 var i, ii = ngAttrPrefixes.length, prefix, name;
1197 for (i = 0; i < ii; ++i) {
1198 prefix = ngAttrPrefixes[i];
1199 if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1200 name = el.getAttribute(prefix + 'jq');
1201 break;
1202 }
1203 }
1204
1205 return (jq.name_ = name);
1206};
1207
1208function concat(array1, array2, index) {
1209 return array1.concat(slice.call(array2, index));
1210}
1211
1212function sliceArgs(args, startIndex) {
1213 return slice.call(args, startIndex || 0);
1214}
1215
1216
1217/* jshint -W101 */
1218/**
1219 * @ngdoc function
1220 * @name angular.bind
1221 * @module ng
1222 * @kind function
1223 *
1224 * @description
1225 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1226 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1227 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1228 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1229 *
1230 * @param {Object} self Context which `fn` should be evaluated in.
1231 * @param {function()} fn Function to be bound.
1232 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1233 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1234 */
1235/* jshint +W101 */
1236function bind(self, fn) {
1237 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1238 if (isFunction(fn) && !(fn instanceof RegExp)) {
1239 return curryArgs.length
1240 ? function() {
1241 return arguments.length
1242 ? fn.apply(self, concat(curryArgs, arguments, 0))
1243 : fn.apply(self, curryArgs);
1244 }
1245 : function() {
1246 return arguments.length
1247 ? fn.apply(self, arguments)
1248 : fn.call(self);
1249 };
1250 } else {
1251 // In IE, native methods are not functions so they cannot be bound (note: they don't need to be).
1252 return fn;
1253 }
1254}
1255
1256
1257function toJsonReplacer(key, value) {
1258 var val = value;
1259
1260 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1261 val = undefined;
1262 } else if (isWindow(value)) {
1263 val = '$WINDOW';
1264 } else if (value && window.document === value) {
1265 val = '$DOCUMENT';
1266 } else if (isScope(value)) {
1267 val = '$SCOPE';
1268 }
1269
1270 return val;
1271}
1272
1273
1274/**
1275 * @ngdoc function
1276 * @name angular.toJson
1277 * @module ng
1278 * @kind function
1279 *
1280 * @description
1281 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1282 * stripped since angular uses this notation internally.
1283 *
1284 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1285 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1286 * If set to an integer, the JSON output will contain that many spaces per indentation.
1287 * @returns {string|undefined} JSON-ified string representing `obj`.
1288 * @knownIssue
1289 *
1290 * The Safari browser throws a `RangeError` instead of returning `null` when it tries to stringify a `Date`
1291 * object with an invalid date value. The only reliable way to prevent this is to monkeypatch the
1292 * `Date.prototype.toJSON` method as follows:
1293 *
1294 * ```
1295 * var _DatetoJSON = Date.prototype.toJSON;
1296 * Date.prototype.toJSON = function() {
1297 * try {
1298 * return _DatetoJSON.call(this);
1299 * } catch(e) {
1300 * if (e instanceof RangeError) {
1301 * return null;
1302 * }
1303 * throw e;
1304 * }
1305 * };
1306 * ```
1307 *
1308 * See https://github.com/angular/angular.js/pull/14221 for more information.
1309 */
1310function toJson(obj, pretty) {
1311 if (isUndefined(obj)) return undefined;
1312 if (!isNumber(pretty)) {
1313 pretty = pretty ? 2 : null;
1314 }
1315 return JSON.stringify(obj, toJsonReplacer, pretty);
1316}
1317
1318
1319/**
1320 * @ngdoc function
1321 * @name angular.fromJson
1322 * @module ng
1323 * @kind function
1324 *
1325 * @description
1326 * Deserializes a JSON string.
1327 *
1328 * @param {string} json JSON string to deserialize.
1329 * @returns {Object|Array|string|number} Deserialized JSON string.
1330 */
1331function fromJson(json) {
1332 return isString(json)
1333 ? JSON.parse(json)
1334 : json;
1335}
1336
1337
1338var ALL_COLONS = /:/g;
1339function timezoneToOffset(timezone, fallback) {
1340 // IE/Edge do not "understand" colon (`:`) in timezone
1341 timezone = timezone.replace(ALL_COLONS, '');
1342 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1343 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1344}
1345
1346
1347function addDateMinutes(date, minutes) {
1348 date = new Date(date.getTime());
1349 date.setMinutes(date.getMinutes() + minutes);
1350 return date;
1351}
1352
1353
1354function convertTimezoneToLocal(date, timezone, reverse) {
1355 reverse = reverse ? -1 : 1;
1356 var dateTimezoneOffset = date.getTimezoneOffset();
1357 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1358 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1359}
1360
1361
1362/**
1363 * @returns {string} Returns the string representation of the element.
1364 */
1365function startingTag(element) {
1366 element = jqLite(element).clone();
1367 try {
1368 // turns out IE does not let you set .html() on elements which
1369 // are not allowed to have children. So we just ignore it.
1370 element.empty();
1371 } catch (e) {}
1372 var elemHtml = jqLite('<div>').append(element).html();
1373 try {
1374 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1375 elemHtml.
1376 match(/^(<[^>]+>)/)[1].
1377 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1378 } catch (e) {
1379 return lowercase(elemHtml);
1380 }
1381
1382}
1383
1384
1385/////////////////////////////////////////////////
1386
1387/**
1388 * Tries to decode the URI component without throwing an exception.
1389 *
1390 * @private
1391 * @param str value potential URI component to check.
1392 * @returns {boolean} True if `value` can be decoded
1393 * with the decodeURIComponent function.
1394 */
1395function tryDecodeURIComponent(value) {
1396 try {
1397 return decodeURIComponent(value);
1398 } catch (e) {
1399 // Ignore any invalid uri component.
1400 }
1401}
1402
1403
1404/**
1405 * Parses an escaped url query string into key-value pairs.
1406 * @returns {Object.<string,boolean|Array>}
1407 */
1408function parseKeyValue(/**string*/keyValue) {
1409 var obj = {};
1410 forEach((keyValue || "").split('&'), function(keyValue) {
1411 var splitPoint, key, val;
1412 if (keyValue) {
1413 key = keyValue = keyValue.replace(/\+/g,'%20');
1414 splitPoint = keyValue.indexOf('=');
1415 if (splitPoint !== -1) {
1416 key = keyValue.substring(0, splitPoint);
1417 val = keyValue.substring(splitPoint + 1);
1418 }
1419 key = tryDecodeURIComponent(key);
1420 if (isDefined(key)) {
1421 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1422 if (!hasOwnProperty.call(obj, key)) {
1423 obj[key] = val;
1424 } else if (isArray(obj[key])) {
1425 obj[key].push(val);
1426 } else {
1427 obj[key] = [obj[key],val];
1428 }
1429 }
1430 }
1431 });
1432 return obj;
1433}
1434
1435function toKeyValue(obj) {
1436 var parts = [];
1437 forEach(obj, function(value, key) {
1438 if (isArray(value)) {
1439 forEach(value, function(arrayValue) {
1440 parts.push(encodeUriQuery(key, true) +
1441 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1442 });
1443 } else {
1444 parts.push(encodeUriQuery(key, true) +
1445 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1446 }
1447 });
1448 return parts.length ? parts.join('&') : '';
1449}
1450
1451
1452/**
1453 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1454 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1455 * segments:
1456 * segment = *pchar
1457 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1458 * pct-encoded = "%" HEXDIG HEXDIG
1459 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1460 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1461 * / "*" / "+" / "," / ";" / "="
1462 */
1463function encodeUriSegment(val) {
1464 return encodeUriQuery(val, true).
1465 replace(/%26/gi, '&').
1466 replace(/%3D/gi, '=').
1467 replace(/%2B/gi, '+');
1468}
1469
1470
1471/**
1472 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1473 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1474 * encoded per http://tools.ietf.org/html/rfc3986:
1475 * query = *( pchar / "/" / "?" )
1476 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1477 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1478 * pct-encoded = "%" HEXDIG HEXDIG
1479 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1480 * / "*" / "+" / "," / ";" / "="
1481 */
1482function encodeUriQuery(val, pctEncodeSpaces) {
1483 return encodeURIComponent(val).
1484 replace(/%40/gi, '@').
1485 replace(/%3A/gi, ':').
1486 replace(/%24/g, '$').
1487 replace(/%2C/gi, ',').
1488 replace(/%3B/gi, ';').
1489 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1490}
1491
1492var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1493
1494function getNgAttribute(element, ngAttr) {
1495 var attr, i, ii = ngAttrPrefixes.length;
1496 for (i = 0; i < ii; ++i) {
1497 attr = ngAttrPrefixes[i] + ngAttr;
1498 if (isString(attr = element.getAttribute(attr))) {
1499 return attr;
1500 }
1501 }
1502 return null;
1503}
1504
1505/**
1506 * @ngdoc directive
1507 * @name ngApp
1508 * @module ng
1509 *
1510 * @element ANY
1511 * @param {angular.Module} ngApp an optional application
1512 * {@link angular.module module} name to load.
1513 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1514 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1515 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1516 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1517 * tracking down the root of these bugs.
1518 *
1519 * @description
1520 *
1521 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1522 * designates the **root element** of the application and is typically placed near the root element
1523 * of the page - e.g. on the `<body>` or `<html>` tags.
1524 *
1525 * There are a few things to keep in mind when using `ngApp`:
1526 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1527 * found in the document will be used to define the root element to auto-bootstrap as an
1528 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1529 * {@link angular.bootstrap} instead.
1530 * - AngularJS applications cannot be nested within each other.
1531 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1532 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1533 * {@link ngRoute.ngView `ngView`}.
1534 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1535 * causing animations to stop working and making the injector inaccessible from outside the app.
1536 *
1537 * You can specify an **AngularJS module** to be used as the root module for the application. This
1538 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1539 * should contain the application code needed or have dependencies on other modules that will
1540 * contain the code. See {@link angular.module} for more information.
1541 *
1542 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1543 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1544 * would not be resolved to `3`.
1545 *
1546 * `ngApp` is the easiest, and most common way to bootstrap an application.
1547 *
1548 <example module="ngAppDemo">
1549 <file name="index.html">
1550 <div ng-controller="ngAppDemoController">
1551 I can add: {{a}} + {{b}} = {{ a+b }}
1552 </div>
1553 </file>
1554 <file name="script.js">
1555 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1556 $scope.a = 1;
1557 $scope.b = 2;
1558 });
1559 </file>
1560 </example>
1561 *
1562 * Using `ngStrictDi`, you would see something like this:
1563 *
1564 <example ng-app-included="true">
1565 <file name="index.html">
1566 <div ng-app="ngAppStrictDemo" ng-strict-di>
1567 <div ng-controller="GoodController1">
1568 I can add: {{a}} + {{b}} = {{ a+b }}
1569
1570 <p>This renders because the controller does not fail to
1571 instantiate, by using explicit annotation style (see
1572 script.js for details)
1573 </p>
1574 </div>
1575
1576 <div ng-controller="GoodController2">
1577 Name: <input ng-model="name"><br />
1578 Hello, {{name}}!
1579
1580 <p>This renders because the controller does not fail to
1581 instantiate, by using explicit annotation style
1582 (see script.js for details)
1583 </p>
1584 </div>
1585
1586 <div ng-controller="BadController">
1587 I can add: {{a}} + {{b}} = {{ a+b }}
1588
1589 <p>The controller could not be instantiated, due to relying
1590 on automatic function annotations (which are disabled in
1591 strict mode). As such, the content of this section is not
1592 interpolated, and there should be an error in your web console.
1593 </p>
1594 </div>
1595 </div>
1596 </file>
1597 <file name="script.js">
1598 angular.module('ngAppStrictDemo', [])
1599 // BadController will fail to instantiate, due to relying on automatic function annotation,
1600 // rather than an explicit annotation
1601 .controller('BadController', function($scope) {
1602 $scope.a = 1;
1603 $scope.b = 2;
1604 })
1605 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1606 // due to using explicit annotations using the array style and $inject property, respectively.
1607 .controller('GoodController1', ['$scope', function($scope) {
1608 $scope.a = 1;
1609 $scope.b = 2;
1610 }])
1611 .controller('GoodController2', GoodController2);
1612 function GoodController2($scope) {
1613 $scope.name = "World";
1614 }
1615 GoodController2.$inject = ['$scope'];
1616 </file>
1617 <file name="style.css">
1618 div[ng-controller] {
1619 margin-bottom: 1em;
1620 -webkit-border-radius: 4px;
1621 border-radius: 4px;
1622 border: 1px solid;
1623 padding: .5em;
1624 }
1625 div[ng-controller^=Good] {
1626 border-color: #d6e9c6;
1627 background-color: #dff0d8;
1628 color: #3c763d;
1629 }
1630 div[ng-controller^=Bad] {
1631 border-color: #ebccd1;
1632 background-color: #f2dede;
1633 color: #a94442;
1634 margin-bottom: 0;
1635 }
1636 </file>
1637 </example>
1638 */
1639function angularInit(element, bootstrap) {
1640 var appElement,
1641 module,
1642 config = {};
1643
1644 // The element `element` has priority over any other element.
1645 forEach(ngAttrPrefixes, function(prefix) {
1646 var name = prefix + 'app';
1647
1648 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1649 appElement = element;
1650 module = element.getAttribute(name);
1651 }
1652 });
1653 forEach(ngAttrPrefixes, function(prefix) {
1654 var name = prefix + 'app';
1655 var candidate;
1656
1657 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1658 appElement = candidate;
1659 module = candidate.getAttribute(name);
1660 }
1661 });
1662 if (appElement) {
1663 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1664 bootstrap(appElement, module ? [module] : [], config);
1665 }
1666}
1667
1668/**
1669 * @ngdoc function
1670 * @name angular.bootstrap
1671 * @module ng
1672 * @description
1673 * Use this function to manually start up angular application.
1674 *
1675 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1676 *
1677 * Angular will detect if it has been loaded into the browser more than once and only allow the
1678 * first loaded script to be bootstrapped and will report a warning to the browser console for
1679 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1680 * multiple instances of Angular try to work on the DOM.
1681 *
1682 * <div class="alert alert-warning">
1683 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1684 * They must use {@link ng.directive:ngApp ngApp}.
1685 * </div>
1686 *
1687 * <div class="alert alert-warning">
1688 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1689 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1690 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1691 * causing animations to stop working and making the injector inaccessible from outside the app.
1692 * </div>
1693 *
1694 * ```html
1695 * <!doctype html>
1696 * <html>
1697 * <body>
1698 * <div ng-controller="WelcomeController">
1699 * {{greeting}}
1700 * </div>
1701 *
1702 * <script src="angular.js"></script>
1703 * <script>
1704 * var app = angular.module('demo', [])
1705 * .controller('WelcomeController', function($scope) {
1706 * $scope.greeting = 'Welcome!';
1707 * });
1708 * angular.bootstrap(document, ['demo']);
1709 * </script>
1710 * </body>
1711 * </html>
1712 * ```
1713 *
1714 * @param {DOMElement} element DOM element which is the root of angular application.
1715 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1716 * Each item in the array should be the name of a predefined module or a (DI annotated)
1717 * function that will be invoked by the injector as a `config` block.
1718 * See: {@link angular.module modules}
1719 * @param {Object=} config an object for defining configuration options for the application. The
1720 * following keys are supported:
1721 *
1722 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1723 * assist in finding bugs which break minified code. Defaults to `false`.
1724 *
1725 * @returns {auto.$injector} Returns the newly created injector for this app.
1726 */
1727function bootstrap(element, modules, config) {
1728 if (!isObject(config)) config = {};
1729 var defaultConfig = {
1730 strictDi: false
1731 };
1732 config = extend(defaultConfig, config);
1733 var doBootstrap = function() {
1734 element = jqLite(element);
1735
1736 if (element.injector()) {
1737 var tag = (element[0] === window.document) ? 'document' : startingTag(element);
1738 // Encode angle brackets to prevent input from being sanitized to empty string #8683.
1739 throw ngMinErr(
1740 'btstrpd',
1741 "App already bootstrapped with this element '{0}'",
1742 tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1743 }
1744
1745 modules = modules || [];
1746 modules.unshift(['$provide', function($provide) {
1747 $provide.value('$rootElement', element);
1748 }]);
1749
1750 if (config.debugInfoEnabled) {
1751 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1752 modules.push(['$compileProvider', function($compileProvider) {
1753 $compileProvider.debugInfoEnabled(true);
1754 }]);
1755 }
1756
1757 modules.unshift('ng');
1758 var injector = createInjector(modules, config.strictDi);
1759 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1760 function bootstrapApply(scope, element, compile, injector) {
1761 scope.$apply(function() {
1762 element.data('$injector', injector);
1763 compile(element)(scope);
1764 });
1765 }]
1766 );
1767 return injector;
1768 };
1769
1770 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1771 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1772
1773 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1774 config.debugInfoEnabled = true;
1775 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1776 }
1777
1778 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1779 return doBootstrap();
1780 }
1781
1782 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1783 angular.resumeBootstrap = function(extraModules) {
1784 forEach(extraModules, function(module) {
1785 modules.push(module);
1786 });
1787 return doBootstrap();
1788 };
1789
1790 if (isFunction(angular.resumeDeferredBootstrap)) {
1791 angular.resumeDeferredBootstrap();
1792 }
1793}
1794
1795/**
1796 * @ngdoc function
1797 * @name angular.reloadWithDebugInfo
1798 * @module ng
1799 * @description
1800 * Use this function to reload the current application with debug information turned on.
1801 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1802 *
1803 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1804 */
1805function reloadWithDebugInfo() {
1806 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1807 window.location.reload();
1808}
1809
1810/**
1811 * @name angular.getTestability
1812 * @module ng
1813 * @description
1814 * Get the testability service for the instance of Angular on the given
1815 * element.
1816 * @param {DOMElement} element DOM element which is the root of angular application.
1817 */
1818function getTestability(rootElement) {
1819 var injector = angular.element(rootElement).injector();
1820 if (!injector) {
1821 throw ngMinErr('test',
1822 'no injector found for element argument to getTestability');
1823 }
1824 return injector.get('$$testability');
1825}
1826
1827var SNAKE_CASE_REGEXP = /[A-Z]/g;
1828function snake_case(name, separator) {
1829 separator = separator || '_';
1830 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1831 return (pos ? separator : '') + letter.toLowerCase();
1832 });
1833}
1834
1835var bindJQueryFired = false;
1836function bindJQuery() {
1837 var originalCleanData;
1838
1839 if (bindJQueryFired) {
1840 return;
1841 }
1842
1843 // bind to jQuery if present;
1844 var jqName = jq();
1845 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1846 !jqName ? undefined : // use jqLite
1847 window[jqName]; // use jQuery specified by `ngJq`
1848
1849 // Use jQuery if it exists with proper functionality, otherwise default to us.
1850 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1851 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1852 // versions. It will not work for sure with jQuery <1.7, though.
1853 if (jQuery && jQuery.fn.on) {
1854 jqLite = jQuery;
1855 extend(jQuery.fn, {
1856 scope: JQLitePrototype.scope,
1857 isolateScope: JQLitePrototype.isolateScope,
1858 controller: JQLitePrototype.controller,
1859 injector: JQLitePrototype.injector,
1860 inheritedData: JQLitePrototype.inheritedData
1861 });
1862
1863 // All nodes removed from the DOM via various jQuery APIs like .remove()
1864 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1865 // the $destroy event on all removed nodes.
1866 originalCleanData = jQuery.cleanData;
1867 jQuery.cleanData = function(elems) {
1868 var events;
1869 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1870 events = jQuery._data(elem, "events");
1871 if (events && events.$destroy) {
1872 jQuery(elem).triggerHandler('$destroy');
1873 }
1874 }
1875 originalCleanData(elems);
1876 };
1877 } else {
1878 jqLite = JQLite;
1879 }
1880
1881 angular.element = jqLite;
1882
1883 // Prevent double-proxying.
1884 bindJQueryFired = true;
1885}
1886
1887/**
1888 * throw error if the argument is falsy.
1889 */
1890function assertArg(arg, name, reason) {
1891 if (!arg) {
1892 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1893 }
1894 return arg;
1895}
1896
1897function assertArgFn(arg, name, acceptArrayAnnotation) {
1898 if (acceptArrayAnnotation && isArray(arg)) {
1899 arg = arg[arg.length - 1];
1900 }
1901
1902 assertArg(isFunction(arg), name, 'not a function, got ' +
1903 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1904 return arg;
1905}
1906
1907/**
1908 * throw error if the name given is hasOwnProperty
1909 * @param {String} name the name to test
1910 * @param {String} context the context in which the name is used, such as module or directive
1911 */
1912function assertNotHasOwnProperty(name, context) {
1913 if (name === 'hasOwnProperty') {
1914 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1915 }
1916}
1917
1918/**
1919 * Return the value accessible from the object by path. Any undefined traversals are ignored
1920 * @param {Object} obj starting object
1921 * @param {String} path path to traverse
1922 * @param {boolean} [bindFnToScope=true]
1923 * @returns {Object} value as accessible by path
1924 */
1925//TODO(misko): this function needs to be removed
1926function getter(obj, path, bindFnToScope) {
1927 if (!path) return obj;
1928 var keys = path.split('.');
1929 var key;
1930 var lastInstance = obj;
1931 var len = keys.length;
1932
1933 for (var i = 0; i < len; i++) {
1934 key = keys[i];
1935 if (obj) {
1936 obj = (lastInstance = obj)[key];
1937 }
1938 }
1939 if (!bindFnToScope && isFunction(obj)) {
1940 return bind(lastInstance, obj);
1941 }
1942 return obj;
1943}
1944
1945/**
1946 * Return the DOM siblings between the first and last node in the given array.
1947 * @param {Array} array like object
1948 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1949 */
1950function getBlockNodes(nodes) {
1951 // TODO(perf): update `nodes` instead of creating a new object?
1952 var node = nodes[0];
1953 var endNode = nodes[nodes.length - 1];
1954 var blockNodes;
1955
1956 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1957 if (blockNodes || nodes[i] !== node) {
1958 if (!blockNodes) {
1959 blockNodes = jqLite(slice.call(nodes, 0, i));
1960 }
1961 blockNodes.push(node);
1962 }
1963 }
1964
1965 return blockNodes || nodes;
1966}
1967
1968
1969/**
1970 * Creates a new object without a prototype. This object is useful for lookup without having to
1971 * guard against prototypically inherited properties via hasOwnProperty.
1972 *
1973 * Related micro-benchmarks:
1974 * - http://jsperf.com/object-create2
1975 * - http://jsperf.com/proto-map-lookup/2
1976 * - http://jsperf.com/for-in-vs-object-keys2
1977 *
1978 * @returns {Object}
1979 */
1980function createMap() {
1981 return Object.create(null);
1982}
1983
1984var NODE_TYPE_ELEMENT = 1;
1985var NODE_TYPE_ATTRIBUTE = 2;
1986var NODE_TYPE_TEXT = 3;
1987var NODE_TYPE_COMMENT = 8;
1988var NODE_TYPE_DOCUMENT = 9;
1989var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1990
1991/**
1992 * @ngdoc type
1993 * @name angular.Module
1994 * @module ng
1995 * @description
1996 *
1997 * Interface for configuring angular {@link angular.module modules}.
1998 */
1999
2000function setupModuleLoader(window) {
2001
2002 var $injectorMinErr = minErr('$injector');
2003 var ngMinErr = minErr('ng');
2004
2005 function ensure(obj, name, factory) {
2006 return obj[name] || (obj[name] = factory());
2007 }
2008
2009 var angular = ensure(window, 'angular', Object);
2010
2011 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2012 angular.$$minErr = angular.$$minErr || minErr;
2013
2014 return ensure(angular, 'module', function() {
2015 /** @type {Object.<string, angular.Module>} */
2016 var modules = {};
2017
2018 /**
2019 * @ngdoc function
2020 * @name angular.module
2021 * @module ng
2022 * @description
2023 *
2024 * The `angular.module` is a global place for creating, registering and retrieving Angular
2025 * modules.
2026 * All modules (angular core or 3rd party) that should be available to an application must be
2027 * registered using this mechanism.
2028 *
2029 * Passing one argument retrieves an existing {@link angular.Module},
2030 * whereas passing more than one argument creates a new {@link angular.Module}
2031 *
2032 *
2033 * # Module
2034 *
2035 * A module is a collection of services, directives, controllers, filters, and configuration information.
2036 * `angular.module` is used to configure the {@link auto.$injector $injector}.
2037 *
2038 * ```js
2039 * // Create a new module
2040 * var myModule = angular.module('myModule', []);
2041 *
2042 * // register a new service
2043 * myModule.value('appName', 'MyCoolApp');
2044 *
2045 * // configure existing services inside initialization blocks.
2046 * myModule.config(['$locationProvider', function($locationProvider) {
2047 * // Configure existing providers
2048 * $locationProvider.hashPrefix('!');
2049 * }]);
2050 * ```
2051 *
2052 * Then you can create an injector and load your modules like this:
2053 *
2054 * ```js
2055 * var injector = angular.injector(['ng', 'myModule'])
2056 * ```
2057 *
2058 * However it's more likely that you'll just use
2059 * {@link ng.directive:ngApp ngApp} or
2060 * {@link angular.bootstrap} to simplify this process for you.
2061 *
2062 * @param {!string} name The name of the module to create or retrieve.
2063 * @param {!Array.<string>=} requires If specified then new module is being created. If
2064 * unspecified then the module is being retrieved for further configuration.
2065 * @param {Function=} configFn Optional configuration function for the module. Same as
2066 * {@link angular.Module#config Module#config()}.
2067 * @returns {angular.Module} new module with the {@link angular.Module} api.
2068 */
2069 return function module(name, requires, configFn) {
2070 var assertNotHasOwnProperty = function(name, context) {
2071 if (name === 'hasOwnProperty') {
2072 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2073 }
2074 };
2075
2076 assertNotHasOwnProperty(name, 'module');
2077 if (requires && modules.hasOwnProperty(name)) {
2078 modules[name] = null;
2079 }
2080 return ensure(modules, name, function() {
2081 if (!requires) {
2082 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2083 "the module name or forgot to load it. If registering a module ensure that you " +
2084 "specify the dependencies as the second argument.", name);
2085 }
2086
2087 /** @type {!Array.<Array.<*>>} */
2088 var invokeQueue = [];
2089
2090 /** @type {!Array.<Function>} */
2091 var configBlocks = [];
2092
2093 /** @type {!Array.<Function>} */
2094 var runBlocks = [];
2095
2096 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2097
2098 /** @type {angular.Module} */
2099 var moduleInstance = {
2100 // Private state
2101 _invokeQueue: invokeQueue,
2102 _configBlocks: configBlocks,
2103 _runBlocks: runBlocks,
2104
2105 /**
2106 * @ngdoc property
2107 * @name angular.Module#requires
2108 * @module ng
2109 *
2110 * @description
2111 * Holds the list of modules which the injector will load before the current module is
2112 * loaded.
2113 */
2114 requires: requires,
2115
2116 /**
2117 * @ngdoc property
2118 * @name angular.Module#name
2119 * @module ng
2120 *
2121 * @description
2122 * Name of the module.
2123 */
2124 name: name,
2125
2126
2127 /**
2128 * @ngdoc method
2129 * @name angular.Module#provider
2130 * @module ng
2131 * @param {string} name service name
2132 * @param {Function} providerType Construction function for creating new instance of the
2133 * service.
2134 * @description
2135 * See {@link auto.$provide#provider $provide.provider()}.
2136 */
2137 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2138
2139 /**
2140 * @ngdoc method
2141 * @name angular.Module#factory
2142 * @module ng
2143 * @param {string} name service name
2144 * @param {Function} providerFunction Function for creating new instance of the service.
2145 * @description
2146 * See {@link auto.$provide#factory $provide.factory()}.
2147 */
2148 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2149
2150 /**
2151 * @ngdoc method
2152 * @name angular.Module#service
2153 * @module ng
2154 * @param {string} name service name
2155 * @param {Function} constructor A constructor function that will be instantiated.
2156 * @description
2157 * See {@link auto.$provide#service $provide.service()}.
2158 */
2159 service: invokeLaterAndSetModuleName('$provide', 'service'),
2160
2161 /**
2162 * @ngdoc method
2163 * @name angular.Module#value
2164 * @module ng
2165 * @param {string} name service name
2166 * @param {*} object Service instance object.
2167 * @description
2168 * See {@link auto.$provide#value $provide.value()}.
2169 */
2170 value: invokeLater('$provide', 'value'),
2171
2172 /**
2173 * @ngdoc method
2174 * @name angular.Module#constant
2175 * @module ng
2176 * @param {string} name constant name
2177 * @param {*} object Constant value.
2178 * @description
2179 * Because the constants are fixed, they get applied before other provide methods.
2180 * See {@link auto.$provide#constant $provide.constant()}.
2181 */
2182 constant: invokeLater('$provide', 'constant', 'unshift'),
2183
2184 /**
2185 * @ngdoc method
2186 * @name angular.Module#decorator
2187 * @module ng
2188 * @param {string} name The name of the service to decorate.
2189 * @param {Function} decorFn This function will be invoked when the service needs to be
2190 * instantiated and should return the decorated service instance.
2191 * @description
2192 * See {@link auto.$provide#decorator $provide.decorator()}.
2193 */
2194 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2195
2196 /**
2197 * @ngdoc method
2198 * @name angular.Module#animation
2199 * @module ng
2200 * @param {string} name animation name
2201 * @param {Function} animationFactory Factory function for creating new instance of an
2202 * animation.
2203 * @description
2204 *
2205 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2206 *
2207 *
2208 * Defines an animation hook that can be later used with
2209 * {@link $animate $animate} service and directives that use this service.
2210 *
2211 * ```js
2212 * module.animation('.animation-name', function($inject1, $inject2) {
2213 * return {
2214 * eventName : function(element, done) {
2215 * //code to run the animation
2216 * //once complete, then run done()
2217 * return function cancellationFunction(element) {
2218 * //code to cancel the animation
2219 * }
2220 * }
2221 * }
2222 * })
2223 * ```
2224 *
2225 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2226 * {@link ngAnimate ngAnimate module} for more information.
2227 */
2228 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2229
2230 /**
2231 * @ngdoc method
2232 * @name angular.Module#filter
2233 * @module ng
2234 * @param {string} name Filter name - this must be a valid angular expression identifier
2235 * @param {Function} filterFactory Factory function for creating new instance of filter.
2236 * @description
2237 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2238 *
2239 * <div class="alert alert-warning">
2240 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2241 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2242 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2243 * (`myapp_subsection_filterx`).
2244 * </div>
2245 */
2246 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2247
2248 /**
2249 * @ngdoc method
2250 * @name angular.Module#controller
2251 * @module ng
2252 * @param {string|Object} name Controller name, or an object map of controllers where the
2253 * keys are the names and the values are the constructors.
2254 * @param {Function} constructor Controller constructor function.
2255 * @description
2256 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2257 */
2258 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2259
2260 /**
2261 * @ngdoc method
2262 * @name angular.Module#directive
2263 * @module ng
2264 * @param {string|Object} name Directive name, or an object map of directives where the
2265 * keys are the names and the values are the factories.
2266 * @param {Function} directiveFactory Factory function for creating new instance of
2267 * directives.
2268 * @description
2269 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2270 */
2271 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2272
2273 /**
2274 * @ngdoc method
2275 * @name angular.Module#component
2276 * @module ng
2277 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2278 * @param {Object} options Component definition object (a simplified
2279 * {@link ng.$compile#directive-definition-object directive definition object})
2280 *
2281 * @description
2282 * See {@link ng.$compileProvider#component $compileProvider.component()}.
2283 */
2284 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2285
2286 /**
2287 * @ngdoc method
2288 * @name angular.Module#config
2289 * @module ng
2290 * @param {Function} configFn Execute this function on module load. Useful for service
2291 * configuration.
2292 * @description
2293 * Use this method to register work which needs to be performed on module loading.
2294 * For more about how to configure services, see
2295 * {@link providers#provider-recipe Provider Recipe}.
2296 */
2297 config: config,
2298
2299 /**
2300 * @ngdoc method
2301 * @name angular.Module#run
2302 * @module ng
2303 * @param {Function} initializationFn Execute this function after injector creation.
2304 * Useful for application initialization.
2305 * @description
2306 * Use this method to register work which should be performed when the injector is done
2307 * loading all modules.
2308 */
2309 run: function(block) {
2310 runBlocks.push(block);
2311 return this;
2312 }
2313 };
2314
2315 if (configFn) {
2316 config(configFn);
2317 }
2318
2319 return moduleInstance;
2320
2321 /**
2322 * @param {string} provider
2323 * @param {string} method
2324 * @param {String=} insertMethod
2325 * @returns {angular.Module}
2326 */
2327 function invokeLater(provider, method, insertMethod, queue) {
2328 if (!queue) queue = invokeQueue;
2329 return function() {
2330 queue[insertMethod || 'push']([provider, method, arguments]);
2331 return moduleInstance;
2332 };
2333 }
2334
2335 /**
2336 * @param {string} provider
2337 * @param {string} method
2338 * @returns {angular.Module}
2339 */
2340 function invokeLaterAndSetModuleName(provider, method) {
2341 return function(recipeName, factoryFunction) {
2342 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2343 invokeQueue.push([provider, method, arguments]);
2344 return moduleInstance;
2345 };
2346 }
2347 });
2348 };
2349 });
2350
2351}
2352
2353/* global shallowCopy: true */
2354
2355/**
2356 * Creates a shallow copy of an object, an array or a primitive.
2357 *
2358 * Assumes that there are no proto properties for objects.
2359 */
2360function shallowCopy(src, dst) {
2361 if (isArray(src)) {
2362 dst = dst || [];
2363
2364 for (var i = 0, ii = src.length; i < ii; i++) {
2365 dst[i] = src[i];
2366 }
2367 } else if (isObject(src)) {
2368 dst = dst || {};
2369
2370 for (var key in src) {
2371 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
2372 dst[key] = src[key];
2373 }
2374 }
2375 }
2376
2377 return dst || src;
2378}
2379
2380/* global toDebugString: true */
2381
2382function serializeObject(obj) {
2383 var seen = [];
2384
2385 return JSON.stringify(obj, function(key, val) {
2386 val = toJsonReplacer(key, val);
2387 if (isObject(val)) {
2388
2389 if (seen.indexOf(val) >= 0) return '...';
2390
2391 seen.push(val);
2392 }
2393 return val;
2394 });
2395}
2396
2397function toDebugString(obj) {
2398 if (typeof obj === 'function') {
2399 return obj.toString().replace(/ \{[\s\S]*$/, '');
2400 } else if (isUndefined(obj)) {
2401 return 'undefined';
2402 } else if (typeof obj !== 'string') {
2403 return serializeObject(obj);
2404 }
2405 return obj;
2406}
2407
2408/* global angularModule: true,
2409 version: true,
2410
2411 $CompileProvider,
2412
2413 htmlAnchorDirective,
2414 inputDirective,
2415 inputDirective,
2416 formDirective,
2417 scriptDirective,
2418 selectDirective,
2419 styleDirective,
2420 optionDirective,
2421 ngBindDirective,
2422 ngBindHtmlDirective,
2423 ngBindTemplateDirective,
2424 ngClassDirective,
2425 ngClassEvenDirective,
2426 ngClassOddDirective,
2427 ngCloakDirective,
2428 ngControllerDirective,
2429 ngFormDirective,
2430 ngHideDirective,
2431 ngIfDirective,
2432 ngIncludeDirective,
2433 ngIncludeFillContentDirective,
2434 ngInitDirective,
2435 ngNonBindableDirective,
2436 ngPluralizeDirective,
2437 ngRepeatDirective,
2438 ngShowDirective,
2439 ngStyleDirective,
2440 ngSwitchDirective,
2441 ngSwitchWhenDirective,
2442 ngSwitchDefaultDirective,
2443 ngOptionsDirective,
2444 ngTranscludeDirective,
2445 ngModelDirective,
2446 ngListDirective,
2447 ngChangeDirective,
2448 patternDirective,
2449 patternDirective,
2450 requiredDirective,
2451 requiredDirective,
2452 minlengthDirective,
2453 minlengthDirective,
2454 maxlengthDirective,
2455 maxlengthDirective,
2456 ngValueDirective,
2457 ngModelOptionsDirective,
2458 ngAttributeAliasDirectives,
2459 ngEventDirectives,
2460
2461 $AnchorScrollProvider,
2462 $AnimateProvider,
2463 $CoreAnimateCssProvider,
2464 $$CoreAnimateJsProvider,
2465 $$CoreAnimateQueueProvider,
2466 $$AnimateRunnerFactoryProvider,
2467 $$AnimateAsyncRunFactoryProvider,
2468 $BrowserProvider,
2469 $CacheFactoryProvider,
2470 $ControllerProvider,
2471 $DateProvider,
2472 $DocumentProvider,
2473 $ExceptionHandlerProvider,
2474 $FilterProvider,
2475 $$ForceReflowProvider,
2476 $InterpolateProvider,
2477 $IntervalProvider,
2478 $$HashMapProvider,
2479 $HttpProvider,
2480 $HttpParamSerializerProvider,
2481 $HttpParamSerializerJQLikeProvider,
2482 $HttpBackendProvider,
2483 $xhrFactoryProvider,
2484 $jsonpCallbacksProvider,
2485 $LocationProvider,
2486 $LogProvider,
2487 $ParseProvider,
2488 $RootScopeProvider,
2489 $QProvider,
2490 $$QProvider,
2491 $$SanitizeUriProvider,
2492 $SceProvider,
2493 $SceDelegateProvider,
2494 $SnifferProvider,
2495 $TemplateCacheProvider,
2496 $TemplateRequestProvider,
2497 $$TestabilityProvider,
2498 $TimeoutProvider,
2499 $$RAFProvider,
2500 $WindowProvider,
2501 $$jqLiteProvider,
2502 $$CookieReaderProvider
2503*/
2504
2505
2506/**
2507 * @ngdoc object
2508 * @name angular.version
2509 * @module ng
2510 * @description
2511 * An object that contains information about the current AngularJS version.
2512 *
2513 * This object has the following properties:
2514 *
2515 * - `full` – `{string}` – Full version string, such as "0.9.18".
2516 * - `major` – `{number}` – Major version number, such as "0".
2517 * - `minor` – `{number}` – Minor version number, such as "9".
2518 * - `dot` – `{number}` – Dot version number, such as "18".
2519 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2520 */
2521var version = {
2522 full: '1.5.8', // all of these placeholder strings will be replaced by grunt's
2523 major: 1, // package task
2524 minor: 5,
2525 dot: 8,
2526 codeName: 'arbitrary-fallbacks'
2527};
2528
2529
2530function publishExternalAPI(angular) {
2531 extend(angular, {
2532 'bootstrap': bootstrap,
2533 'copy': copy,
2534 'extend': extend,
2535 'merge': merge,
2536 'equals': equals,
2537 'element': jqLite,
2538 'forEach': forEach,
2539 'injector': createInjector,
2540 'noop': noop,
2541 'bind': bind,
2542 'toJson': toJson,
2543 'fromJson': fromJson,
2544 'identity': identity,
2545 'isUndefined': isUndefined,
2546 'isDefined': isDefined,
2547 'isString': isString,
2548 'isFunction': isFunction,
2549 'isObject': isObject,
2550 'isNumber': isNumber,
2551 'isElement': isElement,
2552 'isArray': isArray,
2553 'version': version,
2554 'isDate': isDate,
2555 'lowercase': lowercase,
2556 'uppercase': uppercase,
2557 'callbacks': {$$counter: 0},
2558 'getTestability': getTestability,
2559 '$$minErr': minErr,
2560 '$$csp': csp,
2561 'reloadWithDebugInfo': reloadWithDebugInfo
2562 });
2563
2564 angularModule = setupModuleLoader(window);
2565
2566 angularModule('ng', ['ngLocale'], ['$provide',
2567 function ngModule($provide) {
2568 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2569 $provide.provider({
2570 $$sanitizeUri: $$SanitizeUriProvider
2571 });
2572 $provide.provider('$compile', $CompileProvider).
2573 directive({
2574 a: htmlAnchorDirective,
2575 input: inputDirective,
2576 textarea: inputDirective,
2577 form: formDirective,
2578 script: scriptDirective,
2579 select: selectDirective,
2580 style: styleDirective,
2581 option: optionDirective,
2582 ngBind: ngBindDirective,
2583 ngBindHtml: ngBindHtmlDirective,
2584 ngBindTemplate: ngBindTemplateDirective,
2585 ngClass: ngClassDirective,
2586 ngClassEven: ngClassEvenDirective,
2587 ngClassOdd: ngClassOddDirective,
2588 ngCloak: ngCloakDirective,
2589 ngController: ngControllerDirective,
2590 ngForm: ngFormDirective,
2591 ngHide: ngHideDirective,
2592 ngIf: ngIfDirective,
2593 ngInclude: ngIncludeDirective,
2594 ngInit: ngInitDirective,
2595 ngNonBindable: ngNonBindableDirective,
2596 ngPluralize: ngPluralizeDirective,
2597 ngRepeat: ngRepeatDirective,
2598 ngShow: ngShowDirective,
2599 ngStyle: ngStyleDirective,
2600 ngSwitch: ngSwitchDirective,
2601 ngSwitchWhen: ngSwitchWhenDirective,
2602 ngSwitchDefault: ngSwitchDefaultDirective,
2603 ngOptions: ngOptionsDirective,
2604 ngTransclude: ngTranscludeDirective,
2605 ngModel: ngModelDirective,
2606 ngList: ngListDirective,
2607 ngChange: ngChangeDirective,
2608 pattern: patternDirective,
2609 ngPattern: patternDirective,
2610 required: requiredDirective,
2611 ngRequired: requiredDirective,
2612 minlength: minlengthDirective,
2613 ngMinlength: minlengthDirective,
2614 maxlength: maxlengthDirective,
2615 ngMaxlength: maxlengthDirective,
2616 ngValue: ngValueDirective,
2617 ngModelOptions: ngModelOptionsDirective
2618 }).
2619 directive({
2620 ngInclude: ngIncludeFillContentDirective
2621 }).
2622 directive(ngAttributeAliasDirectives).
2623 directive(ngEventDirectives);
2624 $provide.provider({
2625 $anchorScroll: $AnchorScrollProvider,
2626 $animate: $AnimateProvider,
2627 $animateCss: $CoreAnimateCssProvider,
2628 $$animateJs: $$CoreAnimateJsProvider,
2629 $$animateQueue: $$CoreAnimateQueueProvider,
2630 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2631 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2632 $browser: $BrowserProvider,
2633 $cacheFactory: $CacheFactoryProvider,
2634 $controller: $ControllerProvider,
2635 $document: $DocumentProvider,
2636 $exceptionHandler: $ExceptionHandlerProvider,
2637 $filter: $FilterProvider,
2638 $$forceReflow: $$ForceReflowProvider,
2639 $interpolate: $InterpolateProvider,
2640 $interval: $IntervalProvider,
2641 $http: $HttpProvider,
2642 $httpParamSerializer: $HttpParamSerializerProvider,
2643 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2644 $httpBackend: $HttpBackendProvider,
2645 $xhrFactory: $xhrFactoryProvider,
2646 $jsonpCallbacks: $jsonpCallbacksProvider,
2647 $location: $LocationProvider,
2648 $log: $LogProvider,
2649 $parse: $ParseProvider,
2650 $rootScope: $RootScopeProvider,
2651 $q: $QProvider,
2652 $$q: $$QProvider,
2653 $sce: $SceProvider,
2654 $sceDelegate: $SceDelegateProvider,
2655 $sniffer: $SnifferProvider,
2656 $templateCache: $TemplateCacheProvider,
2657 $templateRequest: $TemplateRequestProvider,
2658 $$testability: $$TestabilityProvider,
2659 $timeout: $TimeoutProvider,
2660 $window: $WindowProvider,
2661 $$rAF: $$RAFProvider,
2662 $$jqLite: $$jqLiteProvider,
2663 $$HashMap: $$HashMapProvider,
2664 $$cookieReader: $$CookieReaderProvider
2665 });
2666 }
2667 ]);
2668}
2669
2670/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2671 * Any commits to this file should be reviewed with security in mind. *
2672 * Changes to this file can potentially create security vulnerabilities. *
2673 * An approval from 2 Core members with history of modifying *
2674 * this file is required. *
2675 * *
2676 * Does the change somehow allow for arbitrary javascript to be executed? *
2677 * Or allows for someone to change the prototype of built-in objects? *
2678 * Or gives undesired access to variables likes document or window? *
2679 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2680
2681/* global JQLitePrototype: true,
2682 addEventListenerFn: true,
2683 removeEventListenerFn: true,
2684 BOOLEAN_ATTR: true,
2685 ALIASED_ATTR: true,
2686*/
2687
2688//////////////////////////////////
2689//JQLite
2690//////////////////////////////////
2691
2692/**
2693 * @ngdoc function
2694 * @name angular.element
2695 * @module ng
2696 * @kind function
2697 *
2698 * @description
2699 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2700 *
2701 * If jQuery is available, `angular.element` is an alias for the
2702 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2703 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2704 *
2705 * jqLite is a tiny, API-compatible subset of jQuery that allows
2706 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2707 * commonly needed functionality with the goal of having a very small footprint.
2708 *
2709 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2710 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2711 * specific version of jQuery if multiple versions exist on the page.
2712 *
2713 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2714 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2715 *
2716 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2717 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2718 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2719 *
2720 * ## Angular's jqLite
2721 * jqLite provides only the following jQuery methods:
2722 *
2723 * - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
2724 * - [`after()`](http://api.jquery.com/after/)
2725 * - [`append()`](http://api.jquery.com/append/)
2726 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2727 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2728 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2729 * - [`clone()`](http://api.jquery.com/clone/)
2730 * - [`contents()`](http://api.jquery.com/contents/)
2731 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2732 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2733 * - [`data()`](http://api.jquery.com/data/)
2734 * - [`detach()`](http://api.jquery.com/detach/)
2735 * - [`empty()`](http://api.jquery.com/empty/)
2736 * - [`eq()`](http://api.jquery.com/eq/)
2737 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2738 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2739 * - [`html()`](http://api.jquery.com/html/)
2740 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2741 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2742 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2743 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2744 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2745 * - [`prepend()`](http://api.jquery.com/prepend/)
2746 * - [`prop()`](http://api.jquery.com/prop/)
2747 * - [`ready()`](http://api.jquery.com/ready/)
2748 * - [`remove()`](http://api.jquery.com/remove/)
2749 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2750 * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
2751 * - [`removeData()`](http://api.jquery.com/removeData/)
2752 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2753 * - [`text()`](http://api.jquery.com/text/)
2754 * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument
2755 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers
2756 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2757 * - [`val()`](http://api.jquery.com/val/)
2758 * - [`wrap()`](http://api.jquery.com/wrap/)
2759 *
2760 * ## jQuery/jqLite Extras
2761 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2762 *
2763 * ### Events
2764 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2765 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2766 * element before it is removed.
2767 *
2768 * ### Methods
2769 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2770 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2771 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2772 * `'ngModel'`).
2773 * - `injector()` - retrieves the injector of the current element or its parent.
2774 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2775 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2776 * be enabled.
2777 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2778 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2779 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2780 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2781 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2782 * parent element is reached.
2783 *
2784 * @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
2785 * https://github.com/angular/angular.js/issues/14251 for more information.
2786 *
2787 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2788 * @returns {Object} jQuery object.
2789 */
2790
2791JQLite.expando = 'ng339';
2792
2793var jqCache = JQLite.cache = {},
2794 jqId = 1,
2795 addEventListenerFn = function(element, type, fn) {
2796 element.addEventListener(type, fn, false);
2797 },
2798 removeEventListenerFn = function(element, type, fn) {
2799 element.removeEventListener(type, fn, false);
2800 };
2801
2802/*
2803 * !!! This is an undocumented "private" function !!!
2804 */
2805JQLite._data = function(node) {
2806 //jQuery always returns an object on cache miss
2807 return this.cache[node[this.expando]] || {};
2808};
2809
2810function jqNextId() { return ++jqId; }
2811
2812
2813var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2814var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2815var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2816var jqLiteMinErr = minErr('jqLite');
2817
2818/**
2819 * Converts snake_case to camelCase.
2820 * Also there is special case for Moz prefix starting with upper case letter.
2821 * @param name Name to normalize
2822 */
2823function camelCase(name) {
2824 return name.
2825 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2826 return offset ? letter.toUpperCase() : letter;
2827 }).
2828 replace(MOZ_HACK_REGEXP, 'Moz$1');
2829}
2830
2831var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2832var HTML_REGEXP = /<|&#?\w+;/;
2833var TAG_NAME_REGEXP = /<([\w:-]+)/;
2834var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2835
2836var wrapMap = {
2837 'option': [1, '<select multiple="multiple">', '</select>'],
2838
2839 'thead': [1, '<table>', '</table>'],
2840 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2841 'tr': [2, '<table><tbody>', '</tbody></table>'],
2842 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2843 '_default': [0, "", ""]
2844};
2845
2846wrapMap.optgroup = wrapMap.option;
2847wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2848wrapMap.th = wrapMap.td;
2849
2850
2851function jqLiteIsTextNode(html) {
2852 return !HTML_REGEXP.test(html);
2853}
2854
2855function jqLiteAcceptsData(node) {
2856 // The window object can accept data but has no nodeType
2857 // Otherwise we are only interested in elements (1) and documents (9)
2858 var nodeType = node.nodeType;
2859 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2860}
2861
2862function jqLiteHasData(node) {
2863 for (var key in jqCache[node.ng339]) {
2864 return true;
2865 }
2866 return false;
2867}
2868
2869function jqLiteCleanData(nodes) {
2870 for (var i = 0, ii = nodes.length; i < ii; i++) {
2871 jqLiteRemoveData(nodes[i]);
2872 }
2873}
2874
2875function jqLiteBuildFragment(html, context) {
2876 var tmp, tag, wrap,
2877 fragment = context.createDocumentFragment(),
2878 nodes = [], i;
2879
2880 if (jqLiteIsTextNode(html)) {
2881 // Convert non-html into a text node
2882 nodes.push(context.createTextNode(html));
2883 } else {
2884 // Convert html into DOM nodes
2885 tmp = fragment.appendChild(context.createElement("div"));
2886 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2887 wrap = wrapMap[tag] || wrapMap._default;
2888 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2889
2890 // Descend through wrappers to the right content
2891 i = wrap[0];
2892 while (i--) {
2893 tmp = tmp.lastChild;
2894 }
2895
2896 nodes = concat(nodes, tmp.childNodes);
2897
2898 tmp = fragment.firstChild;
2899 tmp.textContent = "";
2900 }
2901
2902 // Remove wrapper from fragment
2903 fragment.textContent = "";
2904 fragment.innerHTML = ""; // Clear inner HTML
2905 forEach(nodes, function(node) {
2906 fragment.appendChild(node);
2907 });
2908
2909 return fragment;
2910}
2911
2912function jqLiteParseHTML(html, context) {
2913 context = context || window.document;
2914 var parsed;
2915
2916 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2917 return [context.createElement(parsed[1])];
2918 }
2919
2920 if ((parsed = jqLiteBuildFragment(html, context))) {
2921 return parsed.childNodes;
2922 }
2923
2924 return [];
2925}
2926
2927function jqLiteWrapNode(node, wrapper) {
2928 var parent = node.parentNode;
2929
2930 if (parent) {
2931 parent.replaceChild(wrapper, node);
2932 }
2933
2934 wrapper.appendChild(node);
2935}
2936
2937
2938// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2939var jqLiteContains = window.Node.prototype.contains || function(arg) {
2940 // jshint bitwise: false
2941 return !!(this.compareDocumentPosition(arg) & 16);
2942 // jshint bitwise: true
2943};
2944
2945/////////////////////////////////////////////
2946function JQLite(element) {
2947 if (element instanceof JQLite) {
2948 return element;
2949 }
2950
2951 var argIsString;
2952
2953 if (isString(element)) {
2954 element = trim(element);
2955 argIsString = true;
2956 }
2957 if (!(this instanceof JQLite)) {
2958 if (argIsString && element.charAt(0) != '<') {
2959 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2960 }
2961 return new JQLite(element);
2962 }
2963
2964 if (argIsString) {
2965 jqLiteAddNodes(this, jqLiteParseHTML(element));
2966 } else {
2967 jqLiteAddNodes(this, element);
2968 }
2969}
2970
2971function jqLiteClone(element) {
2972 return element.cloneNode(true);
2973}
2974
2975function jqLiteDealoc(element, onlyDescendants) {
2976 if (!onlyDescendants) jqLiteRemoveData(element);
2977
2978 if (element.querySelectorAll) {
2979 var descendants = element.querySelectorAll('*');
2980 for (var i = 0, l = descendants.length; i < l; i++) {
2981 jqLiteRemoveData(descendants[i]);
2982 }
2983 }
2984}
2985
2986function jqLiteOff(element, type, fn, unsupported) {
2987 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2988
2989 var expandoStore = jqLiteExpandoStore(element);
2990 var events = expandoStore && expandoStore.events;
2991 var handle = expandoStore && expandoStore.handle;
2992
2993 if (!handle) return; //no listeners registered
2994
2995 if (!type) {
2996 for (type in events) {
2997 if (type !== '$destroy') {
2998 removeEventListenerFn(element, type, handle);
2999 }
3000 delete events[type];
3001 }
3002 } else {
3003
3004 var removeHandler = function(type) {
3005 var listenerFns = events[type];
3006 if (isDefined(fn)) {
3007 arrayRemove(listenerFns || [], fn);
3008 }
3009 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
3010 removeEventListenerFn(element, type, handle);
3011 delete events[type];
3012 }
3013 };
3014
3015 forEach(type.split(' '), function(type) {
3016 removeHandler(type);
3017 if (MOUSE_EVENT_MAP[type]) {
3018 removeHandler(MOUSE_EVENT_MAP[type]);
3019 }
3020 });
3021 }
3022}
3023
3024function jqLiteRemoveData(element, name) {
3025 var expandoId = element.ng339;
3026 var expandoStore = expandoId && jqCache[expandoId];
3027
3028 if (expandoStore) {
3029 if (name) {
3030 delete expandoStore.data[name];
3031 return;
3032 }
3033
3034 if (expandoStore.handle) {
3035 if (expandoStore.events.$destroy) {
3036 expandoStore.handle({}, '$destroy');
3037 }
3038 jqLiteOff(element);
3039 }
3040 delete jqCache[expandoId];
3041 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
3042 }
3043}
3044
3045
3046function jqLiteExpandoStore(element, createIfNecessary) {
3047 var expandoId = element.ng339,
3048 expandoStore = expandoId && jqCache[expandoId];
3049
3050 if (createIfNecessary && !expandoStore) {
3051 element.ng339 = expandoId = jqNextId();
3052 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
3053 }
3054
3055 return expandoStore;
3056}
3057
3058
3059function jqLiteData(element, key, value) {
3060 if (jqLiteAcceptsData(element)) {
3061
3062 var isSimpleSetter = isDefined(value);
3063 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
3064 var massGetter = !key;
3065 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
3066 var data = expandoStore && expandoStore.data;
3067
3068 if (isSimpleSetter) { // data('key', value)
3069 data[key] = value;
3070 } else {
3071 if (massGetter) { // data()
3072 return data;
3073 } else {
3074 if (isSimpleGetter) { // data('key')
3075 // don't force creation of expandoStore if it doesn't exist yet
3076 return data && data[key];
3077 } else { // mass-setter: data({key1: val1, key2: val2})
3078 extend(data, key);
3079 }
3080 }
3081 }
3082 }
3083}
3084
3085function jqLiteHasClass(element, selector) {
3086 if (!element.getAttribute) return false;
3087 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3088 indexOf(" " + selector + " ") > -1);
3089}
3090
3091function jqLiteRemoveClass(element, cssClasses) {
3092 if (cssClasses && element.setAttribute) {
3093 forEach(cssClasses.split(' '), function(cssClass) {
3094 element.setAttribute('class', trim(
3095 (" " + (element.getAttribute('class') || '') + " ")
3096 .replace(/[\n\t]/g, " ")
3097 .replace(" " + trim(cssClass) + " ", " "))
3098 );
3099 });
3100 }
3101}
3102
3103function jqLiteAddClass(element, cssClasses) {
3104 if (cssClasses && element.setAttribute) {
3105 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3106 .replace(/[\n\t]/g, " ");
3107
3108 forEach(cssClasses.split(' '), function(cssClass) {
3109 cssClass = trim(cssClass);
3110 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3111 existingClasses += cssClass + ' ';
3112 }
3113 });
3114
3115 element.setAttribute('class', trim(existingClasses));
3116 }
3117}
3118
3119
3120function jqLiteAddNodes(root, elements) {
3121 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3122
3123 if (elements) {
3124
3125 // if a Node (the most common case)
3126 if (elements.nodeType) {
3127 root[root.length++] = elements;
3128 } else {
3129 var length = elements.length;
3130
3131 // if an Array or NodeList and not a Window
3132 if (typeof length === 'number' && elements.window !== elements) {
3133 if (length) {
3134 for (var i = 0; i < length; i++) {
3135 root[root.length++] = elements[i];
3136 }
3137 }
3138 } else {
3139 root[root.length++] = elements;
3140 }
3141 }
3142 }
3143}
3144
3145
3146function jqLiteController(element, name) {
3147 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3148}
3149
3150function jqLiteInheritedData(element, name, value) {
3151 // if element is the document object work with the html element instead
3152 // this makes $(document).scope() possible
3153 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3154 element = element.documentElement;
3155 }
3156 var names = isArray(name) ? name : [name];
3157
3158 while (element) {
3159 for (var i = 0, ii = names.length; i < ii; i++) {
3160 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3161 }
3162
3163 // If dealing with a document fragment node with a host element, and no parent, use the host
3164 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3165 // to lookup parent controllers.
3166 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3167 }
3168}
3169
3170function jqLiteEmpty(element) {
3171 jqLiteDealoc(element, true);
3172 while (element.firstChild) {
3173 element.removeChild(element.firstChild);
3174 }
3175}
3176
3177function jqLiteRemove(element, keepData) {
3178 if (!keepData) jqLiteDealoc(element);
3179 var parent = element.parentNode;
3180 if (parent) parent.removeChild(element);
3181}
3182
3183
3184function jqLiteDocumentLoaded(action, win) {
3185 win = win || window;
3186 if (win.document.readyState === 'complete') {
3187 // Force the action to be run async for consistent behavior
3188 // from the action's point of view
3189 // i.e. it will definitely not be in a $apply
3190 win.setTimeout(action);
3191 } else {
3192 // No need to unbind this handler as load is only ever called once
3193 jqLite(win).on('load', action);
3194 }
3195}
3196
3197//////////////////////////////////////////
3198// Functions which are declared directly.
3199//////////////////////////////////////////
3200var JQLitePrototype = JQLite.prototype = {
3201 ready: function(fn) {
3202 var fired = false;
3203
3204 function trigger() {
3205 if (fired) return;
3206 fired = true;
3207 fn();
3208 }
3209
3210 // check if document is already loaded
3211 if (window.document.readyState === 'complete') {
3212 window.setTimeout(trigger);
3213 } else {
3214 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3215 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3216 // jshint -W064
3217 JQLite(window).on('load', trigger); // fallback to window.onload for others
3218 // jshint +W064
3219 }
3220 },
3221 toString: function() {
3222 var value = [];
3223 forEach(this, function(e) { value.push('' + e);});
3224 return '[' + value.join(', ') + ']';
3225 },
3226
3227 eq: function(index) {
3228 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3229 },
3230
3231 length: 0,
3232 push: push,
3233 sort: [].sort,
3234 splice: [].splice
3235};
3236
3237//////////////////////////////////////////
3238// Functions iterating getter/setters.
3239// these functions return self on setter and
3240// value on get.
3241//////////////////////////////////////////
3242var BOOLEAN_ATTR = {};
3243forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3244 BOOLEAN_ATTR[lowercase(value)] = value;
3245});
3246var BOOLEAN_ELEMENTS = {};
3247forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3248 BOOLEAN_ELEMENTS[value] = true;
3249});
3250var ALIASED_ATTR = {
3251 'ngMinlength': 'minlength',
3252 'ngMaxlength': 'maxlength',
3253 'ngMin': 'min',
3254 'ngMax': 'max',
3255 'ngPattern': 'pattern'
3256};
3257
3258function getBooleanAttrName(element, name) {
3259 // check dom last since we will most likely fail on name
3260 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3261
3262 // booleanAttr is here twice to minimize DOM access
3263 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3264}
3265
3266function getAliasedAttrName(name) {
3267 return ALIASED_ATTR[name];
3268}
3269
3270forEach({
3271 data: jqLiteData,
3272 removeData: jqLiteRemoveData,
3273 hasData: jqLiteHasData,
3274 cleanData: jqLiteCleanData
3275}, function(fn, name) {
3276 JQLite[name] = fn;
3277});
3278
3279forEach({
3280 data: jqLiteData,
3281 inheritedData: jqLiteInheritedData,
3282
3283 scope: function(element) {
3284 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3285 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3286 },
3287
3288 isolateScope: function(element) {
3289 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3290 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3291 },
3292
3293 controller: jqLiteController,
3294
3295 injector: function(element) {
3296 return jqLiteInheritedData(element, '$injector');
3297 },
3298
3299 removeAttr: function(element, name) {
3300 element.removeAttribute(name);
3301 },
3302
3303 hasClass: jqLiteHasClass,
3304
3305 css: function(element, name, value) {
3306 name = camelCase(name);
3307
3308 if (isDefined(value)) {
3309 element.style[name] = value;
3310 } else {
3311 return element.style[name];
3312 }
3313 },
3314
3315 attr: function(element, name, value) {
3316 var nodeType = element.nodeType;
3317 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3318 return;
3319 }
3320 var lowercasedName = lowercase(name);
3321 if (BOOLEAN_ATTR[lowercasedName]) {
3322 if (isDefined(value)) {
3323 if (!!value) {
3324 element[name] = true;
3325 element.setAttribute(name, lowercasedName);
3326 } else {
3327 element[name] = false;
3328 element.removeAttribute(lowercasedName);
3329 }
3330 } else {
3331 return (element[name] ||
3332 (element.attributes.getNamedItem(name) || noop).specified)
3333 ? lowercasedName
3334 : undefined;
3335 }
3336 } else if (isDefined(value)) {
3337 element.setAttribute(name, value);
3338 } else if (element.getAttribute) {
3339 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3340 // some elements (e.g. Document) don't have get attribute, so return undefined
3341 var ret = element.getAttribute(name, 2);
3342 // normalize non-existing attributes to undefined (as jQuery)
3343 return ret === null ? undefined : ret;
3344 }
3345 },
3346
3347 prop: function(element, name, value) {
3348 if (isDefined(value)) {
3349 element[name] = value;
3350 } else {
3351 return element[name];
3352 }
3353 },
3354
3355 text: (function() {
3356 getText.$dv = '';
3357 return getText;
3358
3359 function getText(element, value) {
3360 if (isUndefined(value)) {
3361 var nodeType = element.nodeType;
3362 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3363 }
3364 element.textContent = value;
3365 }
3366 })(),
3367
3368 val: function(element, value) {
3369 if (isUndefined(value)) {
3370 if (element.multiple && nodeName_(element) === 'select') {
3371 var result = [];
3372 forEach(element.options, function(option) {
3373 if (option.selected) {
3374 result.push(option.value || option.text);
3375 }
3376 });
3377 return result.length === 0 ? null : result;
3378 }
3379 return element.value;
3380 }
3381 element.value = value;
3382 },
3383
3384 html: function(element, value) {
3385 if (isUndefined(value)) {
3386 return element.innerHTML;
3387 }
3388 jqLiteDealoc(element, true);
3389 element.innerHTML = value;
3390 },
3391
3392 empty: jqLiteEmpty
3393}, function(fn, name) {
3394 /**
3395 * Properties: writes return selection, reads return first value
3396 */
3397 JQLite.prototype[name] = function(arg1, arg2) {
3398 var i, key;
3399 var nodeCount = this.length;
3400
3401 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3402 // in a way that survives minification.
3403 // jqLiteEmpty takes no arguments but is a setter.
3404 if (fn !== jqLiteEmpty &&
3405 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3406 if (isObject(arg1)) {
3407
3408 // we are a write, but the object properties are the key/values
3409 for (i = 0; i < nodeCount; i++) {
3410 if (fn === jqLiteData) {
3411 // data() takes the whole object in jQuery
3412 fn(this[i], arg1);
3413 } else {
3414 for (key in arg1) {
3415 fn(this[i], key, arg1[key]);
3416 }
3417 }
3418 }
3419 // return self for chaining
3420 return this;
3421 } else {
3422 // we are a read, so read the first child.
3423 // TODO: do we still need this?
3424 var value = fn.$dv;
3425 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3426 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3427 for (var j = 0; j < jj; j++) {
3428 var nodeValue = fn(this[j], arg1, arg2);
3429 value = value ? value + nodeValue : nodeValue;
3430 }
3431 return value;
3432 }
3433 } else {
3434 // we are a write, so apply to all children
3435 for (i = 0; i < nodeCount; i++) {
3436 fn(this[i], arg1, arg2);
3437 }
3438 // return self for chaining
3439 return this;
3440 }
3441 };
3442});
3443
3444function createEventHandler(element, events) {
3445 var eventHandler = function(event, type) {
3446 // jQuery specific api
3447 event.isDefaultPrevented = function() {
3448 return event.defaultPrevented;
3449 };
3450
3451 var eventFns = events[type || event.type];
3452 var eventFnsLength = eventFns ? eventFns.length : 0;
3453
3454 if (!eventFnsLength) return;
3455
3456 if (isUndefined(event.immediatePropagationStopped)) {
3457 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3458 event.stopImmediatePropagation = function() {
3459 event.immediatePropagationStopped = true;
3460
3461 if (event.stopPropagation) {
3462 event.stopPropagation();
3463 }
3464
3465 if (originalStopImmediatePropagation) {
3466 originalStopImmediatePropagation.call(event);
3467 }
3468 };
3469 }
3470
3471 event.isImmediatePropagationStopped = function() {
3472 return event.immediatePropagationStopped === true;
3473 };
3474
3475 // Some events have special handlers that wrap the real handler
3476 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3477
3478 // Copy event handlers in case event handlers array is modified during execution.
3479 if ((eventFnsLength > 1)) {
3480 eventFns = shallowCopy(eventFns);
3481 }
3482
3483 for (var i = 0; i < eventFnsLength; i++) {
3484 if (!event.isImmediatePropagationStopped()) {
3485 handlerWrapper(element, event, eventFns[i]);
3486 }
3487 }
3488 };
3489
3490 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3491 // events on `element`
3492 eventHandler.elem = element;
3493 return eventHandler;
3494}
3495
3496function defaultHandlerWrapper(element, event, handler) {
3497 handler.call(element, event);
3498}
3499
3500function specialMouseHandlerWrapper(target, event, handler) {
3501 // Refer to jQuery's implementation of mouseenter & mouseleave
3502 // Read about mouseenter and mouseleave:
3503 // http://www.quirksmode.org/js/events_mouse.html#link8
3504 var related = event.relatedTarget;
3505 // For mousenter/leave call the handler if related is outside the target.
3506 // NB: No relatedTarget if the mouse left/entered the browser window
3507 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3508 handler.call(target, event);
3509 }
3510}
3511
3512//////////////////////////////////////////
3513// Functions iterating traversal.
3514// These functions chain results into a single
3515// selector.
3516//////////////////////////////////////////
3517forEach({
3518 removeData: jqLiteRemoveData,
3519
3520 on: function jqLiteOn(element, type, fn, unsupported) {
3521 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3522
3523 // Do not add event handlers to non-elements because they will not be cleaned up.
3524 if (!jqLiteAcceptsData(element)) {
3525 return;
3526 }
3527
3528 var expandoStore = jqLiteExpandoStore(element, true);
3529 var events = expandoStore.events;
3530 var handle = expandoStore.handle;
3531
3532 if (!handle) {
3533 handle = expandoStore.handle = createEventHandler(element, events);
3534 }
3535
3536 // http://jsperf.com/string-indexof-vs-split
3537 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3538 var i = types.length;
3539
3540 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3541 var eventFns = events[type];
3542
3543 if (!eventFns) {
3544 eventFns = events[type] = [];
3545 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3546 if (type !== '$destroy' && !noEventListener) {
3547 addEventListenerFn(element, type, handle);
3548 }
3549 }
3550
3551 eventFns.push(fn);
3552 };
3553
3554 while (i--) {
3555 type = types[i];
3556 if (MOUSE_EVENT_MAP[type]) {
3557 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3558 addHandler(type, undefined, true);
3559 } else {
3560 addHandler(type);
3561 }
3562 }
3563 },
3564
3565 off: jqLiteOff,
3566
3567 one: function(element, type, fn) {
3568 element = jqLite(element);
3569
3570 //add the listener twice so that when it is called
3571 //you can remove the original function and still be
3572 //able to call element.off(ev, fn) normally
3573 element.on(type, function onFn() {
3574 element.off(type, fn);
3575 element.off(type, onFn);
3576 });
3577 element.on(type, fn);
3578 },
3579
3580 replaceWith: function(element, replaceNode) {
3581 var index, parent = element.parentNode;
3582 jqLiteDealoc(element);
3583 forEach(new JQLite(replaceNode), function(node) {
3584 if (index) {
3585 parent.insertBefore(node, index.nextSibling);
3586 } else {
3587 parent.replaceChild(node, element);
3588 }
3589 index = node;
3590 });
3591 },
3592
3593 children: function(element) {
3594 var children = [];
3595 forEach(element.childNodes, function(element) {
3596 if (element.nodeType === NODE_TYPE_ELEMENT) {
3597 children.push(element);
3598 }
3599 });
3600 return children;
3601 },
3602
3603 contents: function(element) {
3604 return element.contentDocument || element.childNodes || [];
3605 },
3606
3607 append: function(element, node) {
3608 var nodeType = element.nodeType;
3609 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3610
3611 node = new JQLite(node);
3612
3613 for (var i = 0, ii = node.length; i < ii; i++) {
3614 var child = node[i];
3615 element.appendChild(child);
3616 }
3617 },
3618
3619 prepend: function(element, node) {
3620 if (element.nodeType === NODE_TYPE_ELEMENT) {
3621 var index = element.firstChild;
3622 forEach(new JQLite(node), function(child) {
3623 element.insertBefore(child, index);
3624 });
3625 }
3626 },
3627
3628 wrap: function(element, wrapNode) {
3629 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3630 },
3631
3632 remove: jqLiteRemove,
3633
3634 detach: function(element) {
3635 jqLiteRemove(element, true);
3636 },
3637
3638 after: function(element, newElement) {
3639 var index = element, parent = element.parentNode;
3640 newElement = new JQLite(newElement);
3641
3642 for (var i = 0, ii = newElement.length; i < ii; i++) {
3643 var node = newElement[i];
3644 parent.insertBefore(node, index.nextSibling);
3645 index = node;
3646 }
3647 },
3648
3649 addClass: jqLiteAddClass,
3650 removeClass: jqLiteRemoveClass,
3651
3652 toggleClass: function(element, selector, condition) {
3653 if (selector) {
3654 forEach(selector.split(' '), function(className) {
3655 var classCondition = condition;
3656 if (isUndefined(classCondition)) {
3657 classCondition = !jqLiteHasClass(element, className);
3658 }
3659 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3660 });
3661 }
3662 },
3663
3664 parent: function(element) {
3665 var parent = element.parentNode;
3666 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3667 },
3668
3669 next: function(element) {
3670 return element.nextElementSibling;
3671 },
3672
3673 find: function(element, selector) {
3674 if (element.getElementsByTagName) {
3675 return element.getElementsByTagName(selector);
3676 } else {
3677 return [];
3678 }
3679 },
3680
3681 clone: jqLiteClone,
3682
3683 triggerHandler: function(element, event, extraParameters) {
3684
3685 var dummyEvent, eventFnsCopy, handlerArgs;
3686 var eventName = event.type || event;
3687 var expandoStore = jqLiteExpandoStore(element);
3688 var events = expandoStore && expandoStore.events;
3689 var eventFns = events && events[eventName];
3690
3691 if (eventFns) {
3692 // Create a dummy event to pass to the handlers
3693 dummyEvent = {
3694 preventDefault: function() { this.defaultPrevented = true; },
3695 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3696 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3697 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3698 stopPropagation: noop,
3699 type: eventName,
3700 target: element
3701 };
3702
3703 // If a custom event was provided then extend our dummy event with it
3704 if (event.type) {
3705 dummyEvent = extend(dummyEvent, event);
3706 }
3707
3708 // Copy event handlers in case event handlers array is modified during execution.
3709 eventFnsCopy = shallowCopy(eventFns);
3710 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3711
3712 forEach(eventFnsCopy, function(fn) {
3713 if (!dummyEvent.isImmediatePropagationStopped()) {
3714 fn.apply(element, handlerArgs);
3715 }
3716 });
3717 }
3718 }
3719}, function(fn, name) {
3720 /**
3721 * chaining functions
3722 */
3723 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3724 var value;
3725
3726 for (var i = 0, ii = this.length; i < ii; i++) {
3727 if (isUndefined(value)) {
3728 value = fn(this[i], arg1, arg2, arg3);
3729 if (isDefined(value)) {
3730 // any function which returns a value needs to be wrapped
3731 value = jqLite(value);
3732 }
3733 } else {
3734 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3735 }
3736 }
3737 return isDefined(value) ? value : this;
3738 };
3739
3740 // bind legacy bind/unbind to on/off
3741 JQLite.prototype.bind = JQLite.prototype.on;
3742 JQLite.prototype.unbind = JQLite.prototype.off;
3743});
3744
3745
3746// Provider for private $$jqLite service
3747function $$jqLiteProvider() {
3748 this.$get = function $$jqLite() {
3749 return extend(JQLite, {
3750 hasClass: function(node, classes) {
3751 if (node.attr) node = node[0];
3752 return jqLiteHasClass(node, classes);
3753 },
3754 addClass: function(node, classes) {
3755 if (node.attr) node = node[0];
3756 return jqLiteAddClass(node, classes);
3757 },
3758 removeClass: function(node, classes) {
3759 if (node.attr) node = node[0];
3760 return jqLiteRemoveClass(node, classes);
3761 }
3762 });
3763 };
3764}
3765
3766/**
3767 * Computes a hash of an 'obj'.
3768 * Hash of a:
3769 * string is string
3770 * number is number as string
3771 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3772 * that is also assigned to the $$hashKey property of the object.
3773 *
3774 * @param obj
3775 * @returns {string} hash string such that the same input will have the same hash string.
3776 * The resulting string key is in 'type:hashKey' format.
3777 */
3778function hashKey(obj, nextUidFn) {
3779 var key = obj && obj.$$hashKey;
3780
3781 if (key) {
3782 if (typeof key === 'function') {
3783 key = obj.$$hashKey();
3784 }
3785 return key;
3786 }
3787
3788 var objType = typeof obj;
3789 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3790 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3791 } else {
3792 key = objType + ':' + obj;
3793 }
3794
3795 return key;
3796}
3797
3798/**
3799 * HashMap which can use objects as keys
3800 */
3801function HashMap(array, isolatedUid) {
3802 if (isolatedUid) {
3803 var uid = 0;
3804 this.nextUid = function() {
3805 return ++uid;
3806 };
3807 }
3808 forEach(array, this.put, this);
3809}
3810HashMap.prototype = {
3811 /**
3812 * Store key value pair
3813 * @param key key to store can be any type
3814 * @param value value to store can be any type
3815 */
3816 put: function(key, value) {
3817 this[hashKey(key, this.nextUid)] = value;
3818 },
3819
3820 /**
3821 * @param key
3822 * @returns {Object} the value for the key
3823 */
3824 get: function(key) {
3825 return this[hashKey(key, this.nextUid)];
3826 },
3827
3828 /**
3829 * Remove the key/value pair
3830 * @param key
3831 */
3832 remove: function(key) {
3833 var value = this[key = hashKey(key, this.nextUid)];
3834 delete this[key];
3835 return value;
3836 }
3837};
3838
3839var $$HashMapProvider = [function() {
3840 this.$get = [function() {
3841 return HashMap;
3842 }];
3843}];
3844
3845/**
3846 * @ngdoc function
3847 * @module ng
3848 * @name angular.injector
3849 * @kind function
3850 *
3851 * @description
3852 * Creates an injector object that can be used for retrieving services as well as for
3853 * dependency injection (see {@link guide/di dependency injection}).
3854 *
3855 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3856 * {@link angular.module}. The `ng` module must be explicitly added.
3857 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3858 * disallows argument name annotation inference.
3859 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3860 *
3861 * @example
3862 * Typical usage
3863 * ```js
3864 * // create an injector
3865 * var $injector = angular.injector(['ng']);
3866 *
3867 * // use the injector to kick off your application
3868 * // use the type inference to auto inject arguments, or use implicit injection
3869 * $injector.invoke(function($rootScope, $compile, $document) {
3870 * $compile($document)($rootScope);
3871 * $rootScope.$digest();
3872 * });
3873 * ```
3874 *
3875 * Sometimes you want to get access to the injector of a currently running Angular app
3876 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3877 * application has been bootstrapped. You can do this using the extra `injector()` added
3878 * to JQuery/jqLite elements. See {@link angular.element}.
3879 *
3880 * *This is fairly rare but could be the case if a third party library is injecting the
3881 * markup.*
3882 *
3883 * In the following example a new block of HTML containing a `ng-controller`
3884 * directive is added to the end of the document body by JQuery. We then compile and link
3885 * it into the current AngularJS scope.
3886 *
3887 * ```js
3888 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3889 * $(document.body).append($div);
3890 *
3891 * angular.element(document).injector().invoke(function($compile) {
3892 * var scope = angular.element($div).scope();
3893 * $compile($div)(scope);
3894 * });
3895 * ```
3896 */
3897
3898
3899/**
3900 * @ngdoc module
3901 * @name auto
3902 * @installation
3903 * @description
3904 *
3905 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3906 */
3907
3908var ARROW_ARG = /^([^\(]+?)=>/;
3909var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3910var FN_ARG_SPLIT = /,/;
3911var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3912var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3913var $injectorMinErr = minErr('$injector');
3914
3915function stringifyFn(fn) {
3916 // Support: Chrome 50-51 only
3917 // Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
3918 // (See https://github.com/angular/angular.js/issues/14487.)
3919 // TODO (gkalpak): Remove workaround when Chrome v52 is released
3920 return Function.prototype.toString.call(fn) + ' ';
3921}
3922
3923function extractArgs(fn) {
3924 var fnText = stringifyFn(fn).replace(STRIP_COMMENTS, ''),
3925 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
3926 return args;
3927}
3928
3929function anonFn(fn) {
3930 // For anonymous functions, showing at the very least the function signature can help in
3931 // debugging.
3932 var args = extractArgs(fn);
3933 if (args) {
3934 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3935 }
3936 return 'fn';
3937}
3938
3939function annotate(fn, strictDi, name) {
3940 var $inject,
3941 argDecl,
3942 last;
3943
3944 if (typeof fn === 'function') {
3945 if (!($inject = fn.$inject)) {
3946 $inject = [];
3947 if (fn.length) {
3948 if (strictDi) {
3949 if (!isString(name) || !name) {
3950 name = fn.name || anonFn(fn);
3951 }
3952 throw $injectorMinErr('strictdi',
3953 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3954 }
3955 argDecl = extractArgs(fn);
3956 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3957 arg.replace(FN_ARG, function(all, underscore, name) {
3958 $inject.push(name);
3959 });
3960 });
3961 }
3962 fn.$inject = $inject;
3963 }
3964 } else if (isArray(fn)) {
3965 last = fn.length - 1;
3966 assertArgFn(fn[last], 'fn');
3967 $inject = fn.slice(0, last);
3968 } else {
3969 assertArgFn(fn, 'fn', true);
3970 }
3971 return $inject;
3972}
3973
3974///////////////////////////////////////
3975
3976/**
3977 * @ngdoc service
3978 * @name $injector
3979 *
3980 * @description
3981 *
3982 * `$injector` is used to retrieve object instances as defined by
3983 * {@link auto.$provide provider}, instantiate types, invoke methods,
3984 * and load modules.
3985 *
3986 * The following always holds true:
3987 *
3988 * ```js
3989 * var $injector = angular.injector();
3990 * expect($injector.get('$injector')).toBe($injector);
3991 * expect($injector.invoke(function($injector) {
3992 * return $injector;
3993 * })).toBe($injector);
3994 * ```
3995 *
3996 * # Injection Function Annotation
3997 *
3998 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3999 * following are all valid ways of annotating function with injection arguments and are equivalent.
4000 *
4001 * ```js
4002 * // inferred (only works if code not minified/obfuscated)
4003 * $injector.invoke(function(serviceA){});
4004 *
4005 * // annotated
4006 * function explicit(serviceA) {};
4007 * explicit.$inject = ['serviceA'];
4008 * $injector.invoke(explicit);
4009 *
4010 * // inline
4011 * $injector.invoke(['serviceA', function(serviceA){}]);
4012 * ```
4013 *
4014 * ## Inference
4015 *
4016 * In JavaScript calling `toString()` on a function returns the function definition. The definition
4017 * can then be parsed and the function arguments can be extracted. This method of discovering
4018 * annotations is disallowed when the injector is in strict mode.
4019 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
4020 * argument names.
4021 *
4022 * ## `$inject` Annotation
4023 * By adding an `$inject` property onto a function the injection parameters can be specified.
4024 *
4025 * ## Inline
4026 * As an array of injection names, where the last item in the array is the function to call.
4027 */
4028
4029/**
4030 * @ngdoc method
4031 * @name $injector#get
4032 *
4033 * @description
4034 * Return an instance of the service.
4035 *
4036 * @param {string} name The name of the instance to retrieve.
4037 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
4038 * @return {*} The instance.
4039 */
4040
4041/**
4042 * @ngdoc method
4043 * @name $injector#invoke
4044 *
4045 * @description
4046 * Invoke the method and supply the method arguments from the `$injector`.
4047 *
4048 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
4049 * injected according to the {@link guide/di $inject Annotation} rules.
4050 * @param {Object=} self The `this` for the invoked method.
4051 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4052 * object first, before the `$injector` is consulted.
4053 * @returns {*} the value returned by the invoked `fn` function.
4054 */
4055
4056/**
4057 * @ngdoc method
4058 * @name $injector#has
4059 *
4060 * @description
4061 * Allows the user to query if the particular service exists.
4062 *
4063 * @param {string} name Name of the service to query.
4064 * @returns {boolean} `true` if injector has given service.
4065 */
4066
4067/**
4068 * @ngdoc method
4069 * @name $injector#instantiate
4070 * @description
4071 * Create a new instance of JS type. The method takes a constructor function, invokes the new
4072 * operator, and supplies all of the arguments to the constructor function as specified by the
4073 * constructor annotation.
4074 *
4075 * @param {Function} Type Annotated constructor function.
4076 * @param {Object=} locals Optional object. If preset then any argument names are read from this
4077 * object first, before the `$injector` is consulted.
4078 * @returns {Object} new instance of `Type`.
4079 */
4080
4081/**
4082 * @ngdoc method
4083 * @name $injector#annotate
4084 *
4085 * @description
4086 * Returns an array of service names which the function is requesting for injection. This API is
4087 * used by the injector to determine which services need to be injected into the function when the
4088 * function is invoked. There are three ways in which the function can be annotated with the needed
4089 * dependencies.
4090 *
4091 * # Argument names
4092 *
4093 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4094 * by converting the function into a string using `toString()` method and extracting the argument
4095 * names.
4096 * ```js
4097 * // Given
4098 * function MyController($scope, $route) {
4099 * // ...
4100 * }
4101 *
4102 * // Then
4103 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4104 * ```
4105 *
4106 * You can disallow this method by using strict injection mode.
4107 *
4108 * This method does not work with code minification / obfuscation. For this reason the following
4109 * annotation strategies are supported.
4110 *
4111 * # The `$inject` property
4112 *
4113 * If a function has an `$inject` property and its value is an array of strings, then the strings
4114 * represent names of services to be injected into the function.
4115 * ```js
4116 * // Given
4117 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
4118 * // ...
4119 * }
4120 * // Define function dependencies
4121 * MyController['$inject'] = ['$scope', '$route'];
4122 *
4123 * // Then
4124 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4125 * ```
4126 *
4127 * # The array notation
4128 *
4129 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4130 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4131 * a way that survives minification is a better choice:
4132 *
4133 * ```js
4134 * // We wish to write this (not minification / obfuscation safe)
4135 * injector.invoke(function($compile, $rootScope) {
4136 * // ...
4137 * });
4138 *
4139 * // We are forced to write break inlining
4140 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4141 * // ...
4142 * };
4143 * tmpFn.$inject = ['$compile', '$rootScope'];
4144 * injector.invoke(tmpFn);
4145 *
4146 * // To better support inline function the inline annotation is supported
4147 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4148 * // ...
4149 * }]);
4150 *
4151 * // Therefore
4152 * expect(injector.annotate(
4153 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4154 * ).toEqual(['$compile', '$rootScope']);
4155 * ```
4156 *
4157 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4158 * be retrieved as described above.
4159 *
4160 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4161 *
4162 * @returns {Array.<string>} The names of the services which the function requires.
4163 */
4164
4165
4166
4167
4168/**
4169 * @ngdoc service
4170 * @name $provide
4171 *
4172 * @description
4173 *
4174 * The {@link auto.$provide $provide} service has a number of methods for registering components
4175 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4176 * {@link angular.Module}.
4177 *
4178 * An Angular **service** is a singleton object created by a **service factory**. These **service
4179 * factories** are functions which, in turn, are created by a **service provider**.
4180 * The **service providers** are constructor functions. When instantiated they must contain a
4181 * property called `$get`, which holds the **service factory** function.
4182 *
4183 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4184 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4185 * function to get the instance of the **service**.
4186 *
4187 * Often services have no configuration options and there is no need to add methods to the service
4188 * provider. The provider will be no more than a constructor function with a `$get` property. For
4189 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4190 * services without specifying a provider.
4191 *
4192 * * {@link auto.$provide#provider provider(name, provider)} - registers a **service provider** with the
4193 * {@link auto.$injector $injector}
4194 * * {@link auto.$provide#constant constant(name, obj)} - registers a value/object that can be accessed by
4195 * providers and services.
4196 * * {@link auto.$provide#value value(name, obj)} - registers a value/object that can only be accessed by
4197 * services, not providers.
4198 * * {@link auto.$provide#factory factory(name, fn)} - registers a service **factory function**
4199 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4200 * given factory function.
4201 * * {@link auto.$provide#service service(name, Fn)} - registers a **constructor function**
4202 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4203 * a new object using the given constructor function.
4204 * * {@link auto.$provide#decorator decorator(name, decorFn)} - registers a **decorator function** that
4205 * will be able to modify or replace the implementation of another service.
4206 *
4207 * See the individual methods for more information and examples.
4208 */
4209
4210/**
4211 * @ngdoc method
4212 * @name $provide#provider
4213 * @description
4214 *
4215 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4216 * are constructor functions, whose instances are responsible for "providing" a factory for a
4217 * service.
4218 *
4219 * Service provider names start with the name of the service they provide followed by `Provider`.
4220 * For example, the {@link ng.$log $log} service has a provider called
4221 * {@link ng.$logProvider $logProvider}.
4222 *
4223 * Service provider objects can have additional methods which allow configuration of the provider
4224 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4225 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4226 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4227 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4228 * console or not.
4229 *
4230 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4231 'Provider'` key.
4232 * @param {(Object|function())} provider If the provider is:
4233 *
4234 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4235 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4236 * - `Constructor`: a new instance of the provider will be created using
4237 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4238 *
4239 * @returns {Object} registered provider instance
4240
4241 * @example
4242 *
4243 * The following example shows how to create a simple event tracking service and register it using
4244 * {@link auto.$provide#provider $provide.provider()}.
4245 *
4246 * ```js
4247 * // Define the eventTracker provider
4248 * function EventTrackerProvider() {
4249 * var trackingUrl = '/track';
4250 *
4251 * // A provider method for configuring where the tracked events should been saved
4252 * this.setTrackingUrl = function(url) {
4253 * trackingUrl = url;
4254 * };
4255 *
4256 * // The service factory function
4257 * this.$get = ['$http', function($http) {
4258 * var trackedEvents = {};
4259 * return {
4260 * // Call this to track an event
4261 * event: function(event) {
4262 * var count = trackedEvents[event] || 0;
4263 * count += 1;
4264 * trackedEvents[event] = count;
4265 * return count;
4266 * },
4267 * // Call this to save the tracked events to the trackingUrl
4268 * save: function() {
4269 * $http.post(trackingUrl, trackedEvents);
4270 * }
4271 * };
4272 * }];
4273 * }
4274 *
4275 * describe('eventTracker', function() {
4276 * var postSpy;
4277 *
4278 * beforeEach(module(function($provide) {
4279 * // Register the eventTracker provider
4280 * $provide.provider('eventTracker', EventTrackerProvider);
4281 * }));
4282 *
4283 * beforeEach(module(function(eventTrackerProvider) {
4284 * // Configure eventTracker provider
4285 * eventTrackerProvider.setTrackingUrl('/custom-track');
4286 * }));
4287 *
4288 * it('tracks events', inject(function(eventTracker) {
4289 * expect(eventTracker.event('login')).toEqual(1);
4290 * expect(eventTracker.event('login')).toEqual(2);
4291 * }));
4292 *
4293 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4294 * postSpy = spyOn($http, 'post');
4295 * eventTracker.event('login');
4296 * eventTracker.save();
4297 * expect(postSpy).toHaveBeenCalled();
4298 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4299 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4300 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4301 * }));
4302 * });
4303 * ```
4304 */
4305
4306/**
4307 * @ngdoc method
4308 * @name $provide#factory
4309 * @description
4310 *
4311 * Register a **service factory**, which will be called to return the service instance.
4312 * This is short for registering a service where its provider consists of only a `$get` property,
4313 * which is the given service factory function.
4314 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4315 * configure your service in a provider.
4316 *
4317 * @param {string} name The name of the instance.
4318 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4319 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4320 * @returns {Object} registered provider instance
4321 *
4322 * @example
4323 * Here is an example of registering a service
4324 * ```js
4325 * $provide.factory('ping', ['$http', function($http) {
4326 * return function ping() {
4327 * return $http.send('/ping');
4328 * };
4329 * }]);
4330 * ```
4331 * You would then inject and use this service like this:
4332 * ```js
4333 * someModule.controller('Ctrl', ['ping', function(ping) {
4334 * ping();
4335 * }]);
4336 * ```
4337 */
4338
4339
4340/**
4341 * @ngdoc method
4342 * @name $provide#service
4343 * @description
4344 *
4345 * Register a **service constructor**, which will be invoked with `new` to create the service
4346 * instance.
4347 * This is short for registering a service where its provider's `$get` property is a factory
4348 * function that returns an instance instantiated by the injector from the service constructor
4349 * function.
4350 *
4351 * Internally it looks a bit like this:
4352 *
4353 * ```
4354 * {
4355 * $get: function() {
4356 * return $injector.instantiate(constructor);
4357 * }
4358 * }
4359 * ```
4360 *
4361 *
4362 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4363 * as a type/class.
4364 *
4365 * @param {string} name The name of the instance.
4366 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4367 * that will be instantiated.
4368 * @returns {Object} registered provider instance
4369 *
4370 * @example
4371 * Here is an example of registering a service using
4372 * {@link auto.$provide#service $provide.service(class)}.
4373 * ```js
4374 * var Ping = function($http) {
4375 * this.$http = $http;
4376 * };
4377 *
4378 * Ping.$inject = ['$http'];
4379 *
4380 * Ping.prototype.send = function() {
4381 * return this.$http.get('/ping');
4382 * };
4383 * $provide.service('ping', Ping);
4384 * ```
4385 * You would then inject and use this service like this:
4386 * ```js
4387 * someModule.controller('Ctrl', ['ping', function(ping) {
4388 * ping.send();
4389 * }]);
4390 * ```
4391 */
4392
4393
4394/**
4395 * @ngdoc method
4396 * @name $provide#value
4397 * @description
4398 *
4399 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4400 * number, an array, an object or a function. This is short for registering a service where its
4401 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4402 * service**. That also means it is not possible to inject other services into a value service.
4403 *
4404 * Value services are similar to constant services, except that they cannot be injected into a
4405 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4406 * an Angular {@link auto.$provide#decorator decorator}.
4407 *
4408 * @param {string} name The name of the instance.
4409 * @param {*} value The value.
4410 * @returns {Object} registered provider instance
4411 *
4412 * @example
4413 * Here are some examples of creating value services.
4414 * ```js
4415 * $provide.value('ADMIN_USER', 'admin');
4416 *
4417 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4418 *
4419 * $provide.value('halfOf', function(value) {
4420 * return value / 2;
4421 * });
4422 * ```
4423 */
4424
4425
4426/**
4427 * @ngdoc method
4428 * @name $provide#constant
4429 * @description
4430 *
4431 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4432 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4433 * possible to inject other services into a constant.
4434 *
4435 * But unlike {@link auto.$provide#value value}, a constant can be
4436 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4437 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4438 *
4439 * @param {string} name The name of the constant.
4440 * @param {*} value The constant value.
4441 * @returns {Object} registered instance
4442 *
4443 * @example
4444 * Here a some examples of creating constants:
4445 * ```js
4446 * $provide.constant('SHARD_HEIGHT', 306);
4447 *
4448 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4449 *
4450 * $provide.constant('double', function(value) {
4451 * return value * 2;
4452 * });
4453 * ```
4454 */
4455
4456
4457/**
4458 * @ngdoc method
4459 * @name $provide#decorator
4460 * @description
4461 *
4462 * Register a **decorator function** with the {@link auto.$injector $injector}. A decorator function
4463 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4464 * service. The return value of the decorator function may be the original service, or a new service
4465 * that replaces (or wraps and delegates to) the original service.
4466 *
4467 * You can find out more about using decorators in the {@link guide/decorators} guide.
4468 *
4469 * @param {string} name The name of the service to decorate.
4470 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4471 * provided and should return the decorated service instance. The function is called using
4472 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4473 * Local injection arguments:
4474 *
4475 * * `$delegate` - The original service instance, which can be replaced, monkey patched, configured,
4476 * decorated or delegated to.
4477 *
4478 * @example
4479 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4480 * calls to {@link ng.$log#error $log.warn()}.
4481 * ```js
4482 * $provide.decorator('$log', ['$delegate', function($delegate) {
4483 * $delegate.warn = $delegate.error;
4484 * return $delegate;
4485 * }]);
4486 * ```
4487 */
4488
4489
4490function createInjector(modulesToLoad, strictDi) {
4491 strictDi = (strictDi === true);
4492 var INSTANTIATING = {},
4493 providerSuffix = 'Provider',
4494 path = [],
4495 loadedModules = new HashMap([], true),
4496 providerCache = {
4497 $provide: {
4498 provider: supportObject(provider),
4499 factory: supportObject(factory),
4500 service: supportObject(service),
4501 value: supportObject(value),
4502 constant: supportObject(constant),
4503 decorator: decorator
4504 }
4505 },
4506 providerInjector = (providerCache.$injector =
4507 createInternalInjector(providerCache, function(serviceName, caller) {
4508 if (angular.isString(caller)) {
4509 path.push(caller);
4510 }
4511 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4512 })),
4513 instanceCache = {},
4514 protoInstanceInjector =
4515 createInternalInjector(instanceCache, function(serviceName, caller) {
4516 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4517 return instanceInjector.invoke(
4518 provider.$get, provider, undefined, serviceName);
4519 }),
4520 instanceInjector = protoInstanceInjector;
4521
4522 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4523 var runBlocks = loadModules(modulesToLoad);
4524 instanceInjector = protoInstanceInjector.get('$injector');
4525 instanceInjector.strictDi = strictDi;
4526 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4527
4528 return instanceInjector;
4529
4530 ////////////////////////////////////
4531 // $provider
4532 ////////////////////////////////////
4533
4534 function supportObject(delegate) {
4535 return function(key, value) {
4536 if (isObject(key)) {
4537 forEach(key, reverseParams(delegate));
4538 } else {
4539 return delegate(key, value);
4540 }
4541 };
4542 }
4543
4544 function provider(name, provider_) {
4545 assertNotHasOwnProperty(name, 'service');
4546 if (isFunction(provider_) || isArray(provider_)) {
4547 provider_ = providerInjector.instantiate(provider_);
4548 }
4549 if (!provider_.$get) {
4550 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4551 }
4552 return providerCache[name + providerSuffix] = provider_;
4553 }
4554
4555 function enforceReturnValue(name, factory) {
4556 return function enforcedReturnValue() {
4557 var result = instanceInjector.invoke(factory, this);
4558 if (isUndefined(result)) {
4559 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4560 }
4561 return result;
4562 };
4563 }
4564
4565 function factory(name, factoryFn, enforce) {
4566 return provider(name, {
4567 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4568 });
4569 }
4570
4571 function service(name, constructor) {
4572 return factory(name, ['$injector', function($injector) {
4573 return $injector.instantiate(constructor);
4574 }]);
4575 }
4576
4577 function value(name, val) { return factory(name, valueFn(val), false); }
4578
4579 function constant(name, value) {
4580 assertNotHasOwnProperty(name, 'constant');
4581 providerCache[name] = value;
4582 instanceCache[name] = value;
4583 }
4584
4585 function decorator(serviceName, decorFn) {
4586 var origProvider = providerInjector.get(serviceName + providerSuffix),
4587 orig$get = origProvider.$get;
4588
4589 origProvider.$get = function() {
4590 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4591 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4592 };
4593 }
4594
4595 ////////////////////////////////////
4596 // Module Loading
4597 ////////////////////////////////////
4598 function loadModules(modulesToLoad) {
4599 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4600 var runBlocks = [], moduleFn;
4601 forEach(modulesToLoad, function(module) {
4602 if (loadedModules.get(module)) return;
4603 loadedModules.put(module, true);
4604
4605 function runInvokeQueue(queue) {
4606 var i, ii;
4607 for (i = 0, ii = queue.length; i < ii; i++) {
4608 var invokeArgs = queue[i],
4609 provider = providerInjector.get(invokeArgs[0]);
4610
4611 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4612 }
4613 }
4614
4615 try {
4616 if (isString(module)) {
4617 moduleFn = angularModule(module);
4618 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4619 runInvokeQueue(moduleFn._invokeQueue);
4620 runInvokeQueue(moduleFn._configBlocks);
4621 } else if (isFunction(module)) {
4622 runBlocks.push(providerInjector.invoke(module));
4623 } else if (isArray(module)) {
4624 runBlocks.push(providerInjector.invoke(module));
4625 } else {
4626 assertArgFn(module, 'module');
4627 }
4628 } catch (e) {
4629 if (isArray(module)) {
4630 module = module[module.length - 1];
4631 }
4632 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4633 // Safari & FF's stack traces don't contain error.message content
4634 // unlike those of Chrome and IE
4635 // So if stack doesn't contain message, we create a new string that contains both.
4636 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4637 /* jshint -W022 */
4638 e = e.message + '\n' + e.stack;
4639 }
4640 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4641 module, e.stack || e.message || e);
4642 }
4643 });
4644 return runBlocks;
4645 }
4646
4647 ////////////////////////////////////
4648 // internal Injector
4649 ////////////////////////////////////
4650
4651 function createInternalInjector(cache, factory) {
4652
4653 function getService(serviceName, caller) {
4654 if (cache.hasOwnProperty(serviceName)) {
4655 if (cache[serviceName] === INSTANTIATING) {
4656 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4657 serviceName + ' <- ' + path.join(' <- '));
4658 }
4659 return cache[serviceName];
4660 } else {
4661 try {
4662 path.unshift(serviceName);
4663 cache[serviceName] = INSTANTIATING;
4664 return cache[serviceName] = factory(serviceName, caller);
4665 } catch (err) {
4666 if (cache[serviceName] === INSTANTIATING) {
4667 delete cache[serviceName];
4668 }
4669 throw err;
4670 } finally {
4671 path.shift();
4672 }
4673 }
4674 }
4675
4676
4677 function injectionArgs(fn, locals, serviceName) {
4678 var args = [],
4679 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4680
4681 for (var i = 0, length = $inject.length; i < length; i++) {
4682 var key = $inject[i];
4683 if (typeof key !== 'string') {
4684 throw $injectorMinErr('itkn',
4685 'Incorrect injection token! Expected service name as string, got {0}', key);
4686 }
4687 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4688 getService(key, serviceName));
4689 }
4690 return args;
4691 }
4692
4693 function isClass(func) {
4694 // IE 9-11 do not support classes and IE9 leaks with the code below.
4695 if (msie <= 11) {
4696 return false;
4697 }
4698 // Support: Edge 12-13 only
4699 // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
4700 return typeof func === 'function'
4701 && /^(?:class\b|constructor\()/.test(stringifyFn(func));
4702 }
4703
4704 function invoke(fn, self, locals, serviceName) {
4705 if (typeof locals === 'string') {
4706 serviceName = locals;
4707 locals = null;
4708 }
4709
4710 var args = injectionArgs(fn, locals, serviceName);
4711 if (isArray(fn)) {
4712 fn = fn[fn.length - 1];
4713 }
4714
4715 if (!isClass(fn)) {
4716 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4717 // #5388
4718 return fn.apply(self, args);
4719 } else {
4720 args.unshift(null);
4721 return new (Function.prototype.bind.apply(fn, args))();
4722 }
4723 }
4724
4725
4726 function instantiate(Type, locals, serviceName) {
4727 // Check if Type is annotated and use just the given function at n-1 as parameter
4728 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4729 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
4730 var args = injectionArgs(Type, locals, serviceName);
4731 // Empty object at position 0 is ignored for invocation with `new`, but required.
4732 args.unshift(null);
4733 return new (Function.prototype.bind.apply(ctor, args))();
4734 }
4735
4736
4737 return {
4738 invoke: invoke,
4739 instantiate: instantiate,
4740 get: getService,
4741 annotate: createInjector.$$annotate,
4742 has: function(name) {
4743 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4744 }
4745 };
4746 }
4747}
4748
4749createInjector.$$annotate = annotate;
4750
4751/**
4752 * @ngdoc provider
4753 * @name $anchorScrollProvider
4754 *
4755 * @description
4756 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4757 * {@link ng.$location#hash $location.hash()} changes.
4758 */
4759function $AnchorScrollProvider() {
4760
4761 var autoScrollingEnabled = true;
4762
4763 /**
4764 * @ngdoc method
4765 * @name $anchorScrollProvider#disableAutoScrolling
4766 *
4767 * @description
4768 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4769 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4770 * Use this method to disable automatic scrolling.
4771 *
4772 * If automatic scrolling is disabled, one must explicitly call
4773 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4774 * current hash.
4775 */
4776 this.disableAutoScrolling = function() {
4777 autoScrollingEnabled = false;
4778 };
4779
4780 /**
4781 * @ngdoc service
4782 * @name $anchorScroll
4783 * @kind function
4784 * @requires $window
4785 * @requires $location
4786 * @requires $rootScope
4787 *
4788 * @description
4789 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4790 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4791 * in the
4792 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
4793 *
4794 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4795 * match any anchor whenever it changes. This can be disabled by calling
4796 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4797 *
4798 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4799 * vertical scroll-offset (either fixed or dynamic).
4800 *
4801 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4802 * {@link ng.$location#hash $location.hash()} will be used.
4803 *
4804 * @property {(number|function|jqLite)} yOffset
4805 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4806 * positioned elements at the top of the page, such as navbars, headers etc.
4807 *
4808 * `yOffset` can be specified in various ways:
4809 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4810 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4811 * a number representing the offset (in pixels).<br /><br />
4812 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4813 * the top of the page to the element's bottom will be used as offset.<br />
4814 * **Note**: The element will be taken into account only as long as its `position` is set to
4815 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4816 * their height and/or positioning according to the viewport's size.
4817 *
4818 * <br />
4819 * <div class="alert alert-warning">
4820 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4821 * not some child element.
4822 * </div>
4823 *
4824 * @example
4825 <example module="anchorScrollExample">
4826 <file name="index.html">
4827 <div id="scrollArea" ng-controller="ScrollController">
4828 <a ng-click="gotoBottom()">Go to bottom</a>
4829 <a id="bottom"></a> You're at the bottom!
4830 </div>
4831 </file>
4832 <file name="script.js">
4833 angular.module('anchorScrollExample', [])
4834 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4835 function ($scope, $location, $anchorScroll) {
4836 $scope.gotoBottom = function() {
4837 // set the location.hash to the id of
4838 // the element you wish to scroll to.
4839 $location.hash('bottom');
4840
4841 // call $anchorScroll()
4842 $anchorScroll();
4843 };
4844 }]);
4845 </file>
4846 <file name="style.css">
4847 #scrollArea {
4848 height: 280px;
4849 overflow: auto;
4850 }
4851
4852 #bottom {
4853 display: block;
4854 margin-top: 2000px;
4855 }
4856 </file>
4857 </example>
4858 *
4859 * <hr />
4860 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4861 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4862 *
4863 * @example
4864 <example module="anchorScrollOffsetExample">
4865 <file name="index.html">
4866 <div class="fixed-header" ng-controller="headerCtrl">
4867 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4868 Go to anchor {{x}}
4869 </a>
4870 </div>
4871 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4872 Anchor {{x}} of 5
4873 </div>
4874 </file>
4875 <file name="script.js">
4876 angular.module('anchorScrollOffsetExample', [])
4877 .run(['$anchorScroll', function($anchorScroll) {
4878 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4879 }])
4880 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4881 function ($anchorScroll, $location, $scope) {
4882 $scope.gotoAnchor = function(x) {
4883 var newHash = 'anchor' + x;
4884 if ($location.hash() !== newHash) {
4885 // set the $location.hash to `newHash` and
4886 // $anchorScroll will automatically scroll to it
4887 $location.hash('anchor' + x);
4888 } else {
4889 // call $anchorScroll() explicitly,
4890 // since $location.hash hasn't changed
4891 $anchorScroll();
4892 }
4893 };
4894 }
4895 ]);
4896 </file>
4897 <file name="style.css">
4898 body {
4899 padding-top: 50px;
4900 }
4901
4902 .anchor {
4903 border: 2px dashed DarkOrchid;
4904 padding: 10px 10px 200px 10px;
4905 }
4906
4907 .fixed-header {
4908 background-color: rgba(0, 0, 0, 0.2);
4909 height: 50px;
4910 position: fixed;
4911 top: 0; left: 0; right: 0;
4912 }
4913
4914 .fixed-header > a {
4915 display: inline-block;
4916 margin: 5px 15px;
4917 }
4918 </file>
4919 </example>
4920 */
4921 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4922 var document = $window.document;
4923
4924 // Helper function to get first anchor from a NodeList
4925 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4926 // and working in all supported browsers.)
4927 function getFirstAnchor(list) {
4928 var result = null;
4929 Array.prototype.some.call(list, function(element) {
4930 if (nodeName_(element) === 'a') {
4931 result = element;
4932 return true;
4933 }
4934 });
4935 return result;
4936 }
4937
4938 function getYOffset() {
4939
4940 var offset = scroll.yOffset;
4941
4942 if (isFunction(offset)) {
4943 offset = offset();
4944 } else if (isElement(offset)) {
4945 var elem = offset[0];
4946 var style = $window.getComputedStyle(elem);
4947 if (style.position !== 'fixed') {
4948 offset = 0;
4949 } else {
4950 offset = elem.getBoundingClientRect().bottom;
4951 }
4952 } else if (!isNumber(offset)) {
4953 offset = 0;
4954 }
4955
4956 return offset;
4957 }
4958
4959 function scrollTo(elem) {
4960 if (elem) {
4961 elem.scrollIntoView();
4962
4963 var offset = getYOffset();
4964
4965 if (offset) {
4966 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4967 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4968 // top of the viewport.
4969 //
4970 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4971 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4972 // way down the page.
4973 //
4974 // This is often the case for elements near the bottom of the page.
4975 //
4976 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4977 // the top of the element and the offset, which is enough to align the top of `elem` at the
4978 // desired position.
4979 var elemTop = elem.getBoundingClientRect().top;
4980 $window.scrollBy(0, elemTop - offset);
4981 }
4982 } else {
4983 $window.scrollTo(0, 0);
4984 }
4985 }
4986
4987 function scroll(hash) {
4988 hash = isString(hash) ? hash : $location.hash();
4989 var elm;
4990
4991 // empty hash, scroll to the top of the page
4992 if (!hash) scrollTo(null);
4993
4994 // element with given id
4995 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4996
4997 // first anchor with given name :-D
4998 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4999

26769 more lines aren’t shown here.

Source/Libraries/GSF.Web/Shared/Scripts/gsf.web.client.js+5−5
@@ -786,25 +786,25 @@ $.fn.disable = function() { · 785 unmodified lines
786 786
787$.fn.visible = function() {787$.fn.visible = function() {
788 return this.each(function() {788 return this.each(function() {
789 this.css("visibility", "visible");789 $(this).css("visibility", "visible");
790 });790 });
791}791}
792 792
793$.fn.invisible = function() {793$.fn.invisible = function() {
794 return this.each(function() {794 return this.each(function() {
795 this.css("visibility", "hidden");795 $(this).css("visibility", "hidden");
796 });796 });
797}797}
798 798
799$.fn.readCookie = function(name) {799$.fn.readCookie = function(name) {
800 this.val(Cookies.get(name));800 $(this).val(Cookies.get(name));
801}801}
802 802
803$.fn.writeCookie = function(name, expiration) {803$.fn.writeCookie = function(name, expiration) {
804 if (expiration) // Date or number of days804 if (expiration) // Date or number of days
805 Cookies.set(name, this.val(), { expires: expiration });805 Cookies.set(name, $(this).val(), { expires: expiration });
806 else806 else
807 Cookies.set(name, this.val());807 Cookies.set(name, $(this).val());
808}808}
809 809
810$.fn.removeCookie = function(name) {810$.fn.removeCookie = function(name) {
Source/Libraries/GSF.Web/Shared/Scripts/jquery-ui.js+16617−0
@@ -0,0 +1,16617 @@
1/*! jQuery UI - v1.11.4 - 2016-03-31
2* http://jqueryui.com
3* Includes: core.js, widget.js, mouse.js, position.js, draggable.js, droppable.js, resizable.js, selectable.js, sortable.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, menu.js, progressbar.js, selectmenu.js, slider.js, spinner.js, tabs.js, tooltip.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js
4* Copyright jQuery Foundation and other contributors; Licensed MIT */
5
6(function( factory ) {
7 if ( typeof define === "function" && define.amd ) {
8
9 // AMD. Register as an anonymous module.
10 define([ "jquery" ], factory );
11 } else {
12
13 // Browser globals
14 factory( jQuery );
15 }
16}(function( $ ) {
17/*!
18 * jQuery UI Core 1.11.4
19 * http://jqueryui.com
20 *
21 * Copyright jQuery Foundation and other contributors
22 * Released under the MIT license.
23 * http://jquery.org/license
24 *
25 * http://api.jqueryui.com/category/ui-core/
26 */
27
28
29// $.ui might exist from components with no dependencies, e.g., $.ui.position
30$.ui = $.ui || {};
31
32$.extend( $.ui, {
33 version: "1.11.4",
34
35 keyCode: {
36 BACKSPACE: 8,
37 COMMA: 188,
38 DELETE: 46,
39 DOWN: 40,
40 END: 35,
41 ENTER: 13,
42 ESCAPE: 27,
43 HOME: 36,
44 LEFT: 37,
45 PAGE_DOWN: 34,
46 PAGE_UP: 33,
47 PERIOD: 190,
48 RIGHT: 39,
49 SPACE: 32,
50 TAB: 9,
51 UP: 38
52 }
53});
54
55// plugins
56$.fn.extend({
57 scrollParent: function( includeHidden ) {
58 var position = this.css( "position" ),
59 excludeStaticParent = position === "absolute",
60 overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/,
61 scrollParent = this.parents().filter( function() {
62 var parent = $( this );
63 if ( excludeStaticParent && parent.css( "position" ) === "static" ) {
64 return false;
65 }
66 return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + parent.css( "overflow-x" ) );
67 }).eq( 0 );
68
69 return position === "fixed" || !scrollParent.length ? $( this[ 0 ].ownerDocument || document ) : scrollParent;
70 },
71
72 uniqueId: (function() {
73 var uuid = 0;
74
75 return function() {
76 return this.each(function() {
77 if ( !this.id ) {
78 this.id = "ui-id-" + ( ++uuid );
79 }
80 });
81 };
82 })(),
83
84 removeUniqueId: function() {
85 return this.each(function() {
86 if ( /^ui-id-\d+$/.test( this.id ) ) {
87 $( this ).removeAttr( "id" );
88 }
89 });
90 }
91});
92
93// selectors
94function focusable( element, isTabIndexNotNaN ) {
95 var map, mapName, img,
96 nodeName = element.nodeName.toLowerCase();
97 if ( "area" === nodeName ) {
98 map = element.parentNode;
99 mapName = map.name;
100 if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
101 return false;
102 }
103 img = $( "img[usemap='#" + mapName + "']" )[ 0 ];
104 return !!img && visible( img );
105 }
106 return ( /^(input|select|textarea|button|object)$/.test( nodeName ) ?
107 !element.disabled :
108 "a" === nodeName ?
109 element.href || isTabIndexNotNaN :
110 isTabIndexNotNaN) &&
111 // the element and all of its ancestors must be visible
112 visible( element );
113}
114
115function visible( element ) {
116 return $.expr.filters.visible( element ) &&
117 !$( element ).parents().addBack().filter(function() {
118 return $.css( this, "visibility" ) === "hidden";
119 }).length;
120}
121
122$.extend( $.expr[ ":" ], {
123 data: $.expr.createPseudo ?
124 $.expr.createPseudo(function( dataName ) {
125 return function( elem ) {
126 return !!$.data( elem, dataName );
127 };
128 }) :
129 // support: jQuery <1.8
130 function( elem, i, match ) {
131 return !!$.data( elem, match[ 3 ] );
132 },
133
134 focusable: function( element ) {
135 return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
136 },
137
138 tabbable: function( element ) {
139 var tabIndex = $.attr( element, "tabindex" ),
140 isTabIndexNaN = isNaN( tabIndex );
141 return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
142 }
143});
144
145// support: jQuery <1.8
146if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
147 $.each( [ "Width", "Height" ], function( i, name ) {
148 var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
149 type = name.toLowerCase(),
150 orig = {
151 innerWidth: $.fn.innerWidth,
152 innerHeight: $.fn.innerHeight,
153 outerWidth: $.fn.outerWidth,
154 outerHeight: $.fn.outerHeight
155 };
156
157 function reduce( elem, size, border, margin ) {
158 $.each( side, function() {
159 size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
160 if ( border ) {
161 size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
162 }
163 if ( margin ) {
164 size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
165 }
166 });
167 return size;
168 }
169
170 $.fn[ "inner" + name ] = function( size ) {
171 if ( size === undefined ) {
172 return orig[ "inner" + name ].call( this );
173 }
174
175 return this.each(function() {
176 $( this ).css( type, reduce( this, size ) + "px" );
177 });
178 };
179
180 $.fn[ "outer" + name] = function( size, margin ) {
181 if ( typeof size !== "number" ) {
182 return orig[ "outer" + name ].call( this, size );
183 }
184
185 return this.each(function() {
186 $( this).css( type, reduce( this, size, true, margin ) + "px" );
187 });
188 };
189 });
190}
191
192// support: jQuery <1.8
193if ( !$.fn.addBack ) {
194 $.fn.addBack = function( selector ) {
195 return this.add( selector == null ?
196 this.prevObject : this.prevObject.filter( selector )
197 );
198 };
199}
200
201// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
202if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
203 $.fn.removeData = (function( removeData ) {
204 return function( key ) {
205 if ( arguments.length ) {
206 return removeData.call( this, $.camelCase( key ) );
207 } else {
208 return removeData.call( this );
209 }
210 };
211 })( $.fn.removeData );
212}
213
214// deprecated
215$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
216
217$.fn.extend({
218 focus: (function( orig ) {
219 return function( delay, fn ) {
220 return typeof delay === "number" ?
221 this.each(function() {
222 var elem = this;
223 setTimeout(function() {
224 $( elem ).focus();
225 if ( fn ) {
226 fn.call( elem );
227 }
228 }, delay );
229 }) :
230 orig.apply( this, arguments );
231 };
232 })( $.fn.focus ),
233
234 disableSelection: (function() {
235 var eventType = "onselectstart" in document.createElement( "div" ) ?
236 "selectstart" :
237 "mousedown";
238
239 return function() {
240 return this.bind( eventType + ".ui-disableSelection", function( event ) {
241 event.preventDefault();
242 });
243 };
244 })(),
245
246 enableSelection: function() {
247 return this.unbind( ".ui-disableSelection" );
248 },
249
250 zIndex: function( zIndex ) {
251 if ( zIndex !== undefined ) {
252 return this.css( "zIndex", zIndex );
253 }
254
255 if ( this.length ) {
256 var elem = $( this[ 0 ] ), position, value;
257 while ( elem.length && elem[ 0 ] !== document ) {
258 // Ignore z-index if position is set to a value where z-index is ignored by the browser
259 // This makes behavior of this function consistent across browsers
260 // WebKit always returns auto if the element is positioned
261 position = elem.css( "position" );
262 if ( position === "absolute" || position === "relative" || position === "fixed" ) {
263 // IE returns 0 when zIndex is not specified
264 // other browsers return a string
265 // we ignore the case of nested elements with an explicit value of 0
266 // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
267 value = parseInt( elem.css( "zIndex" ), 10 );
268 if ( !isNaN( value ) && value !== 0 ) {
269 return value;
270 }
271 }
272 elem = elem.parent();
273 }
274 }
275
276 return 0;
277 }
278});
279
280// $.ui.plugin is deprecated. Use $.widget() extensions instead.
281$.ui.plugin = {
282 add: function( module, option, set ) {
283 var i,
284 proto = $.ui[ module ].prototype;
285 for ( i in set ) {
286 proto.plugins[ i ] = proto.plugins[ i ] || [];
287 proto.plugins[ i ].push( [ option, set[ i ] ] );
288 }
289 },
290 call: function( instance, name, args, allowDisconnected ) {
291 var i,
292 set = instance.plugins[ name ];
293
294 if ( !set ) {
295 return;
296 }
297
298 if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) ) {
299 return;
300 }
301
302 for ( i = 0; i < set.length; i++ ) {
303 if ( instance.options[ set[ i ][ 0 ] ] ) {
304 set[ i ][ 1 ].apply( instance.element, args );
305 }
306 }
307 }
308};
309
310
311/*!
312 * jQuery UI Widget 1.11.4
313 * http://jqueryui.com
314 *
315 * Copyright jQuery Foundation and other contributors
316 * Released under the MIT license.
317 * http://jquery.org/license
318 *
319 * http://api.jqueryui.com/jQuery.widget/
320 */
321
322
323var widget_uuid = 0,
324 widget_slice = Array.prototype.slice;
325
326$.cleanData = (function( orig ) {
327 return function( elems ) {
328 var events, elem, i;
329 for ( i = 0; (elem = elems[i]) != null; i++ ) {
330 try {
331
332 // Only trigger remove when necessary to save time
333 events = $._data( elem, "events" );
334 if ( events && events.remove ) {
335 $( elem ).triggerHandler( "remove" );
336 }
337
338 // http://bugs.jquery.com/ticket/8235
339 } catch ( e ) {}
340 }
341 orig( elems );
342 };
343})( $.cleanData );
344
345$.widget = function( name, base, prototype ) {
346 var fullName, existingConstructor, constructor, basePrototype,
347 // proxiedPrototype allows the provided prototype to remain unmodified
348 // so that it can be used as a mixin for multiple widgets (#8876)
349 proxiedPrototype = {},
350 namespace = name.split( "." )[ 0 ];
351
352 name = name.split( "." )[ 1 ];
353 fullName = namespace + "-" + name;
354
355 if ( !prototype ) {
356 prototype = base;
357 base = $.Widget;
358 }
359
360 // create selector for plugin
361 $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
362 return !!$.data( elem, fullName );
363 };
364
365 $[ namespace ] = $[ namespace ] || {};
366 existingConstructor = $[ namespace ][ name ];
367 constructor = $[ namespace ][ name ] = function( options, element ) {
368 // allow instantiation without "new" keyword
369 if ( !this._createWidget ) {
370 return new constructor( options, element );
371 }
372
373 // allow instantiation without initializing for simple inheritance
374 // must use "new" keyword (the code above always passes args)
375 if ( arguments.length ) {
376 this._createWidget( options, element );
377 }
378 };
379 // extend with the existing constructor to carry over any static properties
380 $.extend( constructor, existingConstructor, {
381 version: prototype.version,
382 // copy the object used to create the prototype in case we need to
383 // redefine the widget later
384 _proto: $.extend( {}, prototype ),
385 // track widgets that inherit from this widget in case this widget is
386 // redefined after a widget inherits from it
387 _childConstructors: []
388 });
389
390 basePrototype = new base();
391 // we need to make the options hash a property directly on the new instance
392 // otherwise we'll modify the options hash on the prototype that we're
393 // inheriting from
394 basePrototype.options = $.widget.extend( {}, basePrototype.options );
395 $.each( prototype, function( prop, value ) {
396 if ( !$.isFunction( value ) ) {
397 proxiedPrototype[ prop ] = value;
398 return;
399 }
400 proxiedPrototype[ prop ] = (function() {
401 var _super = function() {
402 return base.prototype[ prop ].apply( this, arguments );
403 },
404 _superApply = function( args ) {
405 return base.prototype[ prop ].apply( this, args );
406 };
407 return function() {
408 var __super = this._super,
409 __superApply = this._superApply,
410 returnValue;
411
412 this._super = _super;
413 this._superApply = _superApply;
414
415 returnValue = value.apply( this, arguments );
416
417 this._super = __super;
418 this._superApply = __superApply;
419
420 return returnValue;
421 };
422 })();
423 });
424 constructor.prototype = $.widget.extend( basePrototype, {
425 // TODO: remove support for widgetEventPrefix
426 // always use the name + a colon as the prefix, e.g., draggable:start
427 // don't prefix for widgets that aren't DOM-based
428 widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name
429 }, proxiedPrototype, {
430 constructor: constructor,
431 namespace: namespace,
432 widgetName: name,
433 widgetFullName: fullName
434 });
435
436 // If this widget is being redefined then we need to find all widgets that
437 // are inheriting from it and redefine all of them so that they inherit from
438 // the new version of this widget. We're essentially trying to replace one
439 // level in the prototype chain.
440 if ( existingConstructor ) {
441 $.each( existingConstructor._childConstructors, function( i, child ) {
442 var childPrototype = child.prototype;
443
444 // redefine the child widget using the same prototype that was
445 // originally used, but inherit from the new version of the base
446 $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
447 });
448 // remove the list of existing child constructors from the old constructor
449 // so the old child constructors can be garbage collected
450 delete existingConstructor._childConstructors;
451 } else {
452 base._childConstructors.push( constructor );
453 }
454
455 $.widget.bridge( name, constructor );
456
457 return constructor;
458};
459
460$.widget.extend = function( target ) {
461 var input = widget_slice.call( arguments, 1 ),
462 inputIndex = 0,
463 inputLength = input.length,
464 key,
465 value;
466 for ( ; inputIndex < inputLength; inputIndex++ ) {
467 for ( key in input[ inputIndex ] ) {
468 value = input[ inputIndex ][ key ];
469 if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
470 // Clone objects
471 if ( $.isPlainObject( value ) ) {
472 target[ key ] = $.isPlainObject( target[ key ] ) ?
473 $.widget.extend( {}, target[ key ], value ) :
474 // Don't extend strings, arrays, etc. with objects
475 $.widget.extend( {}, value );
476 // Copy everything else by reference
477 } else {
478 target[ key ] = value;
479 }
480 }
481 }
482 }
483 return target;
484};
485
486$.widget.bridge = function( name, object ) {
487 var fullName = object.prototype.widgetFullName || name;
488 $.fn[ name ] = function( options ) {
489 var isMethodCall = typeof options === "string",
490 args = widget_slice.call( arguments, 1 ),
491 returnValue = this;
492
493 if ( isMethodCall ) {
494 this.each(function() {
495 var methodValue,
496 instance = $.data( this, fullName );
497 if ( options === "instance" ) {
498 returnValue = instance;
499 return false;
500 }
501 if ( !instance ) {
502 return $.error( "cannot call methods on " + name + " prior to initialization; " +
503 "attempted to call method '" + options + "'" );
504 }
505 if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
506 return $.error( "no such method '" + options + "' for " + name + " widget instance" );
507 }
508 methodValue = instance[ options ].apply( instance, args );
509 if ( methodValue !== instance && methodValue !== undefined ) {
510 returnValue = methodValue && methodValue.jquery ?
511 returnValue.pushStack( methodValue.get() ) :
512 methodValue;
513 return false;
514 }
515 });
516 } else {
517
518 // Allow multiple hashes to be passed on init
519 if ( args.length ) {
520 options = $.widget.extend.apply( null, [ options ].concat(args) );
521 }
522
523 this.each(function() {
524 var instance = $.data( this, fullName );
525 if ( instance ) {
526 instance.option( options || {} );
527 if ( instance._init ) {
528 instance._init();
529 }
530 } else {
531 $.data( this, fullName, new object( options, this ) );
532 }
533 });
534 }
535
536 return returnValue;
537 };
538};
539
540$.Widget = function( /* options, element */ ) {};
541$.Widget._childConstructors = [];
542
543$.Widget.prototype = {
544 widgetName: "widget",
545 widgetEventPrefix: "",
546 defaultElement: "<div>",
547 options: {
548 disabled: false,
549
550 // callbacks
551 create: null
552 },
553 _createWidget: function( options, element ) {
554 element = $( element || this.defaultElement || this )[ 0 ];
555 this.element = $( element );
556 this.uuid = widget_uuid++;
557 this.eventNamespace = "." + this.widgetName + this.uuid;
558
559 this.bindings = $();
560 this.hoverable = $();
561 this.focusable = $();
562
563 if ( element !== this ) {
564 $.data( element, this.widgetFullName, this );
565 this._on( true, this.element, {
566 remove: function( event ) {
567 if ( event.target === element ) {
568 this.destroy();
569 }
570 }
571 });
572 this.document = $( element.style ?
573 // element within the document
574 element.ownerDocument :
575 // element is window or document
576 element.document || element );
577 this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
578 }
579
580 this.options = $.widget.extend( {},
581 this.options,
582 this._getCreateOptions(),
583 options );
584
585 this._create();
586 this._trigger( "create", null, this._getCreateEventData() );
587 this._init();
588 },
589 _getCreateOptions: $.noop,
590 _getCreateEventData: $.noop,
591 _create: $.noop,
592 _init: $.noop,
593
594 destroy: function() {
595 this._destroy();
596 // we can probably remove the unbind calls in 2.0
597 // all event bindings should go through this._on()
598 this.element
599 .unbind( this.eventNamespace )
600 .removeData( this.widgetFullName )
601 // support: jquery <1.6.3
602 // http://bugs.jquery.com/ticket/9413
603 .removeData( $.camelCase( this.widgetFullName ) );
604 this.widget()
605 .unbind( this.eventNamespace )
606 .removeAttr( "aria-disabled" )
607 .removeClass(
608 this.widgetFullName + "-disabled " +
609 "ui-state-disabled" );
610
611 // clean up events and states
612 this.bindings.unbind( this.eventNamespace );
613 this.hoverable.removeClass( "ui-state-hover" );
614 this.focusable.removeClass( "ui-state-focus" );
615 },
616 _destroy: $.noop,
617
618 widget: function() {
619 return this.element;
620 },
621
622 option: function( key, value ) {
623 var options = key,
624 parts,
625 curOption,
626 i;
627
628 if ( arguments.length === 0 ) {
629 // don't return a reference to the internal hash
630 return $.widget.extend( {}, this.options );
631 }
632
633 if ( typeof key === "string" ) {
634 // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
635 options = {};
636 parts = key.split( "." );
637 key = parts.shift();
638 if ( parts.length ) {
639 curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
640 for ( i = 0; i < parts.length - 1; i++ ) {
641 curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
642 curOption = curOption[ parts[ i ] ];
643 }
644 key = parts.pop();
645 if ( arguments.length === 1 ) {
646 return curOption[ key ] === undefined ? null : curOption[ key ];
647 }
648 curOption[ key ] = value;
649 } else {
650 if ( arguments.length === 1 ) {
651 return this.options[ key ] === undefined ? null : this.options[ key ];
652 }
653 options[ key ] = value;
654 }
655 }
656
657 this._setOptions( options );
658
659 return this;
660 },
661 _setOptions: function( options ) {
662 var key;
663
664 for ( key in options ) {
665 this._setOption( key, options[ key ] );
666 }
667
668 return this;
669 },
670 _setOption: function( key, value ) {
671 this.options[ key ] = value;
672
673 if ( key === "disabled" ) {
674 this.widget()
675 .toggleClass( this.widgetFullName + "-disabled", !!value );
676
677 // If the widget is becoming disabled, then nothing is interactive
678 if ( value ) {
679 this.hoverable.removeClass( "ui-state-hover" );
680 this.focusable.removeClass( "ui-state-focus" );
681 }
682 }
683
684 return this;
685 },
686
687 enable: function() {
688 return this._setOptions({ disabled: false });
689 },
690 disable: function() {
691 return this._setOptions({ disabled: true });
692 },
693
694 _on: function( suppressDisabledCheck, element, handlers ) {
695 var delegateElement,
696 instance = this;
697
698 // no suppressDisabledCheck flag, shuffle arguments
699 if ( typeof suppressDisabledCheck !== "boolean" ) {
700 handlers = element;
701 element = suppressDisabledCheck;
702 suppressDisabledCheck = false;
703 }
704
705 // no element argument, shuffle and use this.element
706 if ( !handlers ) {
707 handlers = element;
708 element = this.element;
709 delegateElement = this.widget();
710 } else {
711 element = delegateElement = $( element );
712 this.bindings = this.bindings.add( element );
713 }
714
715 $.each( handlers, function( event, handler ) {
716 function handlerProxy() {
717 // allow widgets to customize the disabled handling
718 // - disabled as an array instead of boolean
719 // - disabled class as method for disabling individual parts
720 if ( !suppressDisabledCheck &&
721 ( instance.options.disabled === true ||
722 $( this ).hasClass( "ui-state-disabled" ) ) ) {
723 return;
724 }
725 return ( typeof handler === "string" ? instance[ handler ] : handler )
726 .apply( instance, arguments );
727 }
728
729 // copy the guid so direct unbinding works
730 if ( typeof handler !== "string" ) {
731 handlerProxy.guid = handler.guid =
732 handler.guid || handlerProxy.guid || $.guid++;
733 }
734
735 var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
736 eventName = match[1] + instance.eventNamespace,
737 selector = match[2];
738 if ( selector ) {
739 delegateElement.delegate( selector, eventName, handlerProxy );
740 } else {
741 element.bind( eventName, handlerProxy );
742 }
743 });
744 },
745
746 _off: function( element, eventName ) {
747 eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) +
748 this.eventNamespace;
749 element.unbind( eventName ).undelegate( eventName );
750
751 // Clear the stack to avoid memory leaks (#10056)
752 this.bindings = $( this.bindings.not( element ).get() );
753 this.focusable = $( this.focusable.not( element ).get() );
754 this.hoverable = $( this.hoverable.not( element ).get() );
755 },
756
757 _delay: function( handler, delay ) {
758 function handlerProxy() {
759 return ( typeof handler === "string" ? instance[ handler ] : handler )
760 .apply( instance, arguments );
761 }
762 var instance = this;
763 return setTimeout( handlerProxy, delay || 0 );
764 },
765
766 _hoverable: function( element ) {
767 this.hoverable = this.hoverable.add( element );
768 this._on( element, {
769 mouseenter: function( event ) {
770 $( event.currentTarget ).addClass( "ui-state-hover" );
771 },
772 mouseleave: function( event ) {
773 $( event.currentTarget ).removeClass( "ui-state-hover" );
774 }
775 });
776 },
777
778 _focusable: function( element ) {
779 this.focusable = this.focusable.add( element );
780 this._on( element, {
781 focusin: function( event ) {
782 $( event.currentTarget ).addClass( "ui-state-focus" );
783 },
784 focusout: function( event ) {
785 $( event.currentTarget ).removeClass( "ui-state-focus" );
786 }
787 });
788 },
789
790 _trigger: function( type, event, data ) {
791 var prop, orig,
792 callback = this.options[ type ];
793
794 data = data || {};
795 event = $.Event( event );
796 event.type = ( type === this.widgetEventPrefix ?
797 type :
798 this.widgetEventPrefix + type ).toLowerCase();
799 // the original event may come from any element
800 // so we need to reset the target on the new event
801 event.target = this.element[ 0 ];
802
803 // copy original event properties over to the new event
804 orig = event.originalEvent;
805 if ( orig ) {
806 for ( prop in orig ) {
807 if ( !( prop in event ) ) {
808 event[ prop ] = orig[ prop ];
809 }
810 }
811 }
812
813 this.element.trigger( event, data );
814 return !( $.isFunction( callback ) &&
815 callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
816 event.isDefaultPrevented() );
817 }
818};
819
820$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
821 $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
822 if ( typeof options === "string" ) {
823 options = { effect: options };
824 }
825 var hasOptions,
826 effectName = !options ?
827 method :
828 options === true || typeof options === "number" ?
829 defaultEffect :
830 options.effect || defaultEffect;
831 options = options || {};
832 if ( typeof options === "number" ) {
833 options = { duration: options };
834 }
835 hasOptions = !$.isEmptyObject( options );
836 options.complete = callback;
837 if ( options.delay ) {
838 element.delay( options.delay );
839 }
840 if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
841 element[ method ]( options );
842 } else if ( effectName !== method && element[ effectName ] ) {
843 element[ effectName ]( options.duration, options.easing, callback );
844 } else {
845 element.queue(function( next ) {
846 $( this )[ method ]();
847 if ( callback ) {
848 callback.call( element[ 0 ] );
849 }
850 next();
851 });
852 }
853 };
854});
855
856var widget = $.widget;
857
858
859/*!
860 * jQuery UI Mouse 1.11.4
861 * http://jqueryui.com
862 *
863 * Copyright jQuery Foundation and other contributors
864 * Released under the MIT license.
865 * http://jquery.org/license
866 *
867 * http://api.jqueryui.com/mouse/
868 */
869
870
871var mouseHandled = false;
872$( document ).mouseup( function() {
873 mouseHandled = false;
874});
875
876var mouse = $.widget("ui.mouse", {
877 version: "1.11.4",
878 options: {
879 cancel: "input,textarea,button,select,option",
880 distance: 1,
881 delay: 0
882 },
883 _mouseInit: function() {
884 var that = this;
885
886 this.element
887 .bind("mousedown." + this.widgetName, function(event) {
888 return that._mouseDown(event);
889 })
890 .bind("click." + this.widgetName, function(event) {
891 if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
892 $.removeData(event.target, that.widgetName + ".preventClickEvent");
893 event.stopImmediatePropagation();
894 return false;
895 }
896 });
897
898 this.started = false;
899 },
900
901 // TODO: make sure destroying one instance of mouse doesn't mess with
902 // other instances of mouse
903 _mouseDestroy: function() {
904 this.element.unbind("." + this.widgetName);
905 if ( this._mouseMoveDelegate ) {
906 this.document
907 .unbind("mousemove." + this.widgetName, this._mouseMoveDelegate)
908 .unbind("mouseup." + this.widgetName, this._mouseUpDelegate);
909 }
910 },
911
912 _mouseDown: function(event) {
913 // don't let more than one widget handle mouseStart
914 if ( mouseHandled ) {
915 return;
916 }
917
918 this._mouseMoved = false;
919
920 // we may have missed mouseup (out of window)
921 (this._mouseStarted && this._mouseUp(event));
922
923 this._mouseDownEvent = event;
924
925 var that = this,
926 btnIsLeft = (event.which === 1),
927 // event.target.nodeName works around a bug in IE 8 with
928 // disabled inputs (#7620)
929 elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
930 if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
931 return true;
932 }
933
934 this.mouseDelayMet = !this.options.delay;
935 if (!this.mouseDelayMet) {
936 this._mouseDelayTimer = setTimeout(function() {
937 that.mouseDelayMet = true;
938 }, this.options.delay);
939 }
940
941 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
942 this._mouseStarted = (this._mouseStart(event) !== false);
943 if (!this._mouseStarted) {
944 event.preventDefault();
945 return true;
946 }
947 }
948
949 // Click event may never have fired (Gecko & Opera)
950 if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
951 $.removeData(event.target, this.widgetName + ".preventClickEvent");
952 }
953
954 // these delegates are required to keep context
955 this._mouseMoveDelegate = function(event) {
956 return that._mouseMove(event);
957 };
958 this._mouseUpDelegate = function(event) {
959 return that._mouseUp(event);
960 };
961
962 this.document
963 .bind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
964 .bind( "mouseup." + this.widgetName, this._mouseUpDelegate );
965
966 event.preventDefault();
967
968 mouseHandled = true;
969 return true;
970 },
971
972 _mouseMove: function(event) {
973 // Only check for mouseups outside the document if you've moved inside the document
974 // at least once. This prevents the firing of mouseup in the case of IE<9, which will
975 // fire a mousemove event if content is placed under the cursor. See #7778
976 // Support: IE <9
977 if ( this._mouseMoved ) {
978 // IE mouseup check - mouseup happened when mouse was out of window
979 if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
980 return this._mouseUp(event);
981
982 // Iframe mouseup check - mouseup occurred in another document
983 } else if ( !event.which ) {
984 return this._mouseUp( event );
985 }
986 }
987
988 if ( event.which || event.button ) {
989 this._mouseMoved = true;
990 }
991
992 if (this._mouseStarted) {
993 this._mouseDrag(event);
994 return event.preventDefault();
995 }
996
997 if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
998 this._mouseStarted =
999 (this._mouseStart(this._mouseDownEvent, event) !== false);
1000 (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
1001 }
1002
1003 return !this._mouseStarted;
1004 },
1005
1006 _mouseUp: function(event) {
1007 this.document
1008 .unbind( "mousemove." + this.widgetName, this._mouseMoveDelegate )
1009 .unbind( "mouseup." + this.widgetName, this._mouseUpDelegate );
1010
1011 if (this._mouseStarted) {
1012 this._mouseStarted = false;
1013
1014 if (event.target === this._mouseDownEvent.target) {
1015 $.data(event.target, this.widgetName + ".preventClickEvent", true);
1016 }
1017
1018 this._mouseStop(event);
1019 }
1020
1021 mouseHandled = false;
1022 return false;
1023 },
1024
1025 _mouseDistanceMet: function(event) {
1026 return (Math.max(
1027 Math.abs(this._mouseDownEvent.pageX - event.pageX),
1028 Math.abs(this._mouseDownEvent.pageY - event.pageY)
1029 ) >= this.options.distance
1030 );
1031 },
1032
1033 _mouseDelayMet: function(/* event */) {
1034 return this.mouseDelayMet;
1035 },
1036
1037 // These are placeholder methods, to be overriden by extending plugin
1038 _mouseStart: function(/* event */) {},
1039 _mouseDrag: function(/* event */) {},
1040 _mouseStop: function(/* event */) {},
1041 _mouseCapture: function(/* event */) { return true; }
1042});
1043
1044
1045/*!
1046 * jQuery UI Position 1.11.4
1047 * http://jqueryui.com
1048 *
1049 * Copyright jQuery Foundation and other contributors
1050 * Released under the MIT license.
1051 * http://jquery.org/license
1052 *
1053 * http://api.jqueryui.com/position/
1054 */
1055
1056(function() {
1057
1058$.ui = $.ui || {};
1059
1060var cachedScrollbarWidth, supportsOffsetFractions,
1061 max = Math.max,
1062 abs = Math.abs,
1063 round = Math.round,
1064 rhorizontal = /left|center|right/,
1065 rvertical = /top|center|bottom/,
1066 roffset = /[\+\-]\d+(\.[\d]+)?%?/,
1067 rposition = /^\w+/,
1068 rpercent = /%$/,
1069 _position = $.fn.position;
1070
1071function getOffsets( offsets, width, height ) {
1072 return [
1073 parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
1074 parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
1075 ];
1076}
1077
1078function parseCss( element, property ) {
1079 return parseInt( $.css( element, property ), 10 ) || 0;
1080}
1081
1082function getDimensions( elem ) {
1083 var raw = elem[0];
1084 if ( raw.nodeType === 9 ) {
1085 return {
1086 width: elem.width(),
1087 height: elem.height(),
1088 offset: { top: 0, left: 0 }
1089 };
1090 }
1091 if ( $.isWindow( raw ) ) {
1092 return {
1093 width: elem.width(),
1094 height: elem.height(),
1095 offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
1096 };
1097 }
1098 if ( raw.preventDefault ) {
1099 return {
1100 width: 0,
1101 height: 0,
1102 offset: { top: raw.pageY, left: raw.pageX }
1103 };
1104 }
1105 return {
1106 width: elem.outerWidth(),
1107 height: elem.outerHeight(),
1108 offset: elem.offset()
1109 };
1110}
1111
1112$.position = {
1113 scrollbarWidth: function() {
1114 if ( cachedScrollbarWidth !== undefined ) {
1115 return cachedScrollbarWidth;
1116 }
1117 var w1, w2,
1118 div = $( "<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
1119 innerDiv = div.children()[0];
1120
1121 $( "body" ).append( div );
1122 w1 = innerDiv.offsetWidth;
1123 div.css( "overflow", "scroll" );
1124
1125 w2 = innerDiv.offsetWidth;
1126
1127 if ( w1 === w2 ) {
1128 w2 = div[0].clientWidth;
1129 }
1130
1131 div.remove();
1132
1133 return (cachedScrollbarWidth = w1 - w2);
1134 },
1135 getScrollInfo: function( within ) {
1136 var overflowX = within.isWindow || within.isDocument ? "" :
1137 within.element.css( "overflow-x" ),
1138 overflowY = within.isWindow || within.isDocument ? "" :
1139 within.element.css( "overflow-y" ),
1140 hasOverflowX = overflowX === "scroll" ||
1141 ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
1142 hasOverflowY = overflowY === "scroll" ||
1143 ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
1144 return {
1145 width: hasOverflowY ? $.position.scrollbarWidth() : 0,
1146 height: hasOverflowX ? $.position.scrollbarWidth() : 0
1147 };
1148 },
1149 getWithinInfo: function( element ) {
1150 var withinElement = $( element || window ),
1151 isWindow = $.isWindow( withinElement[0] ),
1152 isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9;
1153 return {
1154 element: withinElement,
1155 isWindow: isWindow,
1156 isDocument: isDocument,
1157 offset: withinElement.offset() || { left: 0, top: 0 },
1158 scrollLeft: withinElement.scrollLeft(),
1159 scrollTop: withinElement.scrollTop(),
1160
1161 // support: jQuery 1.6.x
1162 // jQuery 1.6 doesn't support .outerWidth/Height() on documents or windows
1163 width: isWindow || isDocument ? withinElement.width() : withinElement.outerWidth(),
1164 height: isWindow || isDocument ? withinElement.height() : withinElement.outerHeight()
1165 };
1166 }
1167};
1168
1169$.fn.position = function( options ) {
1170 if ( !options || !options.of ) {
1171 return _position.apply( this, arguments );
1172 }
1173
1174 // make a copy, we don't want to modify arguments
1175 options = $.extend( {}, options );
1176
1177 var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
1178 target = $( options.of ),
1179 within = $.position.getWithinInfo( options.within ),
1180 scrollInfo = $.position.getScrollInfo( within ),
1181 collision = ( options.collision || "flip" ).split( " " ),
1182 offsets = {};
1183
1184 dimensions = getDimensions( target );
1185 if ( target[0].preventDefault ) {
1186 // force left top to allow flipping
1187 options.at = "left top";
1188 }
1189 targetWidth = dimensions.width;
1190 targetHeight = dimensions.height;
1191 targetOffset = dimensions.offset;
1192 // clone to reuse original targetOffset later
1193 basePosition = $.extend( {}, targetOffset );
1194
1195 // force my and at to have valid horizontal and vertical positions
1196 // if a value is missing or invalid, it will be converted to center
1197 $.each( [ "my", "at" ], function() {
1198 var pos = ( options[ this ] || "" ).split( " " ),
1199 horizontalOffset,
1200 verticalOffset;
1201
1202 if ( pos.length === 1) {
1203 pos = rhorizontal.test( pos[ 0 ] ) ?
1204 pos.concat( [ "center" ] ) :
1205 rvertical.test( pos[ 0 ] ) ?
1206 [ "center" ].concat( pos ) :
1207 [ "center", "center" ];
1208 }
1209 pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
1210 pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
1211
1212 // calculate offsets
1213 horizontalOffset = roffset.exec( pos[ 0 ] );
1214 verticalOffset = roffset.exec( pos[ 1 ] );
1215 offsets[ this ] = [
1216 horizontalOffset ? horizontalOffset[ 0 ] : 0,
1217 verticalOffset ? verticalOffset[ 0 ] : 0
1218 ];
1219
1220 // reduce to just the positions without the offsets
1221 options[ this ] = [
1222 rposition.exec( pos[ 0 ] )[ 0 ],
1223 rposition.exec( pos[ 1 ] )[ 0 ]
1224 ];
1225 });
1226
1227 // normalize collision option
1228 if ( collision.length === 1 ) {
1229 collision[ 1 ] = collision[ 0 ];
1230 }
1231
1232 if ( options.at[ 0 ] === "right" ) {
1233 basePosition.left += targetWidth;
1234 } else if ( options.at[ 0 ] === "center" ) {
1235 basePosition.left += targetWidth / 2;
1236 }
1237
1238 if ( options.at[ 1 ] === "bottom" ) {
1239 basePosition.top += targetHeight;
1240 } else if ( options.at[ 1 ] === "center" ) {
1241 basePosition.top += targetHeight / 2;
1242 }
1243
1244 atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
1245 basePosition.left += atOffset[ 0 ];
1246 basePosition.top += atOffset[ 1 ];
1247
1248 return this.each(function() {
1249 var collisionPosition, using,
1250 elem = $( this ),
1251 elemWidth = elem.outerWidth(),
1252 elemHeight = elem.outerHeight(),
1253 marginLeft = parseCss( this, "marginLeft" ),
1254 marginTop = parseCss( this, "marginTop" ),
1255 collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
1256 collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
1257 position = $.extend( {}, basePosition ),
1258 myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
1259
1260 if ( options.my[ 0 ] === "right" ) {
1261 position.left -= elemWidth;
1262 } else if ( options.my[ 0 ] === "center" ) {
1263 position.left -= elemWidth / 2;
1264 }
1265
1266 if ( options.my[ 1 ] === "bottom" ) {
1267 position.top -= elemHeight;
1268 } else if ( options.my[ 1 ] === "center" ) {
1269 position.top -= elemHeight / 2;
1270 }
1271
1272 position.left += myOffset[ 0 ];
1273 position.top += myOffset[ 1 ];
1274
1275 // if the browser doesn't support fractions, then round for consistent results
1276 if ( !supportsOffsetFractions ) {
1277 position.left = round( position.left );
1278 position.top = round( position.top );
1279 }
1280
1281 collisionPosition = {
1282 marginLeft: marginLeft,
1283 marginTop: marginTop
1284 };
1285
1286 $.each( [ "left", "top" ], function( i, dir ) {
1287 if ( $.ui.position[ collision[ i ] ] ) {
1288 $.ui.position[ collision[ i ] ][ dir ]( position, {
1289 targetWidth: targetWidth,
1290 targetHeight: targetHeight,
1291 elemWidth: elemWidth,
1292 elemHeight: elemHeight,
1293 collisionPosition: collisionPosition,
1294 collisionWidth: collisionWidth,
1295 collisionHeight: collisionHeight,
1296 offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
1297 my: options.my,
1298 at: options.at,
1299 within: within,
1300 elem: elem
1301 });
1302 }
1303 });
1304
1305 if ( options.using ) {
1306 // adds feedback as second argument to using callback, if present
1307 using = function( props ) {
1308 var left = targetOffset.left - position.left,
1309 right = left + targetWidth - elemWidth,
1310 top = targetOffset.top - position.top,
1311 bottom = top + targetHeight - elemHeight,
1312 feedback = {
1313 target: {
1314 element: target,
1315 left: targetOffset.left,
1316 top: targetOffset.top,
1317 width: targetWidth,
1318 height: targetHeight
1319 },
1320 element: {
1321 element: elem,
1322 left: position.left,
1323 top: position.top,
1324 width: elemWidth,
1325 height: elemHeight
1326 },
1327 horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
1328 vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
1329 };
1330 if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
1331 feedback.horizontal = "center";
1332 }
1333 if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
1334 feedback.vertical = "middle";
1335 }
1336 if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
1337 feedback.important = "horizontal";
1338 } else {
1339 feedback.important = "vertical";
1340 }
1341 options.using.call( this, props, feedback );
1342 };
1343 }
1344
1345 elem.offset( $.extend( position, { using: using } ) );
1346 });
1347};
1348
1349$.ui.position = {
1350 fit: {
1351 left: function( position, data ) {
1352 var within = data.within,
1353 withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
1354 outerWidth = within.width,
1355 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1356 overLeft = withinOffset - collisionPosLeft,
1357 overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
1358 newOverRight;
1359
1360 // element is wider than within
1361 if ( data.collisionWidth > outerWidth ) {
1362 // element is initially over the left side of within
1363 if ( overLeft > 0 && overRight <= 0 ) {
1364 newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
1365 position.left += overLeft - newOverRight;
1366 // element is initially over right side of within
1367 } else if ( overRight > 0 && overLeft <= 0 ) {
1368 position.left = withinOffset;
1369 // element is initially over both left and right sides of within
1370 } else {
1371 if ( overLeft > overRight ) {
1372 position.left = withinOffset + outerWidth - data.collisionWidth;
1373 } else {
1374 position.left = withinOffset;
1375 }
1376 }
1377 // too far left -> align with left edge
1378 } else if ( overLeft > 0 ) {
1379 position.left += overLeft;
1380 // too far right -> align with right edge
1381 } else if ( overRight > 0 ) {
1382 position.left -= overRight;
1383 // adjust based on position and margin
1384 } else {
1385 position.left = max( position.left - collisionPosLeft, position.left );
1386 }
1387 },
1388 top: function( position, data ) {
1389 var within = data.within,
1390 withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
1391 outerHeight = data.within.height,
1392 collisionPosTop = position.top - data.collisionPosition.marginTop,
1393 overTop = withinOffset - collisionPosTop,
1394 overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
1395 newOverBottom;
1396
1397 // element is taller than within
1398 if ( data.collisionHeight > outerHeight ) {
1399 // element is initially over the top of within
1400 if ( overTop > 0 && overBottom <= 0 ) {
1401 newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
1402 position.top += overTop - newOverBottom;
1403 // element is initially over bottom of within
1404 } else if ( overBottom > 0 && overTop <= 0 ) {
1405 position.top = withinOffset;
1406 // element is initially over both top and bottom of within
1407 } else {
1408 if ( overTop > overBottom ) {
1409 position.top = withinOffset + outerHeight - data.collisionHeight;
1410 } else {
1411 position.top = withinOffset;
1412 }
1413 }
1414 // too far up -> align with top
1415 } else if ( overTop > 0 ) {
1416 position.top += overTop;
1417 // too far down -> align with bottom edge
1418 } else if ( overBottom > 0 ) {
1419 position.top -= overBottom;
1420 // adjust based on position and margin
1421 } else {
1422 position.top = max( position.top - collisionPosTop, position.top );
1423 }
1424 }
1425 },
1426 flip: {
1427 left: function( position, data ) {
1428 var within = data.within,
1429 withinOffset = within.offset.left + within.scrollLeft,
1430 outerWidth = within.width,
1431 offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
1432 collisionPosLeft = position.left - data.collisionPosition.marginLeft,
1433 overLeft = collisionPosLeft - offsetLeft,
1434 overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
1435 myOffset = data.my[ 0 ] === "left" ?
1436 -data.elemWidth :
1437 data.my[ 0 ] === "right" ?
1438 data.elemWidth :
1439 0,
1440 atOffset = data.at[ 0 ] === "left" ?
1441 data.targetWidth :
1442 data.at[ 0 ] === "right" ?
1443 -data.targetWidth :
1444 0,
1445 offset = -2 * data.offset[ 0 ],
1446 newOverRight,
1447 newOverLeft;
1448
1449 if ( overLeft < 0 ) {
1450 newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
1451 if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
1452 position.left += myOffset + atOffset + offset;
1453 }
1454 } else if ( overRight > 0 ) {
1455 newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
1456 if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
1457 position.left += myOffset + atOffset + offset;
1458 }
1459 }
1460 },
1461 top: function( position, data ) {
1462 var within = data.within,
1463 withinOffset = within.offset.top + within.scrollTop,
1464 outerHeight = within.height,
1465 offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
1466 collisionPosTop = position.top - data.collisionPosition.marginTop,
1467 overTop = collisionPosTop - offsetTop,
1468 overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
1469 top = data.my[ 1 ] === "top",
1470 myOffset = top ?
1471 -data.elemHeight :
1472 data.my[ 1 ] === "bottom" ?
1473 data.elemHeight :
1474 0,
1475 atOffset = data.at[ 1 ] === "top" ?
1476 data.targetHeight :
1477 data.at[ 1 ] === "bottom" ?
1478 -data.targetHeight :
1479 0,
1480 offset = -2 * data.offset[ 1 ],
1481 newOverTop,
1482 newOverBottom;
1483 if ( overTop < 0 ) {
1484 newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
1485 if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
1486 position.top += myOffset + atOffset + offset;
1487 }
1488 } else if ( overBottom > 0 ) {
1489 newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
1490 if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
1491 position.top += myOffset + atOffset + offset;
1492 }
1493 }
1494 }
1495 },
1496 flipfit: {
1497 left: function() {
1498 $.ui.position.flip.left.apply( this, arguments );
1499 $.ui.position.fit.left.apply( this, arguments );
1500 },
1501 top: function() {
1502 $.ui.position.flip.top.apply( this, arguments );
1503 $.ui.position.fit.top.apply( this, arguments );
1504 }
1505 }
1506};
1507
1508// fraction support test
1509(function() {
1510 var testElement, testElementParent, testElementStyle, offsetLeft, i,
1511 body = document.getElementsByTagName( "body" )[ 0 ],
1512 div = document.createElement( "div" );
1513
1514 //Create a "fake body" for testing based on method used in jQuery.support
1515 testElement = document.createElement( body ? "div" : "body" );
1516 testElementStyle = {
1517 visibility: "hidden",
1518 width: 0,
1519 height: 0,
1520 border: 0,
1521 margin: 0,
1522 background: "none"
1523 };
1524 if ( body ) {
1525 $.extend( testElementStyle, {
1526 position: "absolute",
1527 left: "-1000px",
1528 top: "-1000px"
1529 });
1530 }
1531 for ( i in testElementStyle ) {
1532 testElement.style[ i ] = testElementStyle[ i ];
1533 }
1534 testElement.appendChild( div );
1535 testElementParent = body || document.documentElement;
1536 testElementParent.insertBefore( testElement, testElementParent.firstChild );
1537
1538 div.style.cssText = "position: absolute; left: 10.7432222px;";
1539
1540 offsetLeft = $( div ).offset().left;
1541 supportsOffsetFractions = offsetLeft > 10 && offsetLeft < 11;
1542
1543 testElement.innerHTML = "";
1544 testElementParent.removeChild( testElement );
1545})();
1546
1547})();
1548
1549var position = $.ui.position;
1550
1551
1552/*!
1553 * jQuery UI Draggable 1.11.4
1554 * http://jqueryui.com
1555 *
1556 * Copyright jQuery Foundation and other contributors
1557 * Released under the MIT license.
1558 * http://jquery.org/license
1559 *
1560 * http://api.jqueryui.com/draggable/
1561 */
1562
1563
1564$.widget("ui.draggable", $.ui.mouse, {
1565 version: "1.11.4",
1566 widgetEventPrefix: "drag",
1567 options: {
1568 addClasses: true,
1569 appendTo: "parent",
1570 axis: false,
1571 connectToSortable: false,
1572 containment: false,
1573 cursor: "auto",
1574 cursorAt: false,
1575 grid: false,
1576 handle: false,
1577 helper: "original",
1578 iframeFix: false,
1579 opacity: false,
1580 refreshPositions: false,
1581 revert: false,
1582 revertDuration: 500,
1583 scope: "default",
1584 scroll: true,
1585 scrollSensitivity: 20,
1586 scrollSpeed: 20,
1587 snap: false,
1588 snapMode: "both",
1589 snapTolerance: 20,
1590 stack: false,
1591 zIndex: false,
1592
1593 // callbacks
1594 drag: null,
1595 start: null,
1596 stop: null
1597 },
1598 _create: function() {
1599
1600 if ( this.options.helper === "original" ) {
1601 this._setPositionRelative();
1602 }
1603 if (this.options.addClasses){
1604 this.element.addClass("ui-draggable");
1605 }
1606 if (this.options.disabled){
1607 this.element.addClass("ui-draggable-disabled");
1608 }
1609 this._setHandleClassName();
1610
1611 this._mouseInit();
1612 },
1613
1614 _setOption: function( key, value ) {
1615 this._super( key, value );
1616 if ( key === "handle" ) {
1617 this._removeHandleClassName();
1618 this._setHandleClassName();
1619 }
1620 },
1621
1622 _destroy: function() {
1623 if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
1624 this.destroyOnClear = true;
1625 return;
1626 }
1627 this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
1628 this._removeHandleClassName();
1629 this._mouseDestroy();
1630 },
1631
1632 _mouseCapture: function(event) {
1633 var o = this.options;
1634
1635 this._blurActiveElement( event );
1636
1637 // among others, prevent a drag on a resizable-handle
1638 if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
1639 return false;
1640 }
1641
1642 //Quit if we're not on a valid handle
1643 this.handle = this._getHandle(event);
1644 if (!this.handle) {
1645 return false;
1646 }
1647
1648 this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
1649
1650 return true;
1651
1652 },
1653
1654 _blockFrames: function( selector ) {
1655 this.iframeBlocks = this.document.find( selector ).map(function() {
1656 var iframe = $( this );
1657
1658 return $( "<div>" )
1659 .css( "position", "absolute" )
1660 .appendTo( iframe.parent() )
1661 .outerWidth( iframe.outerWidth() )
1662 .outerHeight( iframe.outerHeight() )
1663 .offset( iframe.offset() )[ 0 ];
1664 });
1665 },
1666
1667 _unblockFrames: function() {
1668 if ( this.iframeBlocks ) {
1669 this.iframeBlocks.remove();
1670 delete this.iframeBlocks;
1671 }
1672 },
1673
1674 _blurActiveElement: function( event ) {
1675 var document = this.document[ 0 ];
1676
1677 // Only need to blur if the event occurred on the draggable itself, see #10527
1678 if ( !this.handleElement.is( event.target ) ) {
1679 return;
1680 }
1681
1682 // support: IE9
1683 // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe>
1684 try {
1685
1686 // Support: IE9, IE10
1687 // If the <body> is blurred, IE will switch windows, see #9520
1688 if ( document.activeElement && document.activeElement.nodeName.toLowerCase() !== "body" ) {
1689
1690 // Blur any element that currently has focus, see #4261
1691 $( document.activeElement ).blur();
1692 }
1693 } catch ( error ) {}
1694 },
1695
1696 _mouseStart: function(event) {
1697
1698 var o = this.options;
1699
1700 //Create and append the visible helper
1701 this.helper = this._createHelper(event);
1702
1703 this.helper.addClass("ui-draggable-dragging");
1704
1705 //Cache the helper size
1706 this._cacheHelperProportions();
1707
1708 //If ddmanager is used for droppables, set the global draggable
1709 if ($.ui.ddmanager) {
1710 $.ui.ddmanager.current = this;
1711 }
1712
1713 /*
1714 * - Position generation -
1715 * This block generates everything position related - it's the core of draggables.
1716 */
1717
1718 //Cache the margins of the original element
1719 this._cacheMargins();
1720
1721 //Store the helper's css position
1722 this.cssPosition = this.helper.css( "position" );
1723 this.scrollParent = this.helper.scrollParent( true );
1724 this.offsetParent = this.helper.offsetParent();
1725 this.hasFixedAncestor = this.helper.parents().filter(function() {
1726 return $( this ).css( "position" ) === "fixed";
1727 }).length > 0;
1728
1729 //The element's absolute position on the page minus margins
1730 this.positionAbs = this.element.offset();
1731 this._refreshOffsets( event );
1732
1733 //Generate the original position
1734 this.originalPosition = this.position = this._generatePosition( event, false );
1735 this.originalPageX = event.pageX;
1736 this.originalPageY = event.pageY;
1737
1738 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
1739 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
1740
1741 //Set a containment if given in the options
1742 this._setContainment();
1743
1744 //Trigger event + callbacks
1745 if (this._trigger("start", event) === false) {
1746 this._clear();
1747 return false;
1748 }
1749
1750 //Recache the helper size
1751 this._cacheHelperProportions();
1752
1753 //Prepare the droppable offsets
1754 if ($.ui.ddmanager && !o.dropBehaviour) {
1755 $.ui.ddmanager.prepareOffsets(this, event);
1756 }
1757
1758 // Reset helper's right/bottom css if they're set and set explicit width/height instead
1759 // as this prevents resizing of elements with right/bottom set (see #7772)
1760 this._normalizeRightBottom();
1761
1762 this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
1763
1764 //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
1765 if ( $.ui.ddmanager ) {
1766 $.ui.ddmanager.dragStart(this, event);
1767 }
1768
1769 return true;
1770 },
1771
1772 _refreshOffsets: function( event ) {
1773 this.offset = {
1774 top: this.positionAbs.top - this.margins.top,
1775 left: this.positionAbs.left - this.margins.left,
1776 scroll: false,
1777 parent: this._getParentOffset(),
1778 relative: this._getRelativeOffset()
1779 };
1780
1781 this.offset.click = {
1782 left: event.pageX - this.offset.left,
1783 top: event.pageY - this.offset.top
1784 };
1785 },
1786
1787 _mouseDrag: function(event, noPropagation) {
1788 // reset any necessary cached properties (see #5009)
1789 if ( this.hasFixedAncestor ) {
1790 this.offset.parent = this._getParentOffset();
1791 }
1792
1793 //Compute the helpers position
1794 this.position = this._generatePosition( event, true );
1795 this.positionAbs = this._convertPositionTo("absolute");
1796
1797 //Call plugins and callbacks and use the resulting position if something is returned
1798 if (!noPropagation) {
1799 var ui = this._uiHash();
1800 if (this._trigger("drag", event, ui) === false) {
1801 this._mouseUp({});
1802 return false;
1803 }
1804 this.position = ui.position;
1805 }
1806
1807 this.helper[ 0 ].style.left = this.position.left + "px";
1808 this.helper[ 0 ].style.top = this.position.top + "px";
1809
1810 if ($.ui.ddmanager) {
1811 $.ui.ddmanager.drag(this, event);
1812 }
1813
1814 return false;
1815 },
1816
1817 _mouseStop: function(event) {
1818
1819 //If we are using droppables, inform the manager about the drop
1820 var that = this,
1821 dropped = false;
1822 if ($.ui.ddmanager && !this.options.dropBehaviour) {
1823 dropped = $.ui.ddmanager.drop(this, event);
1824 }
1825
1826 //if a drop comes from outside (a sortable)
1827 if (this.dropped) {
1828 dropped = this.dropped;
1829 this.dropped = false;
1830 }
1831
1832 if ((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
1833 $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
1834 if (that._trigger("stop", event) !== false) {
1835 that._clear();
1836 }
1837 });
1838 } else {
1839 if (this._trigger("stop", event) !== false) {
1840 this._clear();
1841 }
1842 }
1843
1844 return false;
1845 },
1846
1847 _mouseUp: function( event ) {
1848 this._unblockFrames();
1849
1850 //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
1851 if ( $.ui.ddmanager ) {
1852 $.ui.ddmanager.dragStop(this, event);
1853 }
1854
1855 // Only need to focus if the event occurred on the draggable itself, see #10527
1856 if ( this.handleElement.is( event.target ) ) {
1857 // The interaction is over; whether or not the click resulted in a drag, focus the element
1858 this.element.focus();
1859 }
1860
1861 return $.ui.mouse.prototype._mouseUp.call(this, event);
1862 },
1863
1864 cancel: function() {
1865
1866 if (this.helper.is(".ui-draggable-dragging")) {
1867 this._mouseUp({});
1868 } else {
1869 this._clear();
1870 }
1871
1872 return this;
1873
1874 },
1875
1876 _getHandle: function(event) {
1877 return this.options.handle ?
1878 !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
1879 true;
1880 },
1881
1882 _setHandleClassName: function() {
1883 this.handleElement = this.options.handle ?
1884 this.element.find( this.options.handle ) : this.element;
1885 this.handleElement.addClass( "ui-draggable-handle" );
1886 },
1887
1888 _removeHandleClassName: function() {
1889 this.handleElement.removeClass( "ui-draggable-handle" );
1890 },
1891
1892 _createHelper: function(event) {
1893
1894 var o = this.options,
1895 helperIsFunction = $.isFunction( o.helper ),
1896 helper = helperIsFunction ?
1897 $( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
1898 ( o.helper === "clone" ?
1899 this.element.clone().removeAttr( "id" ) :
1900 this.element );
1901
1902 if (!helper.parents("body").length) {
1903 helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
1904 }
1905
1906 // http://bugs.jqueryui.com/ticket/9446
1907 // a helper function can return the original element
1908 // which wouldn't have been set to relative in _create
1909 if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
1910 this._setPositionRelative();
1911 }
1912
1913 if (helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
1914 helper.css("position", "absolute");
1915 }
1916
1917 return helper;
1918
1919 },
1920
1921 _setPositionRelative: function() {
1922 if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
1923 this.element[ 0 ].style.position = "relative";
1924 }
1925 },
1926
1927 _adjustOffsetFromHelper: function(obj) {
1928 if (typeof obj === "string") {
1929 obj = obj.split(" ");
1930 }
1931 if ($.isArray(obj)) {
1932 obj = { left: +obj[0], top: +obj[1] || 0 };
1933 }
1934 if ("left" in obj) {
1935 this.offset.click.left = obj.left + this.margins.left;
1936 }
1937 if ("right" in obj) {
1938 this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
1939 }
1940 if ("top" in obj) {
1941 this.offset.click.top = obj.top + this.margins.top;
1942 }
1943 if ("bottom" in obj) {
1944 this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
1945 }
1946 },
1947
1948 _isRootNode: function( element ) {
1949 return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
1950 },
1951
1952 _getParentOffset: function() {
1953
1954 //Get the offsetParent and cache its position
1955 var po = this.offsetParent.offset(),
1956 document = this.document[ 0 ];
1957
1958 // This is a special case where we need to modify a offset calculated on start, since the following happened:
1959 // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
1960 // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
1961 // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
1962 if (this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
1963 po.left += this.scrollParent.scrollLeft();
1964 po.top += this.scrollParent.scrollTop();
1965 }
1966
1967 if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
1968 po = { top: 0, left: 0 };
1969 }
1970
1971 return {
1972 top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"), 10) || 0),
1973 left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"), 10) || 0)
1974 };
1975
1976 },
1977
1978 _getRelativeOffset: function() {
1979 if ( this.cssPosition !== "relative" ) {
1980 return { top: 0, left: 0 };
1981 }
1982
1983 var p = this.element.position(),
1984 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
1985
1986 return {
1987 top: p.top - ( parseInt(this.helper.css( "top" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
1988 left: p.left - ( parseInt(this.helper.css( "left" ), 10) || 0 ) + ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
1989 };
1990
1991 },
1992
1993 _cacheMargins: function() {
1994 this.margins = {
1995 left: (parseInt(this.element.css("marginLeft"), 10) || 0),
1996 top: (parseInt(this.element.css("marginTop"), 10) || 0),
1997 right: (parseInt(this.element.css("marginRight"), 10) || 0),
1998 bottom: (parseInt(this.element.css("marginBottom"), 10) || 0)
1999 };
2000 },
2001
2002 _cacheHelperProportions: function() {
2003 this.helperProportions = {
2004 width: this.helper.outerWidth(),
2005 height: this.helper.outerHeight()
2006 };
2007 },
2008
2009 _setContainment: function() {
2010
2011 var isUserScrollable, c, ce,
2012 o = this.options,
2013 document = this.document[ 0 ];
2014
2015 this.relativeContainer = null;
2016
2017 if ( !o.containment ) {
2018 this.containment = null;
2019 return;
2020 }
2021
2022 if ( o.containment === "window" ) {
2023 this.containment = [
2024 $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
2025 $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
2026 $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
2027 $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
2028 ];
2029 return;
2030 }
2031
2032 if ( o.containment === "document") {
2033 this.containment = [
2034 0,
2035 0,
2036 $( document ).width() - this.helperProportions.width - this.margins.left,
2037 ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
2038 ];
2039 return;
2040 }
2041
2042 if ( o.containment.constructor === Array ) {
2043 this.containment = o.containment;
2044 return;
2045 }
2046
2047 if ( o.containment === "parent" ) {
2048 o.containment = this.helper[ 0 ].parentNode;
2049 }
2050
2051 c = $( o.containment );
2052 ce = c[ 0 ];
2053
2054 if ( !ce ) {
2055 return;
2056 }
2057
2058 isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
2059
2060 this.containment = [
2061 ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
2062 ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
2063 ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
2064 ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
2065 ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
2066 this.helperProportions.width -
2067 this.margins.left -
2068 this.margins.right,
2069 ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
2070 ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
2071 ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
2072 this.helperProportions.height -
2073 this.margins.top -
2074 this.margins.bottom
2075 ];
2076 this.relativeContainer = c;
2077 },
2078
2079 _convertPositionTo: function(d, pos) {
2080
2081 if (!pos) {
2082 pos = this.position;
2083 }
2084
2085 var mod = d === "absolute" ? 1 : -1,
2086 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
2087
2088 return {
2089 top: (
2090 pos.top + // The absolute mouse position
2091 this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
2092 this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
2093 ( ( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod)
2094 ),
2095 left: (
2096 pos.left + // The absolute mouse position
2097 this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
2098 this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
2099 ( ( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod)
2100 )
2101 };
2102
2103 },
2104
2105 _generatePosition: function( event, constrainPosition ) {
2106
2107 var containment, co, top, left,
2108 o = this.options,
2109 scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
2110 pageX = event.pageX,
2111 pageY = event.pageY;
2112
2113 // Cache the scroll
2114 if ( !scrollIsRootNode || !this.offset.scroll ) {
2115 this.offset.scroll = {
2116 top: this.scrollParent.scrollTop(),
2117 left: this.scrollParent.scrollLeft()
2118 };
2119 }
2120
2121 /*
2122 * - Position constraining -
2123 * Constrain the position to a mix of grid, containment.
2124 */
2125
2126 // If we are not dragging yet, we won't check for options
2127 if ( constrainPosition ) {
2128 if ( this.containment ) {
2129 if ( this.relativeContainer ){
2130 co = this.relativeContainer.offset();
2131 containment = [
2132 this.containment[ 0 ] + co.left,
2133 this.containment[ 1 ] + co.top,
2134 this.containment[ 2 ] + co.left,
2135 this.containment[ 3 ] + co.top
2136 ];
2137 } else {
2138 containment = this.containment;
2139 }
2140
2141 if (event.pageX - this.offset.click.left < containment[0]) {
2142 pageX = containment[0] + this.offset.click.left;
2143 }
2144 if (event.pageY - this.offset.click.top < containment[1]) {
2145 pageY = containment[1] + this.offset.click.top;
2146 }
2147 if (event.pageX - this.offset.click.left > containment[2]) {
2148 pageX = containment[2] + this.offset.click.left;
2149 }
2150 if (event.pageY - this.offset.click.top > containment[3]) {
2151 pageY = containment[3] + this.offset.click.top;
2152 }
2153 }
2154
2155 if (o.grid) {
2156 //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
2157 top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
2158 pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
2159
2160 left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
2161 pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
2162 }
2163
2164 if ( o.axis === "y" ) {
2165 pageX = this.originalPageX;
2166 }
2167
2168 if ( o.axis === "x" ) {
2169 pageY = this.originalPageY;
2170 }
2171 }
2172
2173 return {
2174 top: (
2175 pageY - // The absolute mouse position
2176 this.offset.click.top - // Click offset (relative to the element)
2177 this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
2178 this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
2179 ( this.cssPosition === "fixed" ? -this.offset.scroll.top : ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
2180 ),
2181 left: (
2182 pageX - // The absolute mouse position
2183 this.offset.click.left - // Click offset (relative to the element)
2184 this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
2185 this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
2186 ( this.cssPosition === "fixed" ? -this.offset.scroll.left : ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
2187 )
2188 };
2189
2190 },
2191
2192 _clear: function() {
2193 this.helper.removeClass("ui-draggable-dragging");
2194 if (this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
2195 this.helper.remove();
2196 }
2197 this.helper = null;
2198 this.cancelHelperRemoval = false;
2199 if ( this.destroyOnClear ) {
2200 this.destroy();
2201 }
2202 },
2203
2204 _normalizeRightBottom: function() {
2205 if ( this.options.axis !== "y" && this.helper.css( "right" ) !== "auto" ) {
2206 this.helper.width( this.helper.width() );
2207 this.helper.css( "right", "auto" );
2208 }
2209 if ( this.options.axis !== "x" && this.helper.css( "bottom" ) !== "auto" ) {
2210 this.helper.height( this.helper.height() );
2211 this.helper.css( "bottom", "auto" );
2212 }
2213 },
2214
2215 // From now on bulk stuff - mainly helpers
2216
2217 _trigger: function( type, event, ui ) {
2218 ui = ui || this._uiHash();
2219 $.ui.plugin.call( this, type, [ event, ui, this ], true );
2220
2221 // Absolute position and offset (see #6884 ) have to be recalculated after plugins
2222 if ( /^(drag|start|stop)/.test( type ) ) {
2223 this.positionAbs = this._convertPositionTo( "absolute" );
2224 ui.offset = this.positionAbs;
2225 }
2226 return $.Widget.prototype._trigger.call( this, type, event, ui );
2227 },
2228
2229 plugins: {},
2230
2231 _uiHash: function() {
2232 return {
2233 helper: this.helper,
2234 position: this.position,
2235 originalPosition: this.originalPosition,
2236 offset: this.positionAbs
2237 };
2238 }
2239
2240});
2241
2242$.ui.plugin.add( "draggable", "connectToSortable", {
2243 start: function( event, ui, draggable ) {
2244 var uiSortable = $.extend( {}, ui, {
2245 item: draggable.element
2246 });
2247
2248 draggable.sortables = [];
2249 $( draggable.options.connectToSortable ).each(function() {
2250 var sortable = $( this ).sortable( "instance" );
2251
2252 if ( sortable && !sortable.options.disabled ) {
2253 draggable.sortables.push( sortable );
2254
2255 // refreshPositions is called at drag start to refresh the containerCache
2256 // which is used in drag. This ensures it's initialized and synchronized
2257 // with any changes that might have happened on the page since initialization.
2258 sortable.refreshPositions();
2259 sortable._trigger("activate", event, uiSortable);
2260 }
2261 });
2262 },
2263 stop: function( event, ui, draggable ) {
2264 var uiSortable = $.extend( {}, ui, {
2265 item: draggable.element
2266 });
2267
2268 draggable.cancelHelperRemoval = false;
2269
2270 $.each( draggable.sortables, function() {
2271 var sortable = this;
2272
2273 if ( sortable.isOver ) {
2274 sortable.isOver = 0;
2275
2276 // Allow this sortable to handle removing the helper
2277 draggable.cancelHelperRemoval = true;
2278 sortable.cancelHelperRemoval = false;
2279
2280 // Use _storedCSS To restore properties in the sortable,
2281 // as this also handles revert (#9675) since the draggable
2282 // may have modified them in unexpected ways (#8809)
2283 sortable._storedCSS = {
2284 position: sortable.placeholder.css( "position" ),
2285 top: sortable.placeholder.css( "top" ),
2286 left: sortable.placeholder.css( "left" )
2287 };
2288
2289 sortable._mouseStop(event);
2290
2291 // Once drag has ended, the sortable should return to using
2292 // its original helper, not the shared helper from draggable
2293 sortable.options.helper = sortable.options._helper;
2294 } else {
2295 // Prevent this Sortable from removing the helper.
2296 // However, don't set the draggable to remove the helper
2297 // either as another connected Sortable may yet handle the removal.
2298 sortable.cancelHelperRemoval = true;
2299
2300 sortable._trigger( "deactivate", event, uiSortable );
2301 }
2302 });
2303 },
2304 drag: function( event, ui, draggable ) {
2305 $.each( draggable.sortables, function() {
2306 var innermostIntersecting = false,
2307 sortable = this;
2308
2309 // Copy over variables that sortable's _intersectsWith uses
2310 sortable.positionAbs = draggable.positionAbs;
2311 sortable.helperProportions = draggable.helperProportions;
2312 sortable.offset.click = draggable.offset.click;
2313
2314 if ( sortable._intersectsWith( sortable.containerCache ) ) {
2315 innermostIntersecting = true;
2316
2317 $.each( draggable.sortables, function() {
2318 // Copy over variables that sortable's _intersectsWith uses
2319 this.positionAbs = draggable.positionAbs;
2320 this.helperProportions = draggable.helperProportions;
2321 this.offset.click = draggable.offset.click;
2322
2323 if ( this !== sortable &&
2324 this._intersectsWith( this.containerCache ) &&
2325 $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
2326 innermostIntersecting = false;
2327 }
2328
2329 return innermostIntersecting;
2330 });
2331 }
2332
2333 if ( innermostIntersecting ) {
2334 // If it intersects, we use a little isOver variable and set it once,
2335 // so that the move-in stuff gets fired only once.
2336 if ( !sortable.isOver ) {
2337 sortable.isOver = 1;
2338
2339 // Store draggable's parent in case we need to reappend to it later.
2340 draggable._parent = ui.helper.parent();
2341
2342 sortable.currentItem = ui.helper
2343 .appendTo( sortable.element )
2344 .data( "ui-sortable-item", true );
2345
2346 // Store helper option to later restore it
2347 sortable.options._helper = sortable.options.helper;
2348
2349 sortable.options.helper = function() {
2350 return ui.helper[ 0 ];
2351 };
2352
2353 // Fire the start events of the sortable with our passed browser event,
2354 // and our own helper (so it doesn't create a new one)
2355 event.target = sortable.currentItem[ 0 ];
2356 sortable._mouseCapture( event, true );
2357 sortable._mouseStart( event, true, true );
2358
2359 // Because the browser event is way off the new appended portlet,
2360 // modify necessary variables to reflect the changes
2361 sortable.offset.click.top = draggable.offset.click.top;
2362 sortable.offset.click.left = draggable.offset.click.left;
2363 sortable.offset.parent.left -= draggable.offset.parent.left -
2364 sortable.offset.parent.left;
2365 sortable.offset.parent.top -= draggable.offset.parent.top -
2366 sortable.offset.parent.top;
2367
2368 draggable._trigger( "toSortable", event );
2369
2370 // Inform draggable that the helper is in a valid drop zone,
2371 // used solely in the revert option to handle "valid/invalid".
2372 draggable.dropped = sortable.element;
2373
2374 // Need to refreshPositions of all sortables in the case that
2375 // adding to one sortable changes the location of the other sortables (#9675)
2376 $.each( draggable.sortables, function() {
2377 this.refreshPositions();
2378 });
2379
2380 // hack so receive/update callbacks work (mostly)
2381 draggable.currentItem = draggable.element;
2382 sortable.fromOutside = draggable;
2383 }
2384
2385 if ( sortable.currentItem ) {
2386 sortable._mouseDrag( event );
2387 // Copy the sortable's position because the draggable's can potentially reflect
2388 // a relative position, while sortable is always absolute, which the dragged
2389 // element has now become. (#8809)
2390 ui.position = sortable.position;
2391 }
2392 } else {
2393 // If it doesn't intersect with the sortable, and it intersected before,
2394 // we fake the drag stop of the sortable, but make sure it doesn't remove
2395 // the helper by using cancelHelperRemoval.
2396 if ( sortable.isOver ) {
2397
2398 sortable.isOver = 0;
2399 sortable.cancelHelperRemoval = true;
2400
2401 // Calling sortable's mouseStop would trigger a revert,
2402 // so revert must be temporarily false until after mouseStop is called.
2403 sortable.options._revert = sortable.options.revert;
2404 sortable.options.revert = false;
2405
2406 sortable._trigger( "out", event, sortable._uiHash( sortable ) );
2407 sortable._mouseStop( event, true );
2408
2409 // restore sortable behaviors that were modfied
2410 // when the draggable entered the sortable area (#9481)
2411 sortable.options.revert = sortable.options._revert;
2412 sortable.options.helper = sortable.options._helper;
2413
2414 if ( sortable.placeholder ) {
2415 sortable.placeholder.remove();
2416 }
2417
2418 // Restore and recalculate the draggable's offset considering the sortable
2419 // may have modified them in unexpected ways. (#8809, #10669)
2420 ui.helper.appendTo( draggable._parent );
2421 draggable._refreshOffsets( event );
2422 ui.position = draggable._generatePosition( event, true );
2423
2424 draggable._trigger( "fromSortable", event );
2425
2426 // Inform draggable that the helper is no longer in a valid drop zone
2427 draggable.dropped = false;
2428
2429 // Need to refreshPositions of all sortables just in case removing
2430 // from one sortable changes the location of other sortables (#9675)
2431 $.each( draggable.sortables, function() {
2432 this.refreshPositions();
2433 });
2434 }
2435 }
2436 });
2437 }
2438});
2439
2440$.ui.plugin.add("draggable", "cursor", {
2441 start: function( event, ui, instance ) {
2442 var t = $( "body" ),
2443 o = instance.options;
2444
2445 if (t.css("cursor")) {
2446 o._cursor = t.css("cursor");
2447 }
2448 t.css("cursor", o.cursor);
2449 },
2450 stop: function( event, ui, instance ) {
2451 var o = instance.options;
2452 if (o._cursor) {
2453 $("body").css("cursor", o._cursor);
2454 }
2455 }
2456});
2457
2458$.ui.plugin.add("draggable", "opacity", {
2459 start: function( event, ui, instance ) {
2460 var t = $( ui.helper ),
2461 o = instance.options;
2462 if (t.css("opacity")) {
2463 o._opacity = t.css("opacity");
2464 }
2465 t.css("opacity", o.opacity);
2466 },
2467 stop: function( event, ui, instance ) {
2468 var o = instance.options;
2469 if (o._opacity) {
2470 $(ui.helper).css("opacity", o._opacity);
2471 }
2472 }
2473});
2474
2475$.ui.plugin.add("draggable", "scroll", {
2476 start: function( event, ui, i ) {
2477 if ( !i.scrollParentNotHidden ) {
2478 i.scrollParentNotHidden = i.helper.scrollParent( false );
2479 }
2480
2481 if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] && i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
2482 i.overflowOffset = i.scrollParentNotHidden.offset();
2483 }
2484 },
2485 drag: function( event, ui, i ) {
2486
2487 var o = i.options,
2488 scrolled = false,
2489 scrollParent = i.scrollParentNotHidden[ 0 ],
2490 document = i.document[ 0 ];
2491
2492 if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
2493 if ( !o.axis || o.axis !== "x" ) {
2494 if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY < o.scrollSensitivity ) {
2495 scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
2496 } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
2497 scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
2498 }
2499 }
2500
2501 if ( !o.axis || o.axis !== "y" ) {
2502 if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX < o.scrollSensitivity ) {
2503 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
2504 } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
2505 scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
2506 }
2507 }
2508
2509 } else {
2510
2511 if (!o.axis || o.axis !== "x") {
2512 if (event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
2513 scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
2514 } else if ($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
2515 scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
2516 }
2517 }
2518
2519 if (!o.axis || o.axis !== "y") {
2520 if (event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
2521 scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
2522 } else if ($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
2523 scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
2524 }
2525 }
2526
2527 }
2528
2529 if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
2530 $.ui.ddmanager.prepareOffsets(i, event);
2531 }
2532
2533 }
2534});
2535
2536$.ui.plugin.add("draggable", "snap", {
2537 start: function( event, ui, i ) {
2538
2539 var o = i.options;
2540
2541 i.snapElements = [];
2542
2543 $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
2544 var $t = $(this),
2545 $o = $t.offset();
2546 if (this !== i.element[0]) {
2547 i.snapElements.push({
2548 item: this,
2549 width: $t.outerWidth(), height: $t.outerHeight(),
2550 top: $o.top, left: $o.left
2551 });
2552 }
2553 });
2554
2555 },
2556 drag: function( event, ui, inst ) {
2557
2558 var ts, bs, ls, rs, l, r, t, b, i, first,
2559 o = inst.options,
2560 d = o.snapTolerance,
2561 x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
2562 y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
2563
2564 for (i = inst.snapElements.length - 1; i >= 0; i--){
2565
2566 l = inst.snapElements[i].left - inst.margins.left;
2567 r = l + inst.snapElements[i].width;
2568 t = inst.snapElements[i].top - inst.margins.top;
2569 b = t + inst.snapElements[i].height;
2570
2571 if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
2572 if (inst.snapElements[i].snapping) {
2573 (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2574 }
2575 inst.snapElements[i].snapping = false;
2576 continue;
2577 }
2578
2579 if (o.snapMode !== "inner") {
2580 ts = Math.abs(t - y2) <= d;
2581 bs = Math.abs(b - y1) <= d;
2582 ls = Math.abs(l - x2) <= d;
2583 rs = Math.abs(r - x1) <= d;
2584 if (ts) {
2585 ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top;
2586 }
2587 if (bs) {
2588 ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top;
2589 }
2590 if (ls) {
2591 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left;
2592 }
2593 if (rs) {
2594 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left;
2595 }
2596 }
2597
2598 first = (ts || bs || ls || rs);
2599
2600 if (o.snapMode !== "outer") {
2601 ts = Math.abs(t - y1) <= d;
2602 bs = Math.abs(b - y2) <= d;
2603 ls = Math.abs(l - x1) <= d;
2604 rs = Math.abs(r - x2) <= d;
2605 if (ts) {
2606 ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top;
2607 }
2608 if (bs) {
2609 ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top;
2610 }
2611 if (ls) {
2612 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left;
2613 }
2614 if (rs) {
2615 ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left;
2616 }
2617 }
2618
2619 if (!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
2620 (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
2621 }
2622 inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
2623
2624 }
2625
2626 }
2627});
2628
2629$.ui.plugin.add("draggable", "stack", {
2630 start: function( event, ui, instance ) {
2631 var min,
2632 o = instance.options,
2633 group = $.makeArray($(o.stack)).sort(function(a, b) {
2634 return (parseInt($(a).css("zIndex"), 10) || 0) - (parseInt($(b).css("zIndex"), 10) || 0);
2635 });
2636
2637 if (!group.length) { return; }
2638
2639 min = parseInt($(group[0]).css("zIndex"), 10) || 0;
2640 $(group).each(function(i) {
2641 $(this).css("zIndex", min + i);
2642 });
2643 this.css("zIndex", (min + group.length));
2644 }
2645});
2646
2647$.ui.plugin.add("draggable", "zIndex", {
2648 start: function( event, ui, instance ) {
2649 var t = $( ui.helper ),
2650 o = instance.options;
2651
2652 if (t.css("zIndex")) {
2653 o._zIndex = t.css("zIndex");
2654 }
2655 t.css("zIndex", o.zIndex);
2656 },
2657 stop: function( event, ui, instance ) {
2658 var o = instance.options;
2659
2660 if (o._zIndex) {
2661 $(ui.helper).css("zIndex", o._zIndex);
2662 }
2663 }
2664});
2665
2666var draggable = $.ui.draggable;
2667
2668
2669/*!
2670 * jQuery UI Droppable 1.11.4
2671 * http://jqueryui.com
2672 *
2673 * Copyright jQuery Foundation and other contributors
2674 * Released under the MIT license.
2675 * http://jquery.org/license
2676 *
2677 * http://api.jqueryui.com/droppable/
2678 */
2679
2680
2681$.widget( "ui.droppable", {
2682 version: "1.11.4",
2683 widgetEventPrefix: "drop",
2684 options: {
2685 accept: "*",
2686 activeClass: false,
2687 addClasses: true,
2688 greedy: false,
2689 hoverClass: false,
2690 scope: "default",
2691 tolerance: "intersect",
2692
2693 // callbacks
2694 activate: null,
2695 deactivate: null,
2696 drop: null,
2697 out: null,
2698 over: null
2699 },
2700 _create: function() {
2701
2702 var proportions,
2703 o = this.options,
2704 accept = o.accept;
2705
2706 this.isover = false;
2707 this.isout = true;
2708
2709 this.accept = $.isFunction( accept ) ? accept : function( d ) {
2710 return d.is( accept );
2711 };
2712
2713 this.proportions = function( /* valueToWrite */ ) {
2714 if ( arguments.length ) {
2715 // Store the droppable's proportions
2716 proportions = arguments[ 0 ];
2717 } else {
2718 // Retrieve or derive the droppable's proportions
2719 return proportions ?
2720 proportions :
2721 proportions = {
2722 width: this.element[ 0 ].offsetWidth,
2723 height: this.element[ 0 ].offsetHeight
2724 };
2725 }
2726 };
2727
2728 this._addToManager( o.scope );
2729
2730 o.addClasses && this.element.addClass( "ui-droppable" );
2731
2732 },
2733
2734 _addToManager: function( scope ) {
2735 // Add the reference and positions to the manager
2736 $.ui.ddmanager.droppables[ scope ] = $.ui.ddmanager.droppables[ scope ] || [];
2737 $.ui.ddmanager.droppables[ scope ].push( this );
2738 },
2739
2740 _splice: function( drop ) {
2741 var i = 0;
2742 for ( ; i < drop.length; i++ ) {
2743 if ( drop[ i ] === this ) {
2744 drop.splice( i, 1 );
2745 }
2746 }
2747 },
2748
2749 _destroy: function() {
2750 var drop = $.ui.ddmanager.droppables[ this.options.scope ];
2751
2752 this._splice( drop );
2753
2754 this.element.removeClass( "ui-droppable ui-droppable-disabled" );
2755 },
2756
2757 _setOption: function( key, value ) {
2758
2759 if ( key === "accept" ) {
2760 this.accept = $.isFunction( value ) ? value : function( d ) {
2761 return d.is( value );
2762 };
2763 } else if ( key === "scope" ) {
2764 var drop = $.ui.ddmanager.droppables[ this.options.scope ];
2765
2766 this._splice( drop );
2767 this._addToManager( value );
2768 }
2769
2770 this._super( key, value );
2771 },
2772
2773 _activate: function( event ) {
2774 var draggable = $.ui.ddmanager.current;
2775 if ( this.options.activeClass ) {
2776 this.element.addClass( this.options.activeClass );
2777 }
2778 if ( draggable ){
2779 this._trigger( "activate", event, this.ui( draggable ) );
2780 }
2781 },
2782
2783 _deactivate: function( event ) {
2784 var draggable = $.ui.ddmanager.current;
2785 if ( this.options.activeClass ) {
2786 this.element.removeClass( this.options.activeClass );
2787 }
2788 if ( draggable ){
2789 this._trigger( "deactivate", event, this.ui( draggable ) );
2790 }
2791 },
2792
2793 _over: function( event ) {
2794
2795 var draggable = $.ui.ddmanager.current;
2796
2797 // Bail if draggable and droppable are same element
2798 if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2799 return;
2800 }
2801
2802 if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2803 if ( this.options.hoverClass ) {
2804 this.element.addClass( this.options.hoverClass );
2805 }
2806 this._trigger( "over", event, this.ui( draggable ) );
2807 }
2808
2809 },
2810
2811 _out: function( event ) {
2812
2813 var draggable = $.ui.ddmanager.current;
2814
2815 // Bail if draggable and droppable are same element
2816 if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2817 return;
2818 }
2819
2820 if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2821 if ( this.options.hoverClass ) {
2822 this.element.removeClass( this.options.hoverClass );
2823 }
2824 this._trigger( "out", event, this.ui( draggable ) );
2825 }
2826
2827 },
2828
2829 _drop: function( event, custom ) {
2830
2831 var draggable = custom || $.ui.ddmanager.current,
2832 childrenIntersection = false;
2833
2834 // Bail if draggable and droppable are same element
2835 if ( !draggable || ( draggable.currentItem || draggable.element )[ 0 ] === this.element[ 0 ] ) {
2836 return false;
2837 }
2838
2839 this.element.find( ":data(ui-droppable)" ).not( ".ui-draggable-dragging" ).each(function() {
2840 var inst = $( this ).droppable( "instance" );
2841 if (
2842 inst.options.greedy &&
2843 !inst.options.disabled &&
2844 inst.options.scope === draggable.options.scope &&
2845 inst.accept.call( inst.element[ 0 ], ( draggable.currentItem || draggable.element ) ) &&
2846 $.ui.intersect( draggable, $.extend( inst, { offset: inst.element.offset() } ), inst.options.tolerance, event )
2847 ) { childrenIntersection = true; return false; }
2848 });
2849 if ( childrenIntersection ) {
2850 return false;
2851 }
2852
2853 if ( this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2854 if ( this.options.activeClass ) {
2855 this.element.removeClass( this.options.activeClass );
2856 }
2857 if ( this.options.hoverClass ) {
2858 this.element.removeClass( this.options.hoverClass );
2859 }
2860 this._trigger( "drop", event, this.ui( draggable ) );
2861 return this.element;
2862 }
2863
2864 return false;
2865
2866 },
2867
2868 ui: function( c ) {
2869 return {
2870 draggable: ( c.currentItem || c.element ),
2871 helper: c.helper,
2872 position: c.position,
2873 offset: c.positionAbs
2874 };
2875 }
2876
2877});
2878
2879$.ui.intersect = (function() {
2880 function isOverAxis( x, reference, size ) {
2881 return ( x >= reference ) && ( x < ( reference + size ) );
2882 }
2883
2884 return function( draggable, droppable, toleranceMode, event ) {
2885
2886 if ( !droppable.offset ) {
2887 return false;
2888 }
2889
2890 var x1 = ( draggable.positionAbs || draggable.position.absolute ).left + draggable.margins.left,
2891 y1 = ( draggable.positionAbs || draggable.position.absolute ).top + draggable.margins.top,
2892 x2 = x1 + draggable.helperProportions.width,
2893 y2 = y1 + draggable.helperProportions.height,
2894 l = droppable.offset.left,
2895 t = droppable.offset.top,
2896 r = l + droppable.proportions().width,
2897 b = t + droppable.proportions().height;
2898
2899 switch ( toleranceMode ) {
2900 case "fit":
2901 return ( l <= x1 && x2 <= r && t <= y1 && y2 <= b );
2902 case "intersect":
2903 return ( l < x1 + ( draggable.helperProportions.width / 2 ) && // Right Half
2904 x2 - ( draggable.helperProportions.width / 2 ) < r && // Left Half
2905 t < y1 + ( draggable.helperProportions.height / 2 ) && // Bottom Half
2906 y2 - ( draggable.helperProportions.height / 2 ) < b ); // Top Half
2907 case "pointer":
2908 return isOverAxis( event.pageY, t, droppable.proportions().height ) && isOverAxis( event.pageX, l, droppable.proportions().width );
2909 case "touch":
2910 return (
2911 ( y1 >= t && y1 <= b ) || // Top edge touching
2912 ( y2 >= t && y2 <= b ) || // Bottom edge touching
2913 ( y1 < t && y2 > b ) // Surrounded vertically
2914 ) && (
2915 ( x1 >= l && x1 <= r ) || // Left edge touching
2916 ( x2 >= l && x2 <= r ) || // Right edge touching
2917 ( x1 < l && x2 > r ) // Surrounded horizontally
2918 );
2919 default:
2920 return false;
2921 }
2922 };
2923})();
2924
2925/*
2926 This manager tracks offsets of draggables and droppables
2927*/
2928$.ui.ddmanager = {
2929 current: null,
2930 droppables: { "default": [] },
2931 prepareOffsets: function( t, event ) {
2932
2933 var i, j,
2934 m = $.ui.ddmanager.droppables[ t.options.scope ] || [],
2935 type = event ? event.type : null, // workaround for #2317
2936 list = ( t.currentItem || t.element ).find( ":data(ui-droppable)" ).addBack();
2937
2938 droppablesLoop: for ( i = 0; i < m.length; i++ ) {
2939
2940 // No disabled and non-accepted
2941 if ( m[ i ].options.disabled || ( t && !m[ i ].accept.call( m[ i ].element[ 0 ], ( t.currentItem || t.element ) ) ) ) {
2942 continue;
2943 }
2944
2945 // Filter out elements in the current dragged item
2946 for ( j = 0; j < list.length; j++ ) {
2947 if ( list[ j ] === m[ i ].element[ 0 ] ) {
2948 m[ i ].proportions().height = 0;
2949 continue droppablesLoop;
2950 }
2951 }
2952
2953 m[ i ].visible = m[ i ].element.css( "display" ) !== "none";
2954 if ( !m[ i ].visible ) {
2955 continue;
2956 }
2957
2958 // Activate the droppable if used directly from draggables
2959 if ( type === "mousedown" ) {
2960 m[ i ]._activate.call( m[ i ], event );
2961 }
2962
2963 m[ i ].offset = m[ i ].element.offset();
2964 m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth, height: m[ i ].element[ 0 ].offsetHeight });
2965
2966 }
2967
2968 },
2969 drop: function( draggable, event ) {
2970
2971 var dropped = false;
2972 // Create a copy of the droppables in case the list changes during the drop (#9116)
2973 $.each( ( $.ui.ddmanager.droppables[ draggable.options.scope ] || [] ).slice(), function() {
2974
2975 if ( !this.options ) {
2976 return;
2977 }
2978 if ( !this.options.disabled && this.visible && $.ui.intersect( draggable, this, this.options.tolerance, event ) ) {
2979 dropped = this._drop.call( this, event ) || dropped;
2980 }
2981
2982 if ( !this.options.disabled && this.visible && this.accept.call( this.element[ 0 ], ( draggable.currentItem || draggable.element ) ) ) {
2983 this.isout = true;
2984 this.isover = false;
2985 this._deactivate.call( this, event );
2986 }
2987
2988 });
2989 return dropped;
2990
2991 },
2992 dragStart: function( draggable, event ) {
2993 // Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
2994 draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
2995 if ( !draggable.options.refreshPositions ) {
2996 $.ui.ddmanager.prepareOffsets( draggable, event );
2997 }
2998 });
2999 },
3000 drag: function( draggable, event ) {
3001
3002 // If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
3003 if ( draggable.options.refreshPositions ) {
3004 $.ui.ddmanager.prepareOffsets( draggable, event );
3005 }
3006
3007 // Run through all droppables and check their positions based on specific tolerance options
3008 $.each( $.ui.ddmanager.droppables[ draggable.options.scope ] || [], function() {
3009
3010 if ( this.options.disabled || this.greedyChild || !this.visible ) {
3011 return;
3012 }
3013
3014 var parentInstance, scope, parent,
3015 intersects = $.ui.intersect( draggable, this, this.options.tolerance, event ),
3016 c = !intersects && this.isover ? "isout" : ( intersects && !this.isover ? "isover" : null );
3017 if ( !c ) {
3018 return;
3019 }
3020
3021 if ( this.options.greedy ) {
3022 // find droppable parents with same scope
3023 scope = this.options.scope;
3024 parent = this.element.parents( ":data(ui-droppable)" ).filter(function() {
3025 return $( this ).droppable( "instance" ).options.scope === scope;
3026 });
3027
3028 if ( parent.length ) {
3029 parentInstance = $( parent[ 0 ] ).droppable( "instance" );
3030 parentInstance.greedyChild = ( c === "isover" );
3031 }
3032 }
3033
3034 // we just moved into a greedy child
3035 if ( parentInstance && c === "isover" ) {
3036 parentInstance.isover = false;
3037 parentInstance.isout = true;
3038 parentInstance._out.call( parentInstance, event );
3039 }
3040
3041 this[ c ] = true;
3042 this[c === "isout" ? "isover" : "isout"] = false;
3043 this[c === "isover" ? "_over" : "_out"].call( this, event );
3044
3045 // we just moved out of a greedy child
3046 if ( parentInstance && c === "isout" ) {
3047 parentInstance.isout = false;
3048 parentInstance.isover = true;
3049 parentInstance._over.call( parentInstance, event );
3050 }
3051 });
3052
3053 },
3054 dragStop: function( draggable, event ) {
3055 draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
3056 // Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
3057 if ( !draggable.options.refreshPositions ) {
3058 $.ui.ddmanager.prepareOffsets( draggable, event );
3059 }
3060 }
3061};
3062
3063var droppable = $.ui.droppable;
3064
3065
3066/*!
3067 * jQuery UI Resizable 1.11.4
3068 * http://jqueryui.com
3069 *
3070 * Copyright jQuery Foundation and other contributors
3071 * Released under the MIT license.
3072 * http://jquery.org/license
3073 *
3074 * http://api.jqueryui.com/resizable/
3075 */
3076
3077
3078$.widget("ui.resizable", $.ui.mouse, {
3079 version: "1.11.4",
3080 widgetEventPrefix: "resize",
3081 options: {
3082 alsoResize: false,
3083 animate: false,
3084 animateDuration: "slow",
3085 animateEasing: "swing",
3086 aspectRatio: false,
3087 autoHide: false,
3088 containment: false,
3089 ghost: false,
3090 grid: false,
3091 handles: "e,s,se",
3092 helper: false,
3093 maxHeight: null,
3094 maxWidth: null,
3095 minHeight: 10,
3096 minWidth: 10,
3097 // See #7960
3098 zIndex: 90,
3099
3100 // callbacks
3101 resize: null,
3102 start: null,
3103 stop: null
3104 },
3105
3106 _num: function( value ) {
3107 return parseInt( value, 10 ) || 0;
3108 },
3109
3110 _isNumber: function( value ) {
3111 return !isNaN( parseInt( value, 10 ) );
3112 },
3113
3114 _hasScroll: function( el, a ) {
3115
3116 if ( $( el ).css( "overflow" ) === "hidden") {
3117 return false;
3118 }
3119
3120 var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
3121 has = false;
3122
3123 if ( el[ scroll ] > 0 ) {
3124 return true;
3125 }
3126
3127 // TODO: determine which cases actually cause this to happen
3128 // if the element doesn't have the scroll set, see if it's possible to
3129 // set the scroll
3130 el[ scroll ] = 1;
3131 has = ( el[ scroll ] > 0 );
3132 el[ scroll ] = 0;
3133 return has;
3134 },
3135
3136 _create: function() {
3137
3138 var n, i, handle, axis, hname,
3139 that = this,
3140 o = this.options;
3141 this.element.addClass("ui-resizable");
3142
3143 $.extend(this, {
3144 _aspectRatio: !!(o.aspectRatio),
3145 aspectRatio: o.aspectRatio,
3146 originalElement: this.element,
3147 _proportionallyResizeElements: [],
3148 _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
3149 });
3150
3151 // Wrap the element if it cannot hold child nodes
3152 if (this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)) {
3153
3154 this.element.wrap(
3155 $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
3156 position: this.element.css("position"),
3157 width: this.element.outerWidth(),
3158 height: this.element.outerHeight(),
3159 top: this.element.css("top"),
3160 left: this.element.css("left")
3161 })
3162 );
3163
3164 this.element = this.element.parent().data(
3165 "ui-resizable", this.element.resizable( "instance" )
3166 );
3167
3168 this.elementIsWrapper = true;
3169
3170 this.element.css({
3171 marginLeft: this.originalElement.css("marginLeft"),
3172 marginTop: this.originalElement.css("marginTop"),
3173 marginRight: this.originalElement.css("marginRight"),
3174 marginBottom: this.originalElement.css("marginBottom")
3175 });
3176 this.originalElement.css({
3177 marginLeft: 0,
3178 marginTop: 0,
3179 marginRight: 0,
3180 marginBottom: 0
3181 });
3182 // support: Safari
3183 // Prevent Safari textarea resize
3184 this.originalResizeStyle = this.originalElement.css("resize");
3185 this.originalElement.css("resize", "none");
3186
3187 this._proportionallyResizeElements.push( this.originalElement.css({
3188 position: "static",
3189 zoom: 1,
3190 display: "block"
3191 }) );
3192
3193 // support: IE9
3194 // avoid IE jump (hard set the margin)
3195 this.originalElement.css({ margin: this.originalElement.css("margin") });
3196
3197 this._proportionallyResize();
3198 }
3199
3200 this.handles = o.handles ||
3201 ( !$(".ui-resizable-handle", this.element).length ?
3202 "e,s,se" : {
3203 n: ".ui-resizable-n",
3204 e: ".ui-resizable-e",
3205 s: ".ui-resizable-s",
3206 w: ".ui-resizable-w",
3207 se: ".ui-resizable-se",
3208 sw: ".ui-resizable-sw",
3209 ne: ".ui-resizable-ne",
3210 nw: ".ui-resizable-nw"
3211 } );
3212
3213 this._handles = $();
3214 if ( this.handles.constructor === String ) {
3215
3216 if ( this.handles === "all") {
3217 this.handles = "n,e,s,w,se,sw,ne,nw";
3218 }
3219
3220 n = this.handles.split(",");
3221 this.handles = {};
3222
3223 for (i = 0; i < n.length; i++) {
3224
3225 handle = $.trim(n[i]);
3226 hname = "ui-resizable-" + handle;
3227 axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
3228
3229 axis.css({ zIndex: o.zIndex });
3230
3231 // TODO : What's going on here?
3232 if ("se" === handle) {
3233 axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
3234 }
3235
3236 this.handles[handle] = ".ui-resizable-" + handle;
3237 this.element.append(axis);
3238 }
3239
3240 }
3241
3242 this._renderAxis = function(target) {
3243
3244 var i, axis, padPos, padWrapper;
3245
3246 target = target || this.element;
3247
3248 for (i in this.handles) {
3249
3250 if (this.handles[i].constructor === String) {
3251 this.handles[i] = this.element.children( this.handles[ i ] ).first().show();
3252 } else if ( this.handles[ i ].jquery || this.handles[ i ].nodeType ) {
3253 this.handles[ i ] = $( this.handles[ i ] );
3254 this._on( this.handles[ i ], { "mousedown": that._mouseDown });
3255 }
3256
3257 if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)) {
3258
3259 axis = $(this.handles[i], this.element);
3260
3261 padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
3262
3263 padPos = [ "padding",
3264 /ne|nw|n/.test(i) ? "Top" :
3265 /se|sw|s/.test(i) ? "Bottom" :
3266 /^e$/.test(i) ? "Right" : "Left" ].join("");
3267
3268 target.css(padPos, padWrapper);
3269
3270 this._proportionallyResize();
3271 }
3272
3273 this._handles = this._handles.add( this.handles[ i ] );
3274 }
3275 };
3276
3277 // TODO: make renderAxis a prototype function
3278 this._renderAxis(this.element);
3279
3280 this._handles = this._handles.add( this.element.find( ".ui-resizable-handle" ) );
3281 this._handles.disableSelection();
3282
3283 this._handles.mouseover(function() {
3284 if (!that.resizing) {
3285 if (this.className) {
3286 axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
3287 }
3288 that.axis = axis && axis[1] ? axis[1] : "se";
3289 }
3290 });
3291
3292 if (o.autoHide) {
3293 this._handles.hide();
3294 $(this.element)
3295 .addClass("ui-resizable-autohide")
3296 .mouseenter(function() {
3297 if (o.disabled) {
3298 return;
3299 }
3300 $(this).removeClass("ui-resizable-autohide");
3301 that._handles.show();
3302 })
3303 .mouseleave(function() {
3304 if (o.disabled) {
3305 return;
3306 }
3307 if (!that.resizing) {
3308 $(this).addClass("ui-resizable-autohide");
3309 that._handles.hide();
3310 }
3311 });
3312 }
3313
3314 this._mouseInit();
3315 },
3316
3317 _destroy: function() {
3318
3319 this._mouseDestroy();
3320
3321 var wrapper,
3322 _destroy = function(exp) {
3323 $(exp)
3324 .removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
3325 .removeData("resizable")
3326 .removeData("ui-resizable")
3327 .unbind(".resizable")
3328 .find(".ui-resizable-handle")
3329 .remove();
3330 };
3331
3332 // TODO: Unwrap at same DOM position
3333 if (this.elementIsWrapper) {
3334 _destroy(this.element);
3335 wrapper = this.element;
3336 this.originalElement.css({
3337 position: wrapper.css("position"),
3338 width: wrapper.outerWidth(),
3339 height: wrapper.outerHeight(),
3340 top: wrapper.css("top"),
3341 left: wrapper.css("left")
3342 }).insertAfter( wrapper );
3343 wrapper.remove();
3344 }
3345
3346 this.originalElement.css("resize", this.originalResizeStyle);
3347 _destroy(this.originalElement);
3348
3349 return this;
3350 },
3351
3352 _mouseCapture: function(event) {
3353 var i, handle,
3354 capture = false;
3355
3356 for (i in this.handles) {
3357 handle = $(this.handles[i])[0];
3358 if (handle === event.target || $.contains(handle, event.target)) {
3359 capture = true;
3360 }
3361 }
3362
3363 return !this.options.disabled && capture;
3364 },
3365
3366 _mouseStart: function(event) {
3367
3368 var curleft, curtop, cursor,
3369 o = this.options,
3370 el = this.element;
3371
3372 this.resizing = true;
3373
3374 this._renderProxy();
3375
3376 curleft = this._num(this.helper.css("left"));
3377 curtop = this._num(this.helper.css("top"));
3378
3379 if (o.containment) {
3380 curleft += $(o.containment).scrollLeft() || 0;
3381 curtop += $(o.containment).scrollTop() || 0;
3382 }
3383
3384 this.offset = this.helper.offset();
3385 this.position = { left: curleft, top: curtop };
3386
3387 this.size = this._helper ? {
3388 width: this.helper.width(),
3389 height: this.helper.height()
3390 } : {
3391 width: el.width(),
3392 height: el.height()
3393 };
3394
3395 this.originalSize = this._helper ? {
3396 width: el.outerWidth(),
3397 height: el.outerHeight()
3398 } : {
3399 width: el.width(),
3400 height: el.height()
3401 };
3402
3403 this.sizeDiff = {
3404 width: el.outerWidth() - el.width(),
3405 height: el.outerHeight() - el.height()
3406 };
3407
3408 this.originalPosition = { left: curleft, top: curtop };
3409 this.originalMousePosition = { left: event.pageX, top: event.pageY };
3410
3411 this.aspectRatio = (typeof o.aspectRatio === "number") ?
3412 o.aspectRatio :
3413 ((this.originalSize.width / this.originalSize.height) || 1);
3414
3415 cursor = $(".ui-resizable-" + this.axis).css("cursor");
3416 $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
3417
3418 el.addClass("ui-resizable-resizing");
3419 this._propagate("start", event);
3420 return true;
3421 },
3422
3423 _mouseDrag: function(event) {
3424
3425 var data, props,
3426 smp = this.originalMousePosition,
3427 a = this.axis,
3428 dx = (event.pageX - smp.left) || 0,
3429 dy = (event.pageY - smp.top) || 0,
3430 trigger = this._change[a];
3431
3432 this._updatePrevProperties();
3433
3434 if (!trigger) {
3435 return false;
3436 }
3437
3438 data = trigger.apply(this, [ event, dx, dy ]);
3439
3440 this._updateVirtualBoundaries(event.shiftKey);
3441 if (this._aspectRatio || event.shiftKey) {
3442 data = this._updateRatio(data, event);
3443 }
3444
3445 data = this._respectSize(data, event);
3446
3447 this._updateCache(data);
3448
3449 this._propagate("resize", event);
3450
3451 props = this._applyChanges();
3452
3453 if ( !this._helper && this._proportionallyResizeElements.length ) {
3454 this._proportionallyResize();
3455 }
3456
3457 if ( !$.isEmptyObject( props ) ) {
3458 this._updatePrevProperties();
3459 this._trigger( "resize", event, this.ui() );
3460 this._applyChanges();
3461 }
3462
3463 return false;
3464 },
3465
3466 _mouseStop: function(event) {
3467
3468 this.resizing = false;
3469 var pr, ista, soffseth, soffsetw, s, left, top,
3470 o = this.options, that = this;
3471
3472 if (this._helper) {
3473
3474 pr = this._proportionallyResizeElements;
3475 ista = pr.length && (/textarea/i).test(pr[0].nodeName);
3476 soffseth = ista && this._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height;
3477 soffsetw = ista ? 0 : that.sizeDiff.width;
3478
3479 s = {
3480 width: (that.helper.width() - soffsetw),
3481 height: (that.helper.height() - soffseth)
3482 };
3483 left = (parseInt(that.element.css("left"), 10) +
3484 (that.position.left - that.originalPosition.left)) || null;
3485 top = (parseInt(that.element.css("top"), 10) +
3486 (that.position.top - that.originalPosition.top)) || null;
3487
3488 if (!o.animate) {
3489 this.element.css($.extend(s, { top: top, left: left }));
3490 }
3491
3492 that.helper.height(that.size.height);
3493 that.helper.width(that.size.width);
3494
3495 if (this._helper && !o.animate) {
3496 this._proportionallyResize();
3497 }
3498 }
3499
3500 $("body").css("cursor", "auto");
3501
3502 this.element.removeClass("ui-resizable-resizing");
3503
3504 this._propagate("stop", event);
3505
3506 if (this._helper) {
3507 this.helper.remove();
3508 }
3509
3510 return false;
3511
3512 },
3513
3514 _updatePrevProperties: function() {
3515 this.prevPosition = {
3516 top: this.position.top,
3517 left: this.position.left
3518 };
3519 this.prevSize = {
3520 width: this.size.width,
3521 height: this.size.height
3522 };
3523 },
3524
3525 _applyChanges: function() {
3526 var props = {};
3527
3528 if ( this.position.top !== this.prevPosition.top ) {
3529 props.top = this.position.top + "px";
3530 }
3531 if ( this.position.left !== this.prevPosition.left ) {
3532 props.left = this.position.left + "px";
3533 }
3534 if ( this.size.width !== this.prevSize.width ) {
3535 props.width = this.size.width + "px";
3536 }
3537 if ( this.size.height !== this.prevSize.height ) {
3538 props.height = this.size.height + "px";
3539 }
3540
3541 this.helper.css( props );
3542
3543 return props;
3544 },
3545
3546 _updateVirtualBoundaries: function(forceAspectRatio) {
3547 var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
3548 o = this.options;
3549
3550 b = {
3551 minWidth: this._isNumber(o.minWidth) ? o.minWidth : 0,
3552 maxWidth: this._isNumber(o.maxWidth) ? o.maxWidth : Infinity,
3553 minHeight: this._isNumber(o.minHeight) ? o.minHeight : 0,
3554 maxHeight: this._isNumber(o.maxHeight) ? o.maxHeight : Infinity
3555 };
3556
3557 if (this._aspectRatio || forceAspectRatio) {
3558 pMinWidth = b.minHeight * this.aspectRatio;
3559 pMinHeight = b.minWidth / this.aspectRatio;
3560 pMaxWidth = b.maxHeight * this.aspectRatio;
3561 pMaxHeight = b.maxWidth / this.aspectRatio;
3562
3563 if (pMinWidth > b.minWidth) {
3564 b.minWidth = pMinWidth;
3565 }
3566 if (pMinHeight > b.minHeight) {
3567 b.minHeight = pMinHeight;
3568 }
3569 if (pMaxWidth < b.maxWidth) {
3570 b.maxWidth = pMaxWidth;
3571 }
3572 if (pMaxHeight < b.maxHeight) {
3573 b.maxHeight = pMaxHeight;
3574 }
3575 }
3576 this._vBoundaries = b;
3577 },
3578
3579 _updateCache: function(data) {
3580 this.offset = this.helper.offset();
3581 if (this._isNumber(data.left)) {
3582 this.position.left = data.left;
3583 }
3584 if (this._isNumber(data.top)) {
3585 this.position.top = data.top;
3586 }
3587 if (this._isNumber(data.height)) {
3588 this.size.height = data.height;
3589 }
3590 if (this._isNumber(data.width)) {
3591 this.size.width = data.width;
3592 }
3593 },
3594
3595 _updateRatio: function( data ) {
3596
3597 var cpos = this.position,
3598 csize = this.size,
3599 a = this.axis;
3600
3601 if (this._isNumber(data.height)) {
3602 data.width = (data.height * this.aspectRatio);
3603 } else if (this._isNumber(data.width)) {
3604 data.height = (data.width / this.aspectRatio);
3605 }
3606
3607 if (a === "sw") {
3608 data.left = cpos.left + (csize.width - data.width);
3609 data.top = null;
3610 }
3611 if (a === "nw") {
3612 data.top = cpos.top + (csize.height - data.height);
3613 data.left = cpos.left + (csize.width - data.width);
3614 }
3615
3616 return data;
3617 },
3618
3619 _respectSize: function( data ) {
3620
3621 var o = this._vBoundaries,
3622 a = this.axis,
3623 ismaxw = this._isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width),
3624 ismaxh = this._isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
3625 isminw = this._isNumber(data.width) && o.minWidth && (o.minWidth > data.width),
3626 isminh = this._isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
3627 dw = this.originalPosition.left + this.originalSize.width,
3628 dh = this.position.top + this.size.height,
3629 cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
3630 if (isminw) {
3631 data.width = o.minWidth;
3632 }
3633 if (isminh) {
3634 data.height = o.minHeight;
3635 }
3636 if (ismaxw) {
3637 data.width = o.maxWidth;
3638 }
3639 if (ismaxh) {
3640 data.height = o.maxHeight;
3641 }
3642
3643 if (isminw && cw) {
3644 data.left = dw - o.minWidth;
3645 }
3646 if (ismaxw && cw) {
3647 data.left = dw - o.maxWidth;
3648 }
3649 if (isminh && ch) {
3650 data.top = dh - o.minHeight;
3651 }
3652 if (ismaxh && ch) {
3653 data.top = dh - o.maxHeight;
3654 }
3655
3656 // Fixing jump error on top/left - bug #2330
3657 if (!data.width && !data.height && !data.left && data.top) {
3658 data.top = null;
3659 } else if (!data.width && !data.height && !data.top && data.left) {
3660 data.left = null;
3661 }
3662
3663 return data;
3664 },
3665
3666 _getPaddingPlusBorderDimensions: function( element ) {
3667 var i = 0,
3668 widths = [],
3669 borders = [
3670 element.css( "borderTopWidth" ),
3671 element.css( "borderRightWidth" ),
3672 element.css( "borderBottomWidth" ),
3673 element.css( "borderLeftWidth" )
3674 ],
3675 paddings = [
3676 element.css( "paddingTop" ),
3677 element.css( "paddingRight" ),
3678 element.css( "paddingBottom" ),
3679 element.css( "paddingLeft" )
3680 ];
3681
3682 for ( ; i < 4; i++ ) {
3683 widths[ i ] = ( parseInt( borders[ i ], 10 ) || 0 );
3684 widths[ i ] += ( parseInt( paddings[ i ], 10 ) || 0 );
3685 }
3686
3687 return {
3688 height: widths[ 0 ] + widths[ 2 ],
3689 width: widths[ 1 ] + widths[ 3 ]
3690 };
3691 },
3692
3693 _proportionallyResize: function() {
3694
3695 if (!this._proportionallyResizeElements.length) {
3696 return;
3697 }
3698
3699 var prel,
3700 i = 0,
3701 element = this.helper || this.element;
3702
3703 for ( ; i < this._proportionallyResizeElements.length; i++) {
3704
3705 prel = this._proportionallyResizeElements[i];
3706
3707 // TODO: Seems like a bug to cache this.outerDimensions
3708 // considering that we are in a loop.
3709 if (!this.outerDimensions) {
3710 this.outerDimensions = this._getPaddingPlusBorderDimensions( prel );
3711 }
3712
3713 prel.css({
3714 height: (element.height() - this.outerDimensions.height) || 0,
3715 width: (element.width() - this.outerDimensions.width) || 0
3716 });
3717
3718 }
3719
3720 },
3721
3722 _renderProxy: function() {
3723
3724 var el = this.element, o = this.options;
3725 this.elementOffset = el.offset();
3726
3727 if (this._helper) {
3728
3729 this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
3730
3731 this.helper.addClass(this._helper).css({
3732 width: this.element.outerWidth() - 1,
3733 height: this.element.outerHeight() - 1,
3734 position: "absolute",
3735 left: this.elementOffset.left + "px",
3736 top: this.elementOffset.top + "px",
3737 zIndex: ++o.zIndex //TODO: Don't modify option
3738 });
3739
3740 this.helper
3741 .appendTo("body")
3742 .disableSelection();
3743
3744 } else {
3745 this.helper = this.element;
3746 }
3747
3748 },
3749
3750 _change: {
3751 e: function(event, dx) {
3752 return { width: this.originalSize.width + dx };
3753 },
3754 w: function(event, dx) {
3755 var cs = this.originalSize, sp = this.originalPosition;
3756 return { left: sp.left + dx, width: cs.width - dx };
3757 },
3758 n: function(event, dx, dy) {
3759 var cs = this.originalSize, sp = this.originalPosition;
3760 return { top: sp.top + dy, height: cs.height - dy };
3761 },
3762 s: function(event, dx, dy) {
3763 return { height: this.originalSize.height + dy };
3764 },
3765 se: function(event, dx, dy) {
3766 return $.extend(this._change.s.apply(this, arguments),
3767 this._change.e.apply(this, [ event, dx, dy ]));
3768 },
3769 sw: function(event, dx, dy) {
3770 return $.extend(this._change.s.apply(this, arguments),
3771 this._change.w.apply(this, [ event, dx, dy ]));
3772 },
3773 ne: function(event, dx, dy) {
3774 return $.extend(this._change.n.apply(this, arguments),
3775 this._change.e.apply(this, [ event, dx, dy ]));
3776 },
3777 nw: function(event, dx, dy) {
3778 return $.extend(this._change.n.apply(this, arguments),
3779 this._change.w.apply(this, [ event, dx, dy ]));
3780 }
3781 },
3782
3783 _propagate: function(n, event) {
3784 $.ui.plugin.call(this, n, [ event, this.ui() ]);
3785 (n !== "resize" && this._trigger(n, event, this.ui()));
3786 },
3787
3788 plugins: {},
3789
3790 ui: function() {
3791 return {
3792 originalElement: this.originalElement,
3793 element: this.element,
3794 helper: this.helper,
3795 position: this.position,
3796 size: this.size,
3797 originalSize: this.originalSize,
3798 originalPosition: this.originalPosition
3799 };
3800 }
3801
3802});
3803
3804/*
3805 * Resizable Extensions
3806 */
3807
3808$.ui.plugin.add("resizable", "animate", {
3809
3810 stop: function( event ) {
3811 var that = $(this).resizable( "instance" ),
3812 o = that.options,
3813 pr = that._proportionallyResizeElements,
3814 ista = pr.length && (/textarea/i).test(pr[0].nodeName),
3815 soffseth = ista && that._hasScroll(pr[0], "left") ? 0 : that.sizeDiff.height,
3816 soffsetw = ista ? 0 : that.sizeDiff.width,
3817 style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
3818 left = (parseInt(that.element.css("left"), 10) +
3819 (that.position.left - that.originalPosition.left)) || null,
3820 top = (parseInt(that.element.css("top"), 10) +
3821 (that.position.top - that.originalPosition.top)) || null;
3822
3823 that.element.animate(
3824 $.extend(style, top && left ? { top: top, left: left } : {}), {
3825 duration: o.animateDuration,
3826 easing: o.animateEasing,
3827 step: function() {
3828
3829 var data = {
3830 width: parseInt(that.element.css("width"), 10),
3831 height: parseInt(that.element.css("height"), 10),
3832 top: parseInt(that.element.css("top"), 10),
3833 left: parseInt(that.element.css("left"), 10)
3834 };
3835
3836 if (pr && pr.length) {
3837 $(pr[0]).css({ width: data.width, height: data.height });
3838 }
3839
3840 // propagating resize, and updating values for each animation step
3841 that._updateCache(data);
3842 that._propagate("resize", event);
3843
3844 }
3845 }
3846 );
3847 }
3848
3849});
3850
3851$.ui.plugin.add( "resizable", "containment", {
3852
3853 start: function() {
3854 var element, p, co, ch, cw, width, height,
3855 that = $( this ).resizable( "instance" ),
3856 o = that.options,
3857 el = that.element,
3858 oc = o.containment,
3859 ce = ( oc instanceof $ ) ? oc.get( 0 ) : ( /parent/.test( oc ) ) ? el.parent().get( 0 ) : oc;
3860
3861 if ( !ce ) {
3862 return;
3863 }
3864
3865 that.containerElement = $( ce );
3866
3867 if ( /document/.test( oc ) || oc === document ) {
3868 that.containerOffset = {
3869 left: 0,
3870 top: 0
3871 };
3872 that.containerPosition = {
3873 left: 0,
3874 top: 0
3875 };
3876
3877 that.parentData = {
3878 element: $( document ),
3879 left: 0,
3880 top: 0,
3881 width: $( document ).width(),
3882 height: $( document ).height() || document.body.parentNode.scrollHeight
3883 };
3884 } else {
3885 element = $( ce );
3886 p = [];
3887 $([ "Top", "Right", "Left", "Bottom" ]).each(function( i, name ) {
3888 p[ i ] = that._num( element.css( "padding" + name ) );
3889 });
3890
3891 that.containerOffset = element.offset();
3892 that.containerPosition = element.position();
3893 that.containerSize = {
3894 height: ( element.innerHeight() - p[ 3 ] ),
3895 width: ( element.innerWidth() - p[ 1 ] )
3896 };
3897
3898 co = that.containerOffset;
3899 ch = that.containerSize.height;
3900 cw = that.containerSize.width;
3901 width = ( that._hasScroll ( ce, "left" ) ? ce.scrollWidth : cw );
3902 height = ( that._hasScroll ( ce ) ? ce.scrollHeight : ch ) ;
3903
3904 that.parentData = {
3905 element: ce,
3906 left: co.left,
3907 top: co.top,
3908 width: width,
3909 height: height
3910 };
3911 }
3912 },
3913
3914 resize: function( event ) {
3915 var woset, hoset, isParent, isOffsetRelative,
3916 that = $( this ).resizable( "instance" ),
3917 o = that.options,
3918 co = that.containerOffset,
3919 cp = that.position,
3920 pRatio = that._aspectRatio || event.shiftKey,
3921 cop = {
3922 top: 0,
3923 left: 0
3924 },
3925 ce = that.containerElement,
3926 continueResize = true;
3927
3928 if ( ce[ 0 ] !== document && ( /static/ ).test( ce.css( "position" ) ) ) {
3929 cop = co;
3930 }
3931
3932 if ( cp.left < ( that._helper ? co.left : 0 ) ) {
3933 that.size.width = that.size.width +
3934 ( that._helper ?
3935 ( that.position.left - co.left ) :
3936 ( that.position.left - cop.left ) );
3937
3938 if ( pRatio ) {
3939 that.size.height = that.size.width / that.aspectRatio;
3940 continueResize = false;
3941 }
3942 that.position.left = o.helper ? co.left : 0;
3943 }
3944
3945 if ( cp.top < ( that._helper ? co.top : 0 ) ) {
3946 that.size.height = that.size.height +
3947 ( that._helper ?
3948 ( that.position.top - co.top ) :
3949 that.position.top );
3950
3951 if ( pRatio ) {
3952 that.size.width = that.size.height * that.aspectRatio;
3953 continueResize = false;
3954 }
3955 that.position.top = that._helper ? co.top : 0;
3956 }
3957
3958 isParent = that.containerElement.get( 0 ) === that.element.parent().get( 0 );
3959 isOffsetRelative = /relative|absolute/.test( that.containerElement.css( "position" ) );
3960
3961 if ( isParent && isOffsetRelative ) {
3962 that.offset.left = that.parentData.left + that.position.left;
3963 that.offset.top = that.parentData.top + that.position.top;
3964 } else {
3965 that.offset.left = that.element.offset().left;
3966 that.offset.top = that.element.offset().top;
3967 }
3968
3969 woset = Math.abs( that.sizeDiff.width +
3970 (that._helper ?
3971 that.offset.left - cop.left :
3972 (that.offset.left - co.left)) );
3973
3974 hoset = Math.abs( that.sizeDiff.height +
3975 (that._helper ?
3976 that.offset.top - cop.top :
3977 (that.offset.top - co.top)) );
3978
3979 if ( woset + that.size.width >= that.parentData.width ) {
3980 that.size.width = that.parentData.width - woset;
3981 if ( pRatio ) {
3982 that.size.height = that.size.width / that.aspectRatio;
3983 continueResize = false;
3984 }
3985 }
3986
3987 if ( hoset + that.size.height >= that.parentData.height ) {
3988 that.size.height = that.parentData.height - hoset;
3989 if ( pRatio ) {
3990 that.size.width = that.size.height * that.aspectRatio;
3991 continueResize = false;
3992 }
3993 }
3994
3995 if ( !continueResize ) {
3996 that.position.left = that.prevPosition.left;
3997 that.position.top = that.prevPosition.top;
3998 that.size.width = that.prevSize.width;
3999 that.size.height = that.prevSize.height;
4000 }
4001 },
4002
4003 stop: function() {
4004 var that = $( this ).resizable( "instance" ),
4005 o = that.options,
4006 co = that.containerOffset,
4007 cop = that.containerPosition,
4008 ce = that.containerElement,
4009 helper = $( that.helper ),
4010 ho = helper.offset(),
4011 w = helper.outerWidth() - that.sizeDiff.width,
4012 h = helper.outerHeight() - that.sizeDiff.height;
4013
4014 if ( that._helper && !o.animate && ( /relative/ ).test( ce.css( "position" ) ) ) {
4015 $( this ).css({
4016 left: ho.left - cop.left - co.left,
4017 width: w,
4018 height: h
4019 });
4020 }
4021
4022 if ( that._helper && !o.animate && ( /static/ ).test( ce.css( "position" ) ) ) {
4023 $( this ).css({
4024 left: ho.left - cop.left - co.left,
4025 width: w,
4026 height: h
4027 });
4028 }
4029 }
4030});
4031
4032$.ui.plugin.add("resizable", "alsoResize", {
4033
4034 start: function() {
4035 var that = $(this).resizable( "instance" ),
4036 o = that.options;
4037
4038 $(o.alsoResize).each(function() {
4039 var el = $(this);
4040 el.data("ui-resizable-alsoresize", {
4041 width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
4042 left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
4043 });
4044 });
4045 },
4046
4047 resize: function(event, ui) {
4048 var that = $(this).resizable( "instance" ),
4049 o = that.options,
4050 os = that.originalSize,
4051 op = that.originalPosition,
4052 delta = {
4053 height: (that.size.height - os.height) || 0,
4054 width: (that.size.width - os.width) || 0,
4055 top: (that.position.top - op.top) || 0,
4056 left: (that.position.left - op.left) || 0
4057 };
4058
4059 $(o.alsoResize).each(function() {
4060 var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
4061 css = el.parents(ui.originalElement[0]).length ?
4062 [ "width", "height" ] :
4063 [ "width", "height", "top", "left" ];
4064
4065 $.each(css, function(i, prop) {
4066 var sum = (start[prop] || 0) + (delta[prop] || 0);
4067 if (sum && sum >= 0) {
4068 style[prop] = sum || null;
4069 }
4070 });
4071
4072 el.css(style);
4073 });
4074 },
4075
4076 stop: function() {
4077 $(this).removeData("resizable-alsoresize");
4078 }
4079});
4080
4081$.ui.plugin.add("resizable", "ghost", {
4082
4083 start: function() {
4084
4085 var that = $(this).resizable( "instance" ), o = that.options, cs = that.size;
4086
4087 that.ghost = that.originalElement.clone();
4088 that.ghost
4089 .css({
4090 opacity: 0.25,
4091 display: "block",
4092 position: "relative",
4093 height: cs.height,
4094 width: cs.width,
4095 margin: 0,
4096 left: 0,
4097 top: 0
4098 })
4099 .addClass("ui-resizable-ghost")
4100 .addClass(typeof o.ghost === "string" ? o.ghost : "");
4101
4102 that.ghost.appendTo(that.helper);
4103
4104 },
4105
4106 resize: function() {
4107 var that = $(this).resizable( "instance" );
4108 if (that.ghost) {
4109 that.ghost.css({
4110 position: "relative",
4111 height: that.size.height,
4112 width: that.size.width
4113 });
4114 }
4115 },
4116
4117 stop: function() {
4118 var that = $(this).resizable( "instance" );
4119 if (that.ghost && that.helper) {
4120 that.helper.get(0).removeChild(that.ghost.get(0));
4121 }
4122 }
4123
4124});
4125
4126$.ui.plugin.add("resizable", "grid", {
4127
4128 resize: function() {
4129 var outerDimensions,
4130 that = $(this).resizable( "instance" ),
4131 o = that.options,
4132 cs = that.size,
4133 os = that.originalSize,
4134 op = that.originalPosition,
4135 a = that.axis,
4136 grid = typeof o.grid === "number" ? [ o.grid, o.grid ] : o.grid,
4137 gridX = (grid[0] || 1),
4138 gridY = (grid[1] || 1),
4139 ox = Math.round((cs.width - os.width) / gridX) * gridX,
4140 oy = Math.round((cs.height - os.height) / gridY) * gridY,
4141 newWidth = os.width + ox,
4142 newHeight = os.height + oy,
4143 isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
4144 isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
4145 isMinWidth = o.minWidth && (o.minWidth > newWidth),
4146 isMinHeight = o.minHeight && (o.minHeight > newHeight);
4147
4148 o.grid = grid;
4149
4150 if (isMinWidth) {
4151 newWidth += gridX;
4152 }
4153 if (isMinHeight) {
4154 newHeight += gridY;
4155 }
4156 if (isMaxWidth) {
4157 newWidth -= gridX;
4158 }
4159 if (isMaxHeight) {
4160 newHeight -= gridY;
4161 }
4162
4163 if (/^(se|s|e)$/.test(a)) {
4164 that.size.width = newWidth;
4165 that.size.height = newHeight;
4166 } else if (/^(ne)$/.test(a)) {
4167 that.size.width = newWidth;
4168 that.size.height = newHeight;
4169 that.position.top = op.top - oy;
4170 } else if (/^(sw)$/.test(a)) {
4171 that.size.width = newWidth;
4172 that.size.height = newHeight;
4173 that.position.left = op.left - ox;
4174 } else {
4175 if ( newHeight - gridY <= 0 || newWidth - gridX <= 0) {
4176 outerDimensions = that._getPaddingPlusBorderDimensions( this );
4177 }
4178
4179 if ( newHeight - gridY > 0 ) {
4180 that.size.height = newHeight;
4181 that.position.top = op.top - oy;
4182 } else {
4183 newHeight = gridY - outerDimensions.height;
4184 that.size.height = newHeight;
4185 that.position.top = op.top + os.height - newHeight;
4186 }
4187 if ( newWidth - gridX > 0 ) {
4188 that.size.width = newWidth;
4189 that.position.left = op.left - ox;
4190 } else {
4191 newWidth = gridX - outerDimensions.width;
4192 that.size.width = newWidth;
4193 that.position.left = op.left + os.width - newWidth;
4194 }
4195 }
4196 }
4197
4198});
4199
4200var resizable = $.ui.resizable;
4201
4202
4203/*!
4204 * jQuery UI Selectable 1.11.4
4205 * http://jqueryui.com
4206 *
4207 * Copyright jQuery Foundation and other contributors
4208 * Released under the MIT license.
4209 * http://jquery.org/license
4210 *
4211 * http://api.jqueryui.com/selectable/
4212 */
4213
4214
4215var selectable = $.widget("ui.selectable", $.ui.mouse, {
4216 version: "1.11.4",
4217 options: {
4218 appendTo: "body",
4219 autoRefresh: true,
4220 distance: 0,
4221 filter: "*",
4222 tolerance: "touch",
4223
4224 // callbacks
4225 selected: null,
4226 selecting: null,
4227 start: null,
4228 stop: null,
4229 unselected: null,
4230 unselecting: null
4231 },
4232 _create: function() {
4233 var selectees,
4234 that = this;
4235
4236 this.element.addClass("ui-selectable");
4237
4238 this.dragged = false;
4239
4240 // cache selectee children based on filter
4241 this.refresh = function() {
4242 selectees = $(that.options.filter, that.element[0]);
4243 selectees.addClass("ui-selectee");
4244 selectees.each(function() {
4245 var $this = $(this),
4246 pos = $this.offset();
4247 $.data(this, "selectable-item", {
4248 element: this,
4249 $element: $this,
4250 left: pos.left,
4251 top: pos.top,
4252 right: pos.left + $this.outerWidth(),
4253 bottom: pos.top + $this.outerHeight(),
4254 startselected: false,
4255 selected: $this.hasClass("ui-selected"),
4256 selecting: $this.hasClass("ui-selecting"),
4257 unselecting: $this.hasClass("ui-unselecting")
4258 });
4259 });
4260 };
4261 this.refresh();
4262
4263 this.selectees = selectees.addClass("ui-selectee");
4264
4265 this._mouseInit();
4266
4267 this.helper = $("<div class='ui-selectable-helper'></div>");
4268 },
4269
4270 _destroy: function() {
4271 this.selectees
4272 .removeClass("ui-selectee")
4273 .removeData("selectable-item");
4274 this.element
4275 .removeClass("ui-selectable ui-selectable-disabled");
4276 this._mouseDestroy();
4277 },
4278
4279 _mouseStart: function(event) {
4280 var that = this,
4281 options = this.options;
4282
4283 this.opos = [ event.pageX, event.pageY ];
4284
4285 if (this.options.disabled) {
4286 return;
4287 }
4288
4289 this.selectees = $(options.filter, this.element[0]);
4290
4291 this._trigger("start", event);
4292
4293 $(options.appendTo).append(this.helper);
4294 // position helper (lasso)
4295 this.helper.css({
4296 "left": event.pageX,
4297 "top": event.pageY,
4298 "width": 0,
4299 "height": 0
4300 });
4301
4302 if (options.autoRefresh) {
4303 this.refresh();
4304 }
4305
4306 this.selectees.filter(".ui-selected").each(function() {
4307 var selectee = $.data(this, "selectable-item");
4308 selectee.startselected = true;
4309 if (!event.metaKey && !event.ctrlKey) {
4310 selectee.$element.removeClass("ui-selected");
4311 selectee.selected = false;
4312 selectee.$element.addClass("ui-unselecting");
4313 selectee.unselecting = true;
4314 // selectable UNSELECTING callback
4315 that._trigger("unselecting", event, {
4316 unselecting: selectee.element
4317 });
4318 }
4319 });
4320
4321 $(event.target).parents().addBack().each(function() {
4322 var doSelect,
4323 selectee = $.data(this, "selectable-item");
4324 if (selectee) {
4325 doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
4326 selectee.$element
4327 .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
4328 .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
4329 selectee.unselecting = !doSelect;
4330 selectee.selecting = doSelect;
4331 selectee.selected = doSelect;
4332 // selectable (UN)SELECTING callback
4333 if (doSelect) {
4334 that._trigger("selecting", event, {
4335 selecting: selectee.element
4336 });
4337 } else {
4338 that._trigger("unselecting", event, {
4339 unselecting: selectee.element
4340 });
4341 }
4342 return false;
4343 }
4344 });
4345
4346 },
4347
4348 _mouseDrag: function(event) {
4349
4350 this.dragged = true;
4351
4352 if (this.options.disabled) {
4353 return;
4354 }
4355
4356 var tmp,
4357 that = this,
4358 options = this.options,
4359 x1 = this.opos[0],
4360 y1 = this.opos[1],
4361 x2 = event.pageX,
4362 y2 = event.pageY;
4363
4364 if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
4365 if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
4366 this.helper.css({ left: x1, top: y1, width: x2 - x1, height: y2 - y1 });
4367
4368 this.selectees.each(function() {
4369 var selectee = $.data(this, "selectable-item"),
4370 hit = false;
4371
4372 //prevent helper from being selected if appendTo: selectable
4373 if (!selectee || selectee.element === that.element[0]) {
4374 return;
4375 }
4376
4377 if (options.tolerance === "touch") {
4378 hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
4379 } else if (options.tolerance === "fit") {
4380 hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
4381 }
4382
4383 if (hit) {
4384 // SELECT
4385 if (selectee.selected) {
4386 selectee.$element.removeClass("ui-selected");
4387 selectee.selected = false;
4388 }
4389 if (selectee.unselecting) {
4390 selectee.$element.removeClass("ui-unselecting");
4391 selectee.unselecting = false;
4392 }
4393 if (!selectee.selecting) {
4394 selectee.$element.addClass("ui-selecting");
4395 selectee.selecting = true;
4396 // selectable SELECTING callback
4397 that._trigger("selecting", event, {
4398 selecting: selectee.element
4399 });
4400 }
4401 } else {
4402 // UNSELECT
4403 if (selectee.selecting) {
4404 if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
4405 selectee.$element.removeClass("ui-selecting");
4406 selectee.selecting = false;
4407 selectee.$element.addClass("ui-selected");
4408 selectee.selected = true;
4409 } else {
4410 selectee.$element.removeClass("ui-selecting");
4411 selectee.selecting = false;
4412 if (selectee.startselected) {
4413 selectee.$element.addClass("ui-unselecting");
4414 selectee.unselecting = true;
4415 }
4416 // selectable UNSELECTING callback
4417 that._trigger("unselecting", event, {
4418 unselecting: selectee.element
4419 });
4420 }
4421 }
4422 if (selectee.selected) {
4423 if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
4424 selectee.$element.removeClass("ui-selected");
4425 selectee.selected = false;
4426
4427 selectee.$element.addClass("ui-unselecting");
4428 selectee.unselecting = true;
4429 // selectable UNSELECTING callback
4430 that._trigger("unselecting", event, {
4431 unselecting: selectee.element
4432 });
4433 }
4434 }
4435 }
4436 });
4437
4438 return false;
4439 },
4440
4441 _mouseStop: function(event) {
4442 var that = this;
4443
4444 this.dragged = false;
4445
4446 $(".ui-unselecting", this.element[0]).each(function() {
4447 var selectee = $.data(this, "selectable-item");
4448 selectee.$element.removeClass("ui-unselecting");
4449 selectee.unselecting = false;
4450 selectee.startselected = false;
4451 that._trigger("unselected", event, {
4452 unselected: selectee.element
4453 });
4454 });
4455 $(".ui-selecting", this.element[0]).each(function() {
4456 var selectee = $.data(this, "selectable-item");
4457 selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
4458 selectee.selecting = false;
4459 selectee.selected = true;
4460 selectee.startselected = true;
4461 that._trigger("selected", event, {
4462 selected: selectee.element
4463 });
4464 });
4465 this._trigger("stop", event);
4466
4467 this.helper.remove();
4468
4469 return false;
4470 }
4471
4472});
4473
4474
4475/*!
4476 * jQuery UI Sortable 1.11.4
4477 * http://jqueryui.com
4478 *
4479 * Copyright jQuery Foundation and other contributors
4480 * Released under the MIT license.
4481 * http://jquery.org/license
4482 *
4483 * http://api.jqueryui.com/sortable/
4484 */
4485
4486
4487var sortable = $.widget("ui.sortable", $.ui.mouse, {
4488 version: "1.11.4",
4489 widgetEventPrefix: "sort",
4490 ready: false,
4491 options: {
4492 appendTo: "parent",
4493 axis: false,
4494 connectWith: false,
4495 containment: false,
4496 cursor: "auto",
4497 cursorAt: false,
4498 dropOnEmpty: true,
4499 forcePlaceholderSize: false,
4500 forceHelperSize: false,
4501 grid: false,
4502 handle: false,
4503 helper: "original",
4504 items: "> *",
4505 opacity: false,
4506 placeholder: false,
4507 revert: false,
4508 scroll: true,
4509 scrollSensitivity: 20,
4510 scrollSpeed: 20,
4511 scope: "default",
4512 tolerance: "intersect",
4513 zIndex: 1000,
4514
4515 // callbacks
4516 activate: null,
4517 beforeStop: null,
4518 change: null,
4519 deactivate: null,
4520 out: null,
4521 over: null,
4522 receive: null,
4523 remove: null,
4524 sort: null,
4525 start: null,
4526 stop: null,
4527 update: null
4528 },
4529
4530 _isOverAxis: function( x, reference, size ) {
4531 return ( x >= reference ) && ( x < ( reference + size ) );
4532 },
4533
4534 _isFloating: function( item ) {
4535 return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
4536 },
4537
4538 _create: function() {
4539 this.containerCache = {};
4540 this.element.addClass("ui-sortable");
4541
4542 //Get the items
4543 this.refresh();
4544
4545 //Let's determine the parent's offset
4546 this.offset = this.element.offset();
4547
4548 //Initialize mouse events for interaction
4549 this._mouseInit();
4550
4551 this._setHandleClassName();
4552
4553 //We're ready to go
4554 this.ready = true;
4555
4556 },
4557
4558 _setOption: function( key, value ) {
4559 this._super( key, value );
4560
4561 if ( key === "handle" ) {
4562 this._setHandleClassName();
4563 }
4564 },
4565
4566 _setHandleClassName: function() {
4567 this.element.find( ".ui-sortable-handle" ).removeClass( "ui-sortable-handle" );
4568 $.each( this.items, function() {
4569 ( this.instance.options.handle ?
4570 this.item.find( this.instance.options.handle ) : this.item )
4571 .addClass( "ui-sortable-handle" );
4572 });
4573 },
4574
4575 _destroy: function() {
4576 this.element
4577 .removeClass( "ui-sortable ui-sortable-disabled" )
4578 .find( ".ui-sortable-handle" )
4579 .removeClass( "ui-sortable-handle" );
4580 this._mouseDestroy();
4581
4582 for ( var i = this.items.length - 1; i >= 0; i-- ) {
4583 this.items[i].item.removeData(this.widgetName + "-item");
4584 }
4585
4586 return this;
4587 },
4588
4589 _mouseCapture: function(event, overrideHandle) {
4590 var currentItem = null,
4591 validHandle = false,
4592 that = this;
4593
4594 if (this.reverting) {
4595 return false;
4596 }
4597
4598 if(this.options.disabled || this.options.type === "static") {
4599 return false;
4600 }
4601
4602 //We have to refresh the items data once first
4603 this._refreshItems(event);
4604
4605 //Find out if the clicked node (or one of its parents) is a actual item in this.items
4606 $(event.target).parents().each(function() {
4607 if($.data(this, that.widgetName + "-item") === that) {
4608 currentItem = $(this);
4609 return false;
4610 }
4611 });
4612 if($.data(event.target, that.widgetName + "-item") === that) {
4613 currentItem = $(event.target);
4614 }
4615
4616 if(!currentItem) {
4617 return false;
4618 }
4619 if(this.options.handle && !overrideHandle) {
4620 $(this.options.handle, currentItem).find("*").addBack().each(function() {
4621 if(this === event.target) {
4622 validHandle = true;
4623 }
4624 });
4625 if(!validHandle) {
4626 return false;
4627 }
4628 }
4629
4630 this.currentItem = currentItem;
4631 this._removeCurrentsFromItems();
4632 return true;
4633
4634 },
4635
4636 _mouseStart: function(event, overrideHandle, noActivation) {
4637
4638 var i, body,
4639 o = this.options;
4640
4641 this.currentContainer = this;
4642
4643 //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
4644 this.refreshPositions();
4645
4646 //Create and append the visible helper
4647 this.helper = this._createHelper(event);
4648
4649 //Cache the helper size
4650 this._cacheHelperProportions();
4651
4652 /*
4653 * - Position generation -
4654 * This block generates everything position related - it's the core of draggables.
4655 */
4656
4657 //Cache the margins of the original element
4658 this._cacheMargins();
4659
4660 //Get the next scrolling parent
4661 this.scrollParent = this.helper.scrollParent();
4662
4663 //The element's absolute position on the page minus margins
4664 this.offset = this.currentItem.offset();
4665 this.offset = {
4666 top: this.offset.top - this.margins.top,
4667 left: this.offset.left - this.margins.left
4668 };
4669
4670 $.extend(this.offset, {
4671 click: { //Where the click happened, relative to the element
4672 left: event.pageX - this.offset.left,
4673 top: event.pageY - this.offset.top
4674 },
4675 parent: this._getParentOffset(),
4676 relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
4677 });
4678
4679 // Only after we got the offset, we can change the helper's position to absolute
4680 // TODO: Still need to figure out a way to make relative sorting possible
4681 this.helper.css("position", "absolute");
4682 this.cssPosition = this.helper.css("position");
4683
4684 //Generate the original position
4685 this.originalPosition = this._generatePosition(event);
4686 this.originalPageX = event.pageX;
4687 this.originalPageY = event.pageY;
4688
4689 //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
4690 (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
4691
4692 //Cache the former DOM position
4693 this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
4694
4695 //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
4696 if(this.helper[0] !== this.currentItem[0]) {
4697 this.currentItem.hide();
4698 }
4699
4700 //Create the placeholder
4701 this._createPlaceholder();
4702
4703 //Set a containment if given in the options
4704 if(o.containment) {
4705 this._setContainment();
4706 }
4707
4708 if( o.cursor && o.cursor !== "auto" ) { // cursor option
4709 body = this.document.find( "body" );
4710
4711 // support: IE
4712 this.storedCursor = body.css( "cursor" );
4713 body.css( "cursor", o.cursor );
4714
4715 this.storedStylesheet = $( "<style>*{ cursor: "+o.cursor+" !important; }</style>" ).appendTo( body );
4716 }
4717
4718 if(o.opacity) { // opacity option
4719 if (this.helper.css("opacity")) {
4720 this._storedOpacity = this.helper.css("opacity");
4721 }
4722 this.helper.css("opacity", o.opacity);
4723 }
4724
4725 if(o.zIndex) { // zIndex option
4726 if (this.helper.css("zIndex")) {
4727 this._storedZIndex = this.helper.css("zIndex");
4728 }
4729 this.helper.css("zIndex", o.zIndex);
4730 }
4731
4732 //Prepare scrolling
4733 if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
4734 this.overflowOffset = this.scrollParent.offset();
4735 }
4736
4737 //Call callbacks
4738 this._trigger("start", event, this._uiHash());
4739
4740 //Recache the helper size
4741 if(!this._preserveHelperProportions) {
4742 this._cacheHelperProportions();
4743 }
4744
4745
4746 //Post "activate" events to possible containers
4747 if( !noActivation ) {
4748 for ( i = this.containers.length - 1; i >= 0; i-- ) {
4749 this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
4750 }
4751 }
4752
4753 //Prepare possible droppables
4754 if($.ui.ddmanager) {
4755 $.ui.ddmanager.current = this;
4756 }
4757
4758 if ($.ui.ddmanager && !o.dropBehaviour) {
4759 $.ui.ddmanager.prepareOffsets(this, event);
4760 }
4761
4762 this.dragging = true;
4763
4764 this.helper.addClass("ui-sortable-helper");
4765 this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
4766 return true;
4767
4768 },
4769
4770 _mouseDrag: function(event) {
4771 var i, item, itemElement, intersection,
4772 o = this.options,
4773 scrolled = false;
4774
4775 //Compute the helpers position
4776 this.position = this._generatePosition(event);
4777 this.positionAbs = this._convertPositionTo("absolute");
4778
4779 if (!this.lastPositionAbs) {
4780 this.lastPositionAbs = this.positionAbs;
4781 }
4782
4783 //Do scrolling
4784 if(this.options.scroll) {
4785 if(this.scrollParent[0] !== this.document[0] && this.scrollParent[0].tagName !== "HTML") {
4786
4787 if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
4788 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
4789 } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
4790 this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
4791 }
4792
4793 if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
4794 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
4795 } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
4796 this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
4797 }
4798
4799 } else {
4800
4801 if(event.pageY - this.document.scrollTop() < o.scrollSensitivity) {
4802 scrolled = this.document.scrollTop(this.document.scrollTop() - o.scrollSpeed);
4803 } else if(this.window.height() - (event.pageY - this.document.scrollTop()) < o.scrollSensitivity) {
4804 scrolled = this.document.scrollTop(this.document.scrollTop() + o.scrollSpeed);
4805 }
4806
4807 if(event.pageX - this.document.scrollLeft() < o.scrollSensitivity) {
4808 scrolled = this.document.scrollLeft(this.document.scrollLeft() - o.scrollSpeed);
4809 } else if(this.window.width() - (event.pageX - this.document.scrollLeft()) < o.scrollSensitivity) {
4810 scrolled = this.document.scrollLeft(this.document.scrollLeft() + o.scrollSpeed);
4811 }
4812
4813 }
4814
4815 if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
4816 $.ui.ddmanager.prepareOffsets(this, event);
4817 }
4818 }
4819
4820 //Regenerate the absolute position used for position checks
4821 this.positionAbs = this._convertPositionTo("absolute");
4822
4823 //Set the helper position
4824 if(!this.options.axis || this.options.axis !== "y") {
4825 this.helper[0].style.left = this.position.left+"px";
4826 }
4827 if(!this.options.axis || this.options.axis !== "x") {
4828 this.helper[0].style.top = this.position.top+"px";
4829 }
4830
4831 //Rearrange
4832 for (i = this.items.length - 1; i >= 0; i--) {
4833
4834 //Cache variables and intersection, continue if no intersection
4835 item = this.items[i];
4836 itemElement = item.item[0];
4837 intersection = this._intersectsWithPointer(item);
4838 if (!intersection) {
4839 continue;
4840 }
4841
4842 // Only put the placeholder inside the current Container, skip all
4843 // items from other containers. This works because when moving
4844 // an item from one container to another the
4845 // currentContainer is switched before the placeholder is moved.
4846 //
4847 // Without this, moving items in "sub-sortables" can cause
4848 // the placeholder to jitter between the outer and inner container.
4849 if (item.instance !== this.currentContainer) {
4850 continue;
4851 }
4852
4853 // cannot intersect with itself
4854 // no useless actions that have been done before
4855 // no action if the item moved is the parent of the item checked
4856 if (itemElement !== this.currentItem[0] &&
4857 this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
4858 !$.contains(this.placeholder[0], itemElement) &&
4859 (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
4860 ) {
4861
4862 this.direction = intersection === 1 ? "down" : "up";
4863
4864 if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
4865 this._rearrange(event, item);
4866 } else {
4867 break;
4868 }
4869
4870 this._trigger("change", event, this._uiHash());
4871 break;
4872 }
4873 }
4874
4875 //Post events to containers
4876 this._contactContainers(event);
4877
4878 //Interconnect with droppables
4879 if($.ui.ddmanager) {
4880 $.ui.ddmanager.drag(this, event);
4881 }
4882
4883 //Call callbacks
4884 this._trigger("sort", event, this._uiHash());
4885
4886 this.lastPositionAbs = this.positionAbs;
4887 return false;
4888
4889 },
4890
4891 _mouseStop: function(event, noPropagation) {
4892
4893 if(!event) {
4894 return;
4895 }
4896
4897 //If we are using droppables, inform the manager about the drop
4898 if ($.ui.ddmanager && !this.options.dropBehaviour) {
4899 $.ui.ddmanager.drop(this, event);
4900 }
4901
4902 if(this.options.revert) {
4903 var that = this,
4904 cur = this.placeholder.offset(),
4905 axis = this.options.axis,
4906 animation = {};
4907
4908 if ( !axis || axis === "x" ) {
4909 animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollLeft);
4910 }
4911 if ( !axis || axis === "y" ) {
4912 animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === this.document[0].body ? 0 : this.offsetParent[0].scrollTop);
4913 }
4914 this.reverting = true;
4915 $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
4916 that._clear(event);
4917 });
4918 } else {
4919 this._clear(event, noPropagation);
4920 }
4921
4922 return false;
4923
4924 },
4925
4926 cancel: function() {
4927
4928 if(this.dragging) {
4929
4930 this._mouseUp({ target: null });
4931
4932 if(this.options.helper === "original") {
4933 this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
4934 } else {
4935 this.currentItem.show();
4936 }
4937
4938 //Post deactivating events to containers
4939 for (var i = this.containers.length - 1; i >= 0; i--){
4940 this.containers[i]._trigger("deactivate", null, this._uiHash(this));
4941 if(this.containers[i].containerCache.over) {
4942 this.containers[i]._trigger("out", null, this._uiHash(this));
4943 this.containers[i].containerCache.over = 0;
4944 }
4945 }
4946
4947 }
4948
4949 if (this.placeholder) {
4950 //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
4951 if(this.placeholder[0].parentNode) {
4952 this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
4953 }
4954 if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
4955 this.helper.remove();
4956 }
4957
4958 $.extend(this, {
4959 helper: null,
4960 dragging: false,
4961 reverting: false,
4962 _noFinalSort: null
4963 });
4964
4965 if(this.domPosition.prev) {
4966 $(this.domPosition.prev).after(this.currentItem);
4967 } else {
4968 $(this.domPosition.parent).prepend(this.currentItem);
4969 }
4970 }
4971
4972 return this;
4973
4974 },
4975
4976 serialize: function(o) {
4977
4978 var items = this._getItemsAsjQuery(o && o.connected),
4979 str = [];
4980 o = o || {};
4981
4982 $(items).each(function() {
4983 var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
4984 if (res) {
4985 str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
4986 }
4987 });
4988
4989 if(!str.length && o.key) {
4990 str.push(o.key + "=");
4991 }
4992
4993 return str.join("&");
4994
4995 },
4996
4997 toArray: function(o) {
4998
4999 var items = this._getItemsAsjQuery(o && o.connected),

11618 more lines aren’t shown here.

Source/Libraries/GSF.Web/Shared/Scripts/primeui.js+13963−0
@@ -0,0 +1,13963 @@
1/**
2 * PUI Object
3 */
4var PUI = {
5
6 zindex : 1000,
7
8 gridColumns: {
9 '1': 'ui-grid-col-12',
10 '2': 'ui-grid-col-6',
11 '3': 'ui-grid-col-4',
12 '4': 'ui-grid-col-3',
13 '6': 'ui-grid-col-2',
14 '12': 'ui-grid-col-11'
15 },
16
17 charSet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
18
19 /**
20 * Aligns container scrollbar to keep item in container viewport, algorithm copied from jquery-ui menu widget
21 */
22 scrollInView: function(container, item) {
23 var borderTop = parseFloat(container.css('borderTopWidth')) || 0,
24 paddingTop = parseFloat(container.css('paddingTop')) || 0,
25 offset = item.offset().top - container.offset().top - borderTop - paddingTop,
26 scroll = container.scrollTop(),
27 elementHeight = container.height(),
28 itemHeight = item.outerHeight(true);
29
30 if(offset < 0) {
31 container.scrollTop(scroll + offset);
32 }
33 else if((offset + itemHeight) > elementHeight) {
34 container.scrollTop(scroll + offset - elementHeight + itemHeight);
35 }
36 },
37
38 generateRandomId: function() {
39 var id = '';
40 for (var i = 1; i <= 10; i++) {
41 var randPos = Math.floor(Math.random() * this.charSet.length);
42 id += this.charSet[randPos];
43 }
44 return id;
45 },
46
47 isIE: function(version) {
48 return (this.browser.msie && parseInt(this.browser.version, 10) === version);
49 },
50
51 escapeRegExp: function(text) {
52 return text.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
53 },
54
55 escapeHTML: function(value) {
56 return value.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
57 },
58
59 escapeClientId: function(id) {
60 return "#" + id.replace(/:/g,"\\:");
61 },
62
63 clearSelection: function() {
64 if(window.getSelection) {
65 if(window.getSelection().empty) {
66 window.getSelection().empty();
67 } else if(window.getSelection().removeAllRanges) {
68 window.getSelection().removeAllRanges();
69 }
70 } else if(document.selection && document.selection.empty) {
71 document.selection.empty();
72 }
73 },
74
75 inArray: function(arr, item) {
76 for(var i = 0; i < arr.length; i++) {
77 if(arr[i] === item) {
78 return true;
79 }
80 }
81
82 return false;
83 },
84
85 calculateScrollbarWidth: function() {
86 if(!this.scrollbarWidth) {
87 if(this.browser.msie) {
88 var $textarea1 = $('<textarea cols="10" rows="2"></textarea>')
89 .css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body'),
90 $textarea2 = $('<textarea cols="10" rows="2" style="overflow: hidden;"></textarea>')
91 .css({ position: 'absolute', top: -1000, left: -1000 }).appendTo('body');
92 this.scrollbarWidth = $textarea1.width() - $textarea2.width();
93 $textarea1.add($textarea2).remove();
94 }
95 else {
96 var $div = $('<div />')
97 .css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000 })
98 .prependTo('body').append('<div />').find('div')
99 .css({ width: '100%', height: 200 });
100 this.scrollbarWidth = 100 - $div.width();
101 $div.parent().remove();
102 }
103 }
104
105 return this.scrollbarWidth;
106 },
107
108 //adapted from jquery browser plugin
109 resolveUserAgent: function() {
110 var matched, browser;
111
112 jQuery.uaMatch = function( ua ) {
113 ua = ua.toLowerCase();
114
115 var match = /(opr)[\/]([\w.]+)/.exec( ua ) ||
116 /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
117 /(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec( ua ) ||
118 /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
119 /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
120 /(msie) ([\w.]+)/.exec( ua ) ||
121 ua.indexOf("trident") >= 0 && /(rv)(?::| )([\w.]+)/.exec( ua ) ||
122 ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
123 [];
124
125 var platform_match = /(ipad)/.exec( ua ) ||
126 /(iphone)/.exec( ua ) ||
127 /(android)/.exec( ua ) ||
128 /(windows phone)/.exec( ua ) ||
129 /(win)/.exec( ua ) ||
130 /(mac)/.exec( ua ) ||
131 /(linux)/.exec( ua ) ||
132 /(cros)/i.exec( ua ) ||
133 [];
134
135 return {
136 browser: match[ 3 ] || match[ 1 ] || "",
137 version: match[ 2 ] || "0",
138 platform: platform_match[ 0 ] || ""
139 };
140 };
141
142 matched = jQuery.uaMatch( window.navigator.userAgent );
143 browser = {};
144
145 if ( matched.browser ) {
146 browser[ matched.browser ] = true;
147 browser.version = matched.version;
148 browser.versionNumber = parseInt(matched.version);
149 }
150
151 if ( matched.platform ) {
152 browser[ matched.platform ] = true;
153 }
154
155 // These are all considered mobile platforms, meaning they run a mobile browser
156 if ( browser.android || browser.ipad || browser.iphone || browser[ "windows phone" ] ) {
157 browser.mobile = true;
158 }
159
160 // These are all considered desktop platforms, meaning they run a desktop browser
161 if ( browser.cros || browser.mac || browser.linux || browser.win ) {
162 browser.desktop = true;
163 }
164
165 // Chrome, Opera 15+ and Safari are webkit based browsers
166 if ( browser.chrome || browser.opr || browser.safari ) {
167 browser.webkit = true;
168 }
169
170 // IE11 has a new token so we will assign it msie to avoid breaking changes
171 if ( browser.rv )
172 {
173 var ie = "msie";
174
175 matched.browser = ie;
176 browser[ie] = true;
177 }
178
179 // Opera 15+ are identified as opr
180 if ( browser.opr )
181 {
182 var opera = "opera";
183
184 matched.browser = opera;
185 browser[opera] = true;
186 }
187
188 // Stock Android browsers are marked as Safari on Android.
189 if ( browser.safari && browser.android )
190 {
191 var android = "android";
192
193 matched.browser = android;
194 browser[android] = true;
195 }
196
197 // Assign the name and platform variable
198 browser.name = matched.browser;
199 browser.platform = matched.platform;
200
201 this.browser = browser;
202 $.browser = browser;
203 },
204
205 getGridColumn: function(number) {
206 return this.gridColumns[number + ''];
207 },
208
209 executeFunctionByName: function(functionName /*, args */) {
210 var args = [].slice.call(arguments).splice(1),
211 context = window,
212 namespaces = functionName.split("."),
213 func = namespaces.pop();
214 for(var i = 0; i < namespaces.length; i++) {
215 context = context[namespaces[i]];
216 }
217 return context[func].apply(this, args);
218 },
219
220 resolveObjectByName: function(name) {
221 if(name) {
222 var parts = name.split(".");
223 for(var i = 0, len = parts.length, obj = window; i < len; ++i) {
224 obj = obj[parts[i]];
225 }
226 return obj;
227 }
228 else {
229 return null;
230 }
231 },
232
233 getCookie : function(name) {
234 return $.cookie(name);
235 },
236
237 setCookie : function(name, value, cfg) {
238 $.cookie(name, value, cfg);
239 },
240
241 deleteCookie: function(name, cfg) {
242 $.removeCookie(name, cfg);
243 }
244
245};
246
247PUI.resolveUserAgent();
248/**
249 * PrimeUI Accordion widget
250 */
251(function() {
252
253 $.widget("primeui.puiaccordion", {
254
255 options: {
256 activeIndex: 0,
257 multiple: false
258 },
259
260 _create: function() {
261 if(this.options.multiple) {
262 this.options.activeIndex = this.options.activeIndex||[0];
263 }
264
265 var $this = this;
266 this.element.addClass('ui-accordion ui-widget ui-helper-reset');
267
268 var tabContainers = this.element.children();
269
270 //primeui
271 if(tabContainers.is('div')) {
272 this.panelMode = 'native';
273 this.headers = this.element.children('h3');
274 this.panels = this.element.children('div');
275 }
276 //primeng
277 else {
278 this.panelMode = 'wrapped';
279 this.headers = tabContainers.children('h3');
280 this.panels = tabContainers.children('div');
281 }
282
283 this.headers.addClass('ui-accordion-header ui-helper-reset ui-state-default').each(function(i) {
284 var header = $(this),
285 title = header.html(),
286 active = $this.options.multiple ? ($.inArray(i, $this.options.activeIndex) !== -1) : (i == $this.options.activeIndex),
287 headerClass = (active) ? 'ui-state-active ui-corner-top' : 'ui-corner-all',
288 iconClass = (active) ? 'fa fa-fw fa-caret-down' : 'fa fa-fw fa-caret-right';
289
290 header.addClass(headerClass).html('<span class="' + iconClass + '"></span><a href="#">' + title + '</a>');
291 });
292
293 this.panels.each(function(i) {
294 var content = $(this);
295 content.addClass('ui-accordion-content ui-helper-reset ui-widget-content'),
296 active = $this.options.multiple ? ($.inArray(i, $this.options.activeIndex) !== -1) : (i == $this.options.activeIndex);
297
298 if(!active) {
299 content.addClass('ui-helper-hidden');
300 }
301 });
302
303 this.headers.children('a').disableSelection();
304
305 this._bindEvents();
306 },
307
308 _destroy: function() {
309 this._unbindEvents();
310 this.element.removeClass('ui-accordion ui-widget ui-helper-reset');
311 this.headers.removeClass('ui-accordion-header ui-helper-reset ui-state-default ui-state-hover ui-state-active ui-state-disabled ui-corner-all ui-corner-top');
312 this.panels.removeClass('ui-accordion-content ui-helper-reset ui-widget-content ui-helper-hidden');
313 this.headers.children('.fa').remove();
314 this.headers.children('a').contents().unwrap();
315 },
316
317 _bindEvents: function() {
318 var $this = this;
319
320 this.headers.on('mouseover.puiaccordion', function() {
321 var element = $(this);
322 if(!element.hasClass('ui-state-active')&&!element.hasClass('ui-state-disabled')) {
323 element.addClass('ui-state-hover');
324 }
325 }).on('mouseout.puiaccordion', function() {
326 var element = $(this);
327 if(!element.hasClass('ui-state-active')&&!element.hasClass('ui-state-disabled')) {
328 element.removeClass('ui-state-hover');
329 }
330 }).on('click.puiaccordion', function(e) {
331 var element = $(this);
332 if(!element.hasClass('ui-state-disabled')) {
333 var tabIndex = ($this.panelMode === 'native') ? element.index() / 2 : element.parent().index();
334
335 if(element.hasClass('ui-state-active')) {
336 $this.unselect(tabIndex);
337 }
338 else {
339 $this.select(tabIndex, false);
340 }
341 }
342
343 e.preventDefault();
344 });
345 },
346
347 _unbindEvents: function() {
348 this.headers.off('mouseover.puiaccordion mouseout.puiaccordion click.puiaccordion');
349 },
350
351 /**
352 * Activates a tab with given index
353 */
354 select: function(index, silent) {
355 var panel = this.panels.eq(index);
356
357 if(!silent) {
358 this._trigger('change', null, {'index': index});
359 }
360
361 //update state
362 if(this.options.multiple) {
363 this._addToSelection(index);
364 }
365 else {
366 this.options.activeIndex = index;
367 }
368 this._show(panel);
369 },
370
371 /**
372 * Deactivates a tab with given index
373 */
374 unselect: function(index) {
375 var panel = this.panels.eq(index),
376 header = panel.prev();
377
378 header.attr('aria-expanded', false).children('.fa').removeClass('fa-caret-down').addClass('fa-caret-right');
379 header.removeClass('ui-state-active ui-corner-top').addClass('ui-corner-all');
380 panel.attr('aria-hidden', true).slideUp();
381
382 this._removeFromSelection(index);
383 },
384
385 _show: function(panel) {
386 //deactivate current
387 if(!this.options.multiple) {
388 var oldHeader = this.headers.filter('.ui-state-active');
389 oldHeader.children('.fa').removeClass('fa-caret-down').addClass('fa-caret-right');
390 oldHeader.attr('aria-expanded', false).removeClass('ui-state-active ui-corner-top').addClass('ui-corner-all').next().attr('aria-hidden', true).slideUp();
391 }
392
393 //activate selected
394 var newHeader = panel.prev();
395 newHeader.attr('aria-expanded', true).addClass('ui-state-active ui-corner-top').removeClass('ui-state-hover ui-corner-all')
396 .children('.fa').removeClass('fa-caret-right').addClass('fa-caret-down');
397
398 panel.attr('aria-hidden', false).slideDown('normal');
399 },
400
401 _addToSelection: function(nodeId) {
402 this.options.activeIndex.push(nodeId);
403 },
404
405 _removeFromSelection: function(index) {
406 this.options.activeIndex = $.grep(this.options.activeIndex, function(r) {
407 return r != index;
408 });
409 },
410
411 _setOption: function(key, value) {
412 if(key === 'activeIndex') {
413 this.select(value, true);
414 }
415 else {
416 $.Widget.prototype._setOption.apply(this, arguments);
417 }
418 }
419
420 });
421})();
422/**
423 * PrimeUI autocomplete widget
424 */
425(function() {
426
427 $.widget("primeui.puiautocomplete", {
428
429 options: {
430 delay: 300,
431 minQueryLength: 1,
432 multiple: false,
433 dropdown: false,
434 scrollHeight: 200,
435 forceSelection: false,
436 effect:null,
437 effectOptions: {},
438 effectSpeed: 'normal',
439 content: null,
440 caseSensitive: false
441 },
442
443 _create: function() {
444 this.element.wrap('<span class="ui-autocomplete ui-widget" />');
445 this.element.puiinputtext();
446 this.panel = $('<div class="ui-autocomplete-panel ui-widget-content ui-corner-all ui-helper-hidden ui-shadow"></div>').appendTo('body');
447
448 if(this.options.multiple) {
449 this.element.wrap('<ul class="ui-autocomplete-multiple ui-widget ui-inputtext ui-state-default ui-corner-all">' +
450 '<li class="ui-autocomplete-input-token"></li></ul>');
451 this.inputContainer = this.element.parent();
452 this.multiContainer = this.inputContainer.parent();
453 }
454 else {
455 if(this.options.dropdown) {
456 this.dropdown = $('<button type="button" class="ui-autocomplete-dropdown ui-button ui-widget ui-state-default ui-corner-right ui-button-icon-only">' +
457 '<span class="fa fa-fw fa-caret-down"></span><span class="ui-button-text">&nbsp;</span></button>')
458 .insertAfter(this.element);
459 this.element.removeClass('ui-corner-all').addClass('ui-corner-left');
460 }
461 }
462
463 this._bindEvents();
464 },
465
466 _bindEvents: function() {
467 var $this = this;
468
469 this._bindKeyEvents();
470
471 if(this.options.dropdown) {
472 this.dropdown.on('mouseenter.puiautocomplete', function() {
473 if(!$this.element.prop('disabled')) {
474 $this.dropdown.addClass('ui-state-hover');
475 }
476 })
477 .on('mouseleave.puiautocomplete', function() {
478 $this.dropdown.removeClass('ui-state-hover');
479 })
480 .on('mousedown.puiautocomplete', function() {
481 if(!$this.element.prop('disabled')) {
482 $this.dropdown.addClass('ui-state-active');
483 }
484 })
485 .on('mouseup.puiautocomplete', function() {
486 if(!$this.element.prop('disabled')) {
487 $this.dropdown.removeClass('ui-state-active');
488 $this.search('');
489 $this.element.focus();
490 }
491 })
492 .on('focus.puiautocomplete', function() {
493 $this.dropdown.addClass('ui-state-focus');
494 })
495 .on('blur.puiautocomplete', function() {
496 $this.dropdown.removeClass('ui-state-focus');
497 })
498 .on('keydown.puiautocomplete', function(e) {
499 var keyCode = $.ui.keyCode;
500
501 if(e.which == keyCode.ENTER || e.which == keyCode.NUMPAD_ENTER) {
502 $this.search('');
503 $this.input.focus();
504
505 e.preventDefault();
506 }
507 });
508 }
509
510 if(this.options.multiple) {
511 this.multiContainer.on('hover.puiautocomplete', function() {
512 $(this).toggleClass('ui-state-hover');
513 })
514 .on('click.puiautocomplete', function() {
515 $this.element.trigger('focus');
516 });
517
518 this.element.on('focus.ui-autocomplete', function() {
519 $this.multiContainer.addClass('ui-state-focus');
520 })
521 .on('blur.ui-autocomplete', function(e) {
522 $this.multiContainer.removeClass('ui-state-focus');
523 });
524 }
525
526 if(this.options.forceSelection) {
527 this.currentItems = [this.element.val()];
528
529 this.element.on('blur.puiautocomplete', function() {
530 var value = $(this).val(),
531 valid = false;
532
533 for(var i = 0; i < $this.currentItems.length; i++) {
534 if($this.currentItems[i] === value) {
535 valid = true;
536 break;
537 }
538 }
539
540 if(!valid) {
541 $this.element.val('');
542 }
543 });
544 }
545
546 $(document.body).bind('mousedown.puiautocomplete', function (e) {
547 if($this.panel.is(":hidden")) {
548 return;
549 }
550
551 if(e.target === $this.element.get(0)) {
552 return;
553 }
554
555 var offset = $this.panel.offset();
556 if (e.pageX < offset.left ||
557 e.pageX > offset.left + $this.panel.width() ||
558 e.pageY < offset.top ||
559 e.pageY > offset.top + $this.panel.height()) {
560 $this.hide();
561 }
562 });
563
564 $(window).bind('resize.' + this.element.id, function() {
565 if($this.panel.is(':visible')) {
566 $this._alignPanel();
567 }
568 });
569 },
570
571 _bindKeyEvents: function() {
572 var $this = this;
573
574 this.element.on('keyup.puiautocomplete', function(e) {
575 var keyCode = $.ui.keyCode,
576 key = e.which,
577 shouldSearch = true;
578
579 if(key == keyCode.UP ||
580 key == keyCode.LEFT ||
581 key == keyCode.DOWN ||
582 key == keyCode.RIGHT ||
583 key == keyCode.TAB ||
584 key == keyCode.SHIFT ||
585 key == keyCode.ENTER ||
586 key == keyCode.NUMPAD_ENTER) {
587 shouldSearch = false;
588 }
589
590 if(shouldSearch) {
591 var value = $this.element.val();
592
593 if(!value.length) {
594 $this.hide();
595 }
596
597 if(value.length >= $this.options.minQueryLength) {
598 if($this.timeout) {
599 window.clearTimeout($this.timeout);
600 }
601
602 $this.timeout = window.setTimeout(function() {
603 $this.search(value);
604 },
605 $this.options.delay);
606 }
607 }
608
609 }).on('keydown.puiautocomplete', function(e) {
610 if($this.panel.is(':visible')) {
611 var keyCode = $.ui.keyCode,
612 highlightedItem = $this.items.filter('.ui-state-highlight');
613
614 switch(e.which) {
615 case keyCode.UP:
616 case keyCode.LEFT:
617 var prev = highlightedItem.prev();
618
619 if(prev.length == 1) {
620 highlightedItem.removeClass('ui-state-highlight');
621 prev.addClass('ui-state-highlight');
622
623 if($this.options.scrollHeight) {
624 PUI.scrollInView($this.panel, prev);
625 }
626 }
627
628 e.preventDefault();
629 break;
630
631 case keyCode.DOWN:
632 case keyCode.RIGHT:
633 var next = highlightedItem.next();
634
635 if(next.length == 1) {
636 highlightedItem.removeClass('ui-state-highlight');
637 next.addClass('ui-state-highlight');
638
639 if($this.options.scrollHeight) {
640 PUI.scrollInView($this.panel, next);
641 }
642 }
643
644 e.preventDefault();
645 break;
646
647 case keyCode.ENTER:
648 case keyCode.NUMPAD_ENTER:
649 highlightedItem.trigger('click');
650
651 e.preventDefault();
652 break;
653
654 case keyCode.ALT:
655 case 224:
656 break;
657
658 case keyCode.TAB:
659 highlightedItem.trigger('click');
660 $this.hide();
661 break;
662 }
663 }
664
665 });
666 },
667
668 _bindDynamicEvents: function() {
669 var $this = this;
670
671 this.items.on('mouseover.puiautocomplete', function() {
672 var item = $(this);
673
674 if(!item.hasClass('ui-state-highlight')) {
675 $this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
676 item.addClass('ui-state-highlight');
677 }
678 })
679 .on('click.puiautocomplete', function(event) {
680 var item = $(this);
681
682 if($this.options.multiple) {
683 var tokenMarkup = '<li class="ui-autocomplete-token ui-state-active ui-corner-all ui-helper-hidden">';
684 tokenMarkup += '<span class="ui-autocomplete-token-icon fa fa-fw fa-close" />';
685 tokenMarkup += '<span class="ui-autocomplete-token-label">' + item.data('label') + '</span></li>';
686
687 $(tokenMarkup).data(item.data())
688 .insertBefore($this.inputContainer).fadeIn()
689 .children('.ui-autocomplete-token-icon').on('click.ui-autocomplete', function(e) {
690 var token = $(this).parent();
691 $this._removeItem(token);
692 $this._trigger('unselect', e, token);
693 });
694
695 $this.element.val('').trigger('focus');
696 }
697 else {
698 $this.element.val(item.data('label')).focus();
699 }
700
701 $this._trigger('select', event, item);
702 $this.hide();
703 });
704 },
705
706 search: function(q) {
707 this.query = this.options.caseSensitive ? q : q.toLowerCase();
708
709 var request = {
710 query: this.query
711 };
712
713 if(this.options.completeSource) {
714 if($.isArray(this.options.completeSource)) {
715 var sourceArr = this.options.completeSource,
716 data = [],
717 emptyQuery = ($.trim(q) === '');
718
719 for(var i = 0 ; i < sourceArr.length; i++) {
720 var item = sourceArr[i],
721 itemLabel = item.label||item;
722
723 if(!this.options.caseSensitive) {
724 itemLabel = itemLabel.toLowerCase();
725 }
726
727 if(emptyQuery||itemLabel.indexOf(this.query) === 0) {
728 data.push({label:sourceArr[i], value: item});
729 }
730 }
731
732 this._handleData(data);
733 }
734 else {
735 this.options.completeSource.call(this, request, this._handleData);
736 }
737 }
738 },
739
740 _handleData: function(data) {
741 var $this = this;
742 this.panel.html('');
743 this.listContainer = $('<ul class="ui-autocomplete-items ui-autocomplete-list ui-widget-content ui-widget ui-corner-all ui-helper-reset"></ul>').appendTo(this.panel);
744
745 for(var i = 0; i < data.length; i++) {
746 var item = $('<li class="ui-autocomplete-item ui-autocomplete-list-item ui-corner-all"></li>');
747 item.data(data[i]);
748
749 if(this.options.content)
750 item.html(this.options.content.call(this, data[i]));
751 else
752 item.text(data[i].label);
753
754 this.listContainer.append(item);
755 }
756
757 this.items = this.listContainer.children('.ui-autocomplete-item');
758
759 this._bindDynamicEvents();
760
761 if(this.items.length > 0) {
762 var firstItem = $this.items.eq(0),
763 hidden = this.panel.is(':hidden');
764 firstItem.addClass('ui-state-highlight');
765
766 if($this.query.length > 0 && !$this.options.content) {
767 $this.items.each(function() {
768 var item = $(this),
769 text = item.html(),
770 re = new RegExp(PUI.escapeRegExp($this.query), 'gi'),
771 highlighedText = text.replace(re, '<span class="ui-autocomplete-query">$&</span>');
772
773 item.html(highlighedText);
774 });
775 }
776
777 if(this.options.forceSelection) {
778 this.currentItems = [];
779 $.each(data, function(i, item) {
780 $this.currentItems.push(item.label);
781 });
782 }
783
784 //adjust height
785 if($this.options.scrollHeight) {
786 var heightConstraint = hidden ? $this.panel.height() : $this.panel.children().height();
787
788 if(heightConstraint > $this.options.scrollHeight)
789 $this.panel.height($this.options.scrollHeight);
790 else
791 $this.panel.css('height', 'auto');
792
793 }
794
795 if(hidden) {
796 $this.show();
797 }
798 else {
799 $this._alignPanel();
800 }
801 }
802 else {
803 this.panel.hide();
804 }
805 },
806
807 show: function() {
808 this._alignPanel();
809
810 if(this.options.effect)
811 this.panel.show(this.options.effect, {}, this.options.effectSpeed);
812 else
813 this.panel.show();
814 },
815
816 hide: function() {
817 this.panel.hide();
818 this.panel.css('height', 'auto');
819 },
820
821 _removeItem: function(item) {
822 item.fadeOut('fast', function() {
823 var token = $(this);
824
825 token.remove();
826 });
827 },
828
829 _alignPanel: function() {
830 var panelWidth = null;
831
832 if(this.options.multiple) {
833 panelWidth = this.multiContainer.innerWidth() - (this.element.position().left - this.multiContainer.position().left);
834 }
835 else {
836 if(this.panel.is(':visible')) {
837 panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
838 }
839 else {
840 this.panel.css({'visibility':'hidden','display':'block'});
841 panelWidth = this.panel.children('.ui-autocomplete-items').outerWidth();
842 this.panel.css({'visibility':'visible','display':'none'});
843 }
844
845 var inputWidth = this.element.outerWidth();
846 if(panelWidth < inputWidth) {
847 panelWidth = inputWidth;
848 }
849 }
850
851 this.panel.css({
852 'left':'',
853 'top':'',
854 'width': panelWidth,
855 'z-index': ++PUI.zindex
856 })
857 .position({
858 my: 'left top',
859 at: 'left bottom',
860 of: this.element
861 });
862 }
863 });
864
865})();
866/**
867 * PrimeFaces Button Widget
868 */
869(function() {
870
871 $.widget("primeui.puibutton", {
872
873 options: {
874 value: null,
875 icon: null,
876 iconPos: 'left',
877 click: null
878 },
879
880 _create: function() {
881 var element = this.element;
882 this.elementText = this.element.text();
883
884 var value = this.options.value||(this.elementText === '' ? 'ui-button' : this.elementText),
885 disabled = element.prop('disabled'),
886 styleClass = null;
887
888 if(this.options.icon) {
889 styleClass = (value === 'ui-button') ? 'ui-button-icon-only' : 'ui-button-text-icon-' + this.options.iconPos;
890 }
891 else {
892 styleClass = 'ui-button-text-only';
893 }
894
895 if(disabled) {
896 styleClass += ' ui-state-disabled';
897 }
898
899 this.element.addClass('ui-button ui-widget ui-state-default ui-corner-all ' + styleClass).text('');
900
901 if(this.options.icon) {
902 this.element.append('<span class="ui-button-icon-' + this.options.iconPos + ' ui-c fa fa-fw ' + this.options.icon + '" />');
903 }
904
905 this.element.append('<span class="ui-button-text ui-c">' + value + '</span>');
906
907 if(!disabled) {
908 this._bindEvents();
909 }
910 },
911
912 _destroy: function() {
913 this.element.removeClass('ui-button ui-widget ui-state-default ui-state-hover ui-state-active ui-state-disabled ui-state-focus ui-corner-all ' +
914 'ui-button-text-only ui-button-icon-only ui-button-text-icon-right ui-button-text-icon-left');
915 this._unbindEvents();
916 this.element.children('.fa').remove();
917 this.element.children('.ui-button-text').remove();
918 this.element.text(this.elementText);
919 },
920
921 _bindEvents: function() {
922 var element = this.element,
923 $this = this;
924
925 element.on('mouseover.puibutton', function(){
926 if(!element.prop('disabled')) {
927 element.addClass('ui-state-hover');
928 }
929 }).on('mouseout.puibutton', function() {
930 $(this).removeClass('ui-state-active ui-state-hover');
931 }).on('mousedown.puibutton', function() {
932 if(!element.hasClass('ui-state-disabled')) {
933 element.addClass('ui-state-active').removeClass('ui-state-hover');
934 }
935 }).on('mouseup.puibutton', function(e) {
936 element.removeClass('ui-state-active').addClass('ui-state-hover');
937
938 $this._trigger('click', e);
939 }).on('focus.puibutton', function() {
940 element.addClass('ui-state-focus');
941 }).on('blur.puibutton', function() {
942 element.removeClass('ui-state-focus');
943 }).on('keydown.puibutton',function(e) {
944 if(e.keyCode == $.ui.keyCode.SPACE || e.keyCode == $.ui.keyCode.ENTER || e.keyCode == $.ui.keyCode.NUMPAD_ENTER) {
945 element.addClass('ui-state-active');
946 }
947 }).on('keyup.puibutton', function() {
948 element.removeClass('ui-state-active');
949 });
950
951 return this;
952 },
953
954 _unbindEvents: function() {
955 this.element.off('mouseover.puibutton mouseout.puibutton mousedown.puibutton mouseup.puibutton focus.puibutton blur.puibutton keydown.puibutton keyup.puibutton');
956 },
957
958 disable: function() {
959 this._unbindEvents();
960 this.element.addClass('ui-state-disabled').prop('disabled',true);
961 },
962
963 enable: function() {
964 if(this.element.prop('disabled')) {
965 this._bindEvents();
966 this.element.prop('disabled', false).removeClass('ui-state-disabled');
967 }
968 },
969
970 _setOption: function(key, value) {
971 if(key === 'disabled') {
972 if(value)
973 this.disable();
974 else
975 this.enable();
976 }
977 else {
978 $.Widget.prototype._setOption.apply(this, arguments);
979 }
980 }
981
982 });
983})();
984/**
985 * PrimeUI Carousel widget
986 */
987(function() {
988
989 $.widget("primeui.puicarousel", {
990
991 options: {
992 datasource: null,
993 numVisible: 3,
994 firstVisible: 0,
995 headerText: null,
996 effectDuration: 500,
997 circular :false,
998 breakpoint: 560,
999 itemContent: null,
1000 responsive: true,
1001 autoplayInterval: 0,
1002 easing: 'easeInOutCirc',
1003 pageLinks: 3,
1004 style: null,
1005 styleClass: null,
1006 template: null,
1007 enhanced: false
1008 },
1009
1010 _create: function() {
1011 this.id = this.element.attr('id');
1012 if(!this.id) {
1013 this.id = this.element.uniqueId().attr('id');
1014 }
1015
1016 if(!this.options.enhanced) {
1017 this.element.wrap('<div class="ui-carousel ui-widget ui-widget-content ui-corner-all"><div class="ui-carousel-viewport"></div></div>');
1018 }
1019
1020 this.container = this.element.parent().parent();
1021 this.element.addClass('ui-carousel-items');
1022 this.viewport = this.element.parent();
1023 this.container.prepend('<div class="ui-carousel-header ui-widget-header"><div class="ui-carousel-header-title"></div></div>');
1024 this.header = this.container.children('.ui-carousel-header');
1025 this.header.append('<span class="ui-carousel-button ui-carousel-next-button fa fa-arrow-circle-right"></span>' +
1026 '<span class="ui-carousel-button ui-carousel-prev-button fa fa-arrow-circle-left"></span>');
1027
1028 if(this.options.headerText) {
1029 this.header.children('.ui-carousel-header-title').html(this.options.headerText);
1030 }
1031
1032 if(this.options.styleClass) {
1033 this.container.addClass(this.options.styleClass);
1034 }
1035
1036 if(this.options.style) {
1037 this.container.attr('style', this.options.style);
1038 }
1039
1040 if(this.options.datasource)
1041 this._loadData();
1042 else
1043 this._render();
1044 },
1045
1046 _destroy: function() {
1047 this._unbindEvents();
1048 this.header.remove();
1049 this.items.removeClass('ui-carousel-item ui-widget-content ui-corner-all').css('width','auto');
1050 this.element.removeClass('ui-carousel-items').css('left','auto');
1051
1052 if(!this.options.enhanced) {
1053 this.element.unwrap().unwrap();
1054 }
1055
1056 if(this.options.datasource) {
1057 this.items.remove();
1058 }
1059 },
1060
1061 _loadData: function() {
1062 if($.isArray(this.options.datasource))
1063 this._render(this.options.datasource);
1064 else if($.type(this.options.datasource) === 'function')
1065 this.options.datasource.call(this, this._render);
1066 },
1067
1068 _updateDatasource: function(value) {
1069 this.options.datasource = value;
1070 this.element.children().remove();
1071 this.header.children('.ui-carousel-page-links').remove();
1072 this.header.children('select').remove();
1073 this._loadData();
1074 },
1075
1076 _render: function(data) {
1077 this.data = data;
1078
1079 if(this.data) {
1080 for(var i = 0; i < data.length; i++) {
1081 var itemContent = this._createItemContent(data[i]);
1082 if($.type(itemContent) === 'string')
1083 this.element.append('<li>' + itemContent + '</li>');
1084 else
1085 this.element.append($('<li></li>').wrapInner(itemContent));
1086 }
1087 }
1088
1089 this.items = this.element.children('li');
1090 this.items.addClass('ui-carousel-item ui-widget-content ui-corner-all');
1091 this.itemsCount = this.items.length;
1092 this.columns = this.options.numVisible;
1093 this.first = this.options.firstVisible;
1094 this.page = parseInt(this.first/this.columns);
1095 this.totalPages = Math.ceil(this.itemsCount/this.options.numVisible);
1096
1097 this._renderPageLinks();
1098
1099 this.prevNav = this.header.children('.ui-carousel-prev-button');
1100 this.nextNav = this.header.children('.ui-carousel-next-button');
1101 this.pageLinks = this.header.find('> .ui-carousel-page-links > .ui-carousel-page-link');
1102 this.dropdown = this.header.children('.ui-carousel-dropdown');
1103 this.mobileDropdown = this.header.children('.ui-carousel-mobiledropdown');
1104
1105 this._bindEvents();
1106
1107 if(this.options.responsive) {
1108 this.refreshDimensions();
1109 }
1110 else {
1111 this.calculateItemWidths();
1112 this.container.width(this.container.width());
1113 this.updateNavigators();
1114 }
1115 },
1116
1117 _renderPageLinks: function() {
1118 if(this.totalPages <= this.options.pageLinks) {
1119 this.pageLinksContainer = $('<div class="ui-carousel-page-links"></div>');
1120 for(var i = 0; i < this.totalPages; i++) {
1121 this.pageLinksContainer.append('<a href="#" class="ui-carousel-page-link fa fa-circle-o"></a>');
1122 }
1123 this.header.append(this.pageLinksContainer);
1124 }
1125 else {
1126 this.dropdown = $('<select class="ui-carousel-dropdown ui-widget ui-state-default ui-corner-left"></select>');
1127 for(var i = 0; i < this.totalPages; i++) {
1128 var pageNumber = (i+1);
1129 this.dropdown.append('<option value="' + pageNumber + '">' + pageNumber + '</option>');
1130 }
1131 this.header.append(this.dropdown);
1132 }
1133
1134 if(this.options.responsive) {
1135 this.mobileDropdown = $('<select class="ui-carousel-mobiledropdown ui-widget ui-state-default ui-corner-left"></select>');
1136 for(var i = 0; i < this.itemsCount; i++) {
1137 var pageNumber = (i+1);
1138 this.mobileDropdown.append('<option value="' + pageNumber + '">' + pageNumber + '</option>');
1139 }
1140 this.header.append(this.mobileDropdown);
1141 }
1142 },
1143
1144 calculateItemWidths: function() {
1145 var firstItem = this.items.eq(0);
1146 if(firstItem.length) {
1147 var itemFrameWidth = firstItem.outerWidth(true) - firstItem.width(); //sum of margin, border and padding
1148 this.items.width((this.viewport.innerWidth() - itemFrameWidth * this.columns) / this.columns);
1149 }
1150 },
1151
1152 refreshDimensions: function() {
1153 var win = $(window);
1154 if(win.width() <= this.options.breakpoint) {
1155 this.columns = 1;
1156 this.calculateItemWidths(this.columns);
1157 this.totalPages = this.itemsCount;
1158 this.mobileDropdown.show();
1159 this.pageLinks.hide();
1160 }
1161 else {
1162 this.columns = this.options.numVisible;
1163 this.calculateItemWidths();
1164 this.totalPages = Math.ceil(this.itemsCount / this.options.numVisible);
1165 this.mobileDropdown.hide();
1166 this.pageLinks.show();
1167 }
1168
1169 this.page = parseInt(this.first / this.columns);
1170 this.updateNavigators();
1171 this.element.css('left', (-1 * (this.viewport.innerWidth() * this.page)));
1172 },
1173
1174 _bindEvents: function() {
1175 var $this = this;
1176
1177 if(this.eventsBound) {
1178 return;
1179 }
1180
1181 this.prevNav.on('click.puicarousel', function() {
1182 if($this.page !== 0) {
1183 $this.setPage($this.page - 1);
1184 }
1185 else if($this.options.circular) {
1186 $this.setPage($this.totalPages - 1);
1187 }
1188 });
1189
1190 this.nextNav.on('click.puicarousel', function() {
1191 var lastPage = ($this.page === ($this.totalPages - 1));
1192
1193 if(!lastPage) {
1194 $this.setPage($this.page + 1);
1195 }
1196 else if($this.options.circular) {
1197 $this.setPage(0);
1198 }
1199 });
1200
1201 if($.swipe) {
1202 this.element.swipe({
1203 swipe:function(event, direction) {
1204 if(direction === 'left') {
1205 if($this.page === ($this.totalPages - 1)) {
1206 if($this.options.circular)
1207 $this.setPage(0);
1208 }
1209 else {
1210 $this.setPage($this.page + 1);
1211 }
1212 }
1213 else if(direction === 'right') {
1214 if($this.page === 0) {
1215 if($this.options.circular)
1216 $this.setPage($this.totalPages - 1);
1217 }
1218 else {
1219 $this.setPage($this.page - 1);
1220 }
1221 }
1222 }
1223 });
1224 }
1225
1226 if(this.pageLinks.length) {
1227 this.pageLinks.on('click.puicarousel', function(e) {
1228 $this.setPage($(this).index());
1229 e.preventDefault();
1230 });
1231 }
1232
1233 this.header.children('select').on('change.puicarousel', function() {
1234 $this.setPage(parseInt($(this).val()) - 1);
1235 });
1236
1237 if(this.options.autoplayInterval) {
1238 this.options.circular = true;
1239 this.startAutoplay();
1240 }
1241
1242 if(this.options.responsive) {
1243 var resizeNS = 'resize.' + this.id;
1244 $(window).off(resizeNS).on(resizeNS, function() {
1245 $this.refreshDimensions();
1246 });
1247 }
1248
1249 this.eventsBound = true;
1250 },
1251
1252 _unbindEvents: function() {
1253 this.prevNav.off('click.puicarousel');
1254 this.nextNav.off('click.puicarousel');
1255 if(this.pageLinks.length) {
1256 this.pageLinks.off('click.puicarousel');
1257 }
1258 this.header.children('select').off('change.puicarousel');
1259
1260 if(this.options.autoplayInterval) {
1261 this.stopAutoplay();
1262 }
1263
1264 if(this.options.responsive) {
1265 $(window).off('resize.' + this.id)
1266 }
1267 },
1268
1269 updateNavigators: function() {
1270 if(!this.options.circular) {
1271 if(this.page === 0) {
1272 this.prevNav.addClass('ui-state-disabled');
1273 this.nextNav.removeClass('ui-state-disabled');
1274 }
1275 else if(this.page === (this.totalPages - 1)) {
1276 this.prevNav.removeClass('ui-state-disabled');
1277 this.nextNav.addClass('ui-state-disabled');
1278 }
1279 else {
1280 this.prevNav.removeClass('ui-state-disabled');
1281 this.nextNav.removeClass('ui-state-disabled');
1282 }
1283 }
1284
1285 if(this.pageLinks.length) {
1286 this.pageLinks.filter('.fa-dot-circle-o').removeClass('fa-dot-circle-o');
1287 this.pageLinks.eq(this.page).addClass('fa-dot-circle-o');
1288 }
1289
1290 if(this.dropdown.length) {
1291 this.dropdown.val(this.page + 1);
1292 }
1293
1294 if(this.mobileDropdown.length) {
1295 this.mobileDropdown.val(this.page + 1);
1296 }
1297 },
1298
1299 setPage: function(p) {
1300 if(p !== this.page && !this.element.is(':animated')) {
1301 var $this = this;
1302
1303 this.element.animate({
1304 left: -1 * (this.viewport.innerWidth() * p)
1305 ,easing: this.options.easing
1306 },
1307 {
1308 duration: this.options.effectDuration,
1309 easing: this.options.easing,
1310 complete: function() {
1311 $this.page = p;
1312 $this.first = $this.page * $this.columns;
1313 $this.updateNavigators();
1314 $this._trigger('pageChange', null, {'page':p});
1315 }
1316 });
1317 }
1318 },
1319
1320 startAutoplay: function() {
1321 var $this = this;
1322
1323 this.interval = setInterval(function() {
1324 if($this.page === ($this.totalPages - 1))
1325 $this.setPage(0);
1326 else
1327 $this.setPage($this.page + 1);
1328 }, this.options.autoplayInterval);
1329 },
1330
1331 stopAutoplay: function() {
1332 clearInterval(this.interval);
1333 },
1334
1335 _setOption: function(key, value) {
1336 if(key === 'datasource')
1337 this._updateDatasource(value);
1338 else
1339 $.Widget.prototype._setOption.apply(this, arguments);
1340 },
1341
1342 _createItemContent: function(obj) {
1343 if(this.options.template) {
1344 var template = this.options.template.html();
1345 Mustache.parse(template);
1346 return Mustache.render(template, obj);
1347 }
1348 else {
1349 return this.options.itemContent.call(this, obj);
1350 }
1351 }
1352
1353 });
1354
1355})();
1356/**
1357 * PrimeUI checkbox widget
1358 */
1359(function() {
1360
1361 $.widget("primeui.puicheckbox", {
1362
1363 _create: function() {
1364 this.element.wrap('<div class="ui-chkbox ui-widget"><div class="ui-helper-hidden-accessible"></div></div>');
1365 this.container = this.element.parent().parent();
1366 this.box = $('<div class="ui-chkbox-box ui-widget ui-corner-all ui-state-default">').appendTo(this.container);
1367 this.icon = $('<span class="ui-chkbox-icon ui-c"></span>').appendTo(this.box);
1368 this.disabled = this.element.prop('disabled');
1369 this.label = $('label[for="' + this.element.attr('id') + '"]');
1370
1371 if(this.isChecked()) {
1372 this.box.addClass('ui-state-active');
1373 this.icon.addClass('fa fa-fw fa-check');
1374 }
1375
1376 if(this.disabled) {
1377 this.box.addClass('ui-state-disabled');
1378 } else {
1379 this._bindEvents();
1380 }
1381 },
1382
1383 _bindEvents: function() {
1384 var $this = this;
1385
1386 this.box.on('mouseover.puicheckbox', function() {
1387 if(!$this.isChecked())
1388 $this.box.addClass('ui-state-hover');
1389 })
1390 .on('mouseout.puicheckbox', function() {
1391 $this.box.removeClass('ui-state-hover');
1392 })
1393 .on('click.puicheckbox', function() {
1394 $this.toggle();
1395 });
1396
1397 this.element.on('focus.puicheckbox', function() {
1398 if($this.isChecked()) {
1399 $this.box.removeClass('ui-state-active');
1400 }
1401
1402 $this.box.addClass('ui-state-focus');
1403 })
1404 .on('blur.puicheckbox', function() {
1405 if($this.isChecked()) {
1406 $this.box.addClass('ui-state-active');
1407 }
1408
1409 $this.box.removeClass('ui-state-focus');
1410 })
1411 .on('keydown.puicheckbox', function(e) {
1412 var keyCode = $.ui.keyCode;
1413 if(e.which == keyCode.SPACE) {
1414 e.preventDefault();
1415 }
1416 })
1417 .on('keyup.puicheckbox', function(e) {
1418 var keyCode = $.ui.keyCode;
1419 if(e.which == keyCode.SPACE) {
1420 $this.toggle(true);
1421
1422 e.preventDefault();
1423 }
1424 });
1425
1426 this.label.on('click.puicheckbox', function(e) {
1427 $this.toggle();
1428 e.preventDefault();
1429 });
1430 },
1431
1432 toggle: function(keypress) {
1433 if(this.isChecked()) {
1434 this.uncheck(keypress);
1435 } else {
1436 this.check(keypress);
1437 }
1438
1439 this._trigger('change', null, this.isChecked());
1440 },
1441
1442 isChecked: function() {
1443 return this.element.prop('checked');
1444 },
1445
1446 check: function(activate, silent) {
1447 if(!this.isChecked()) {
1448 this.element.prop('checked', true);
1449 this.icon.addClass('fa fa-fw fa-check');
1450
1451 if(!activate) {
1452 this.box.addClass('ui-state-active');
1453 }
1454
1455 if(!silent) {
1456 this.element.trigger('change');
1457 }
1458 }
1459 },
1460
1461 uncheck: function() {
1462 if(this.isChecked()) {
1463 this.element.prop('checked', false);
1464 this.box.removeClass('ui-state-active');
1465 this.icon.removeClass('fa fa-fw fa-check');
1466
1467 this.element.trigger('change');
1468 }
1469 },
1470
1471 _unbindEvents: function() {
1472 this.box.off('mouseover.puicheckbox mouseout.puicheckbox click.puicheckbox');
1473 this.element.off('focus.puicheckbox blur.puicheckbox keydown.puicheckbox keyup.puicheckbox');
1474
1475 if (this.label.length) {
1476 this.label.off('click.puicheckbox');
1477 }
1478 },
1479
1480 disable: function() {
1481 this.box.prop('disabled', true);
1482 this.box.attr('aria-disabled', true);
1483 this.box.addClass('ui-state-disabled').removeClass('ui-state-hover');
1484 this._unbindEvents();
1485 },
1486
1487 enable: function() {
1488 this.box.prop('disabled', false);
1489 this.box.attr('aria-disabled', false);
1490 this.box.removeClass('ui-state-disabled');
1491 this._bindEvents();
1492 },
1493
1494 _destroy: function() {
1495 this._unbindEvents();
1496 this.container.removeClass('ui-chkbox ui-widget');
1497 this.box.remove();
1498 this.element.unwrap().unwrap();
1499 }
1500
1501 });
1502
1503})();
1504/**
1505 * PrimeUI Datagrid Widget
1506 */
1507(function() {
1508
1509 $.widget("primeui.puidatagrid", {
1510
1511 options: {
1512 columns: 3,
1513 datasource: null,
1514 paginator: null,
1515 header: null,
1516 footer: null,
1517 content: null,
1518 lazy: false,
1519 template: null
1520 },
1521
1522 _create: function() {
1523 this.id = this.element.attr('id');
1524 if(!this.id) {
1525 this.id = this.element.uniqueId().attr('id');
1526 }
1527
1528 this.element.addClass('ui-datagrid ui-widget');
1529
1530 //header
1531 if(this.options.header) {
1532 this.element.append('<div class="ui-datagrid-header ui-widget-header ui-corner-top">' + this.options.header + '</div>');
1533 }
1534
1535 //content
1536 this.content = $('<div class="ui-datagrid-content ui-widget-content ui-datagrid-col-' + this.options.columns + '"></div>').appendTo(this.element);
1537
1538 //footer
1539 if(this.options.footer) {
1540 this.element.append('<div class="ui-datagrid-footer ui-widget-header ui-corner-top">' + this.options.footer + '</div>');
1541 }
1542
1543 //data
1544 if(this.options.datasource) {
1545 this._initDatasource();
1546 }
1547 },
1548
1549 _onDataInit: function(data) {
1550 this._onDataUpdate(data);
1551 this._initPaginator();
1552 },
1553
1554 _onDataUpdate: function(data) {
1555 this.data = data;
1556 if(!this.data) {
1557 this.data = [];
1558 }
1559
1560 this.reset();
1561
1562 this._renderData();
1563 },
1564
1565 _onLazyLoad: function(data) {
1566 this.data = data;
1567 if(!this.data) {
1568 this.data = [];
1569 }
1570
1571 this._renderData();
1572 },
1573
1574 reset: function() {
1575 if(this.paginator) {
1576 this.paginator.puipaginator('setState', {
1577 page: 0,
1578 totalRecords: this.options.lazy ? this.options.paginator.totalRecords : this.data.length
1579 });
1580 }
1581 },
1582
1583 paginate: function() {
1584 if(this.options.lazy) {
1585 this.options.datasource.call(this, this._onLazyLoad, this._createStateMeta());
1586 }
1587 else {
1588 this._renderData();
1589 }
1590 },
1591
1592 _renderData: function() {
1593 if(this.data) {
1594 this.content.html('');
1595
1596 var firstNonLazy = this._getFirst(),
1597 first = this.options.lazy ? 0 : firstNonLazy,
1598 rows = this._getRows(),
1599 gridRow = null;
1600
1601 for(var i = first; i < (first + rows); i++) {
1602 var dataValue = this.data[i];
1603
1604 if(dataValue) {
1605 var gridColumn = $('<div></div>').appendTo(this.content),
1606 markup = this._createItemContent(dataValue);
1607 gridColumn.append(markup);
1608 }
1609 }
1610 }
1611 },
1612
1613 _getFirst: function() {
1614 if(this.paginator) {
1615 var page = this.paginator.puipaginator('option', 'page'),
1616 rows = this.paginator.puipaginator('option', 'rows');
1617
1618 return (page * rows);
1619 }
1620 else {
1621 return 0;
1622 }
1623 },
1624
1625 _getRows: function() {
1626 if(this.options.paginator)
1627 return this.paginator ? this.paginator.puipaginator('option', 'rows') : this.options.paginator.rows;
1628 else
1629 return this.data ? this.data.length : 0;
1630 },
1631
1632 _createStateMeta: function() {
1633 var state = {
1634 first: this._getFirst(),
1635 rows: this._getRows()
1636 };
1637
1638 return state;
1639 },
1640
1641 _initPaginator: function() {
1642 var $this = this;
1643 if(this.options.paginator) {
1644 this.options.paginator.paginate = function(event, state) {
1645 $this.paginate();
1646 };
1647
1648 this.options.paginator.totalRecords = this.options.lazy ? this.options.paginator.totalRecords : this.data.length;
1649 this.paginator = $('<div></div>').insertAfter(this.content).puipaginator(this.options.paginator);
1650 }
1651 },
1652
1653 _initDatasource: function() {
1654 if($.isArray(this.options.datasource)) {
1655 this._onDataInit(this.options.datasource);
1656 }
1657 else {
1658 if($.type(this.options.datasource) === 'string') {
1659 var $this = this,
1660 dataURL = this.options.datasource;
1661
1662 this.options.datasource = function() {
1663 $.ajax({
1664 type: 'GET',
1665 url: dataURL,
1666 dataType: "json",
1667 context: $this,
1668 success: function (response) {
1669 this._onDataInit(response);
1670 }
1671 });
1672 };
1673 }
1674
1675 if($.type(this.options.datasource) === 'function') {
1676 if(this.options.lazy)
1677 this.options.datasource.call(this, this._onDataInit, {first:0, rows: this._getRows()});
1678 else
1679 this.options.datasource.call(this, this._onDataInit);
1680 }
1681 }
1682 },
1683
1684 _updateDatasource: function(datasource) {
1685 this.options.datasource = datasource;
1686
1687 if($.isArray(this.options.datasource)) {
1688 this._onDataUpdate(this.options.datasource);
1689 }
1690 else if($.type(this.options.datasource) === 'function') {
1691 if(this.options.lazy)
1692 this.options.datasource.call(this, this._onDataUpdate, {first:0, rows: this._getRows()});
1693 else
1694 this.options.datasource.call(this, this._onDataUpdate);
1695 }
1696 },
1697
1698 _setOption: function(key, value) {
1699 if(key === 'datasource') {
1700 this._updateDatasource(value);
1701 }
1702 else {
1703 $.Widget.prototype._setOption.apply(this, arguments);
1704 }
1705 },
1706
1707 _createItemContent: function(obj) {
1708 if(this.options.template) {
1709 var templateContent = this.options.template.html();
1710 Mustache.parse(templateContent);
1711 return Mustache.render(templateContent, obj);
1712 }
1713 else {
1714 return this.options.content.call(this, obj);
1715 }
1716 }
1717
1718 });
1719})();
1720/**
1721 * PrimeUI Datascroller Widget
1722 */
1723(function() {
1724
1725 $.widget("primeui.puidatascroller", {
1726
1727 options: {
1728 header: null,
1729 buffer: 0.9,
1730 chunkSize: 10,
1731 datasource: null,
1732 lazy: false,
1733 content: null,
1734 template: null,
1735 mode: 'document',
1736 loader: null,
1737 scrollHeight: null,
1738 totalSize: null
1739 },
1740
1741 _create: function() {
1742 this.id = this.element.attr('id');
1743 if(!this.id) {
1744 this.id = this.element.uniqueId().attr('id');
1745 }
1746
1747 this.element.addClass('ui-datascroller ui-widget');
1748 if(this.options.header) {
1749 this.header = this.element.append('<div class="ui-datascroller-header ui-widget-header ui-corner-top">' + this.options.header + '</div>').children('.ui-datascroller-header');
1750 }
1751
1752 this.content = this.element.append('<div class="ui-datascroller-content ui-widget-content ui-corner-bottom"></div>').children('.ui-datascroller-content');
1753 this.list = this.content.append('<ul class="ui-datascroller-list"></ul>').children('.ui-datascroller-list');
1754 this.loaderContainer = this.content.append('<div class="ui-datascroller-loader"></div>').children('.ui-datascroller-loader');
1755 this.loadStatus = $('<div class="ui-datascroller-loading"></div>');
1756 this.loading = false;
1757 this.allLoaded = false;
1758 this.offset = 0;
1759
1760 if(this.options.mode === 'self') {
1761 this.element.addClass('ui-datascroller-inline');
1762
1763 if(this.options.scrollHeight) {
1764 this.content.css('height', this.options.scrollHeight);
1765 }
1766 }
1767
1768 if(this.options.loader) {
1769 this.bindManualLoader();
1770 }
1771 else {
1772 this.bindScrollListener();
1773 }
1774
1775 if(this.options.datasource) {
1776 if($.isArray(this.options.datasource)) {
1777 this._onDataInit(this.options.datasource);
1778 }
1779 else {
1780 if($.type(this.options.datasource) === 'string') {
1781 var $this = this,
1782 dataURL = this.options.datasource;
1783
1784 this.options.datasource = function() {
1785 $.ajax({
1786 type: 'GET',
1787 url: dataURL,
1788 dataType: "json",
1789 context: $this,
1790 success: function (response) {
1791 this._onDataInit(response);
1792 }
1793 });
1794 };
1795 }
1796
1797 if($.type(this.options.datasource) === 'function') {
1798 if(this.options.lazy)
1799 this.options.datasource.call(this, this._onLazyLoad, {first:this.offset});
1800 else
1801 this.options.datasource.call(this, this._onDataInit);
1802 }
1803 }
1804 }
1805 },
1806
1807 _onDataInit: function(data) {
1808 this.data = data||[];
1809 this.options.totalSize = this.data.length;
1810
1811 this._load();
1812 },
1813
1814 _onLazyLoad: function(data) {
1815 this._renderData(data, 0, this.options.chunkSize);
1816
1817 this._onloadComplete();
1818 },
1819
1820 bindScrollListener: function() {
1821 var $this = this;
1822
1823 if(this.options.mode === 'document') {
1824 var win = $(window),
1825 doc = $(document),
1826 $this = this,
1827 NS = 'scroll.' + this.id;
1828
1829 win.off(NS).on(NS, function () {
1830 if(win.scrollTop() >= ((doc.height() * $this.options.buffer) - win.height()) && $this.shouldLoad()) {
1831 $this._load();
1832 }
1833 });
1834 }
1835 else {
1836 this.content.on('scroll', function () {
1837 var scrollTop = this.scrollTop,
1838 scrollHeight = this.scrollHeight,
1839 viewportHeight = this.clientHeight;
1840
1841 if((scrollTop >= ((scrollHeight * $this.options.buffer) - (viewportHeight))) && $this.shouldLoad()) {
1842 $this._load();
1843 }
1844 });
1845 }
1846 },
1847
1848 bindManualLoader: function() {
1849 var $this = this;
1850
1851 this.options.loader.on('click.dataScroller', function(e) {
1852 $this._load();
1853 e.preventDefault();
1854 });
1855 },
1856
1857 _load: function() {
1858 this.loading = true;
1859 this.loadStatus.appendTo(this.loaderContainer);
1860 if(this.options.loader) {
1861 this.options.loader.hide();
1862 }
1863
1864 if(this.options.lazy) {
1865 this.options.datasource.call(this, this._onLazyLoad, {first: this.offset});
1866 }
1867 else {
1868 this._renderData(this.data, this.offset, (this.offset + this.options.chunkSize));
1869 this._onloadComplete();
1870 }
1871 },
1872
1873 _renderData: function(data, start, end) {
1874 if(data && data.length) {
1875 for(var i = start; i < end; i++) {
1876 var listItem = $('<li class="ui-datascroller-item"></li>'),
1877 content = this._createItemContent(data[i]);
1878 listItem.append(content);
1879
1880 this.list.append(listItem);
1881 }
1882 }
1883 },
1884
1885 shouldLoad: function() {
1886 return (!this.loading && !this.allLoaded);
1887 },
1888
1889 _createItemContent: function(obj) {
1890 if(this.options.template) {
1891 var template = this.options.template.html();
1892 Mustache.parse(template);
1893 return Mustache.render(template, obj);
1894 }
1895 else {
1896 return this.options.content.call(this, obj);
1897 }
1898 },
1899
1900 _onloadComplete: function() {
1901 this.offset += this.options.chunkSize;
1902 this.loading = false;
1903 this.allLoaded = this.offset >= this.options.totalSize;
1904
1905 this.loadStatus.remove();
1906
1907 if(this.options.loader && !this.allLoaded) {
1908 this.options.loader.show();
1909 }
1910 }
1911
1912 });
1913
1914})();
1915/**
1916 * PrimeUI Datatable Widget
1917 */
1918(function() {
1919
1920 $.widget("primeui.puidatatable", {
1921
1922 options: {
1923 columns: null,
1924 datasource: null,
1925 paginator: null,
1926 globalFilter:null,
1927 selectionMode: null,
1928 caption: null,
1929 footer: null,
1930 sortField: null,
1931 sortOrder: 1,
1932 sortMeta: [],
1933 sortMode: null,
1934 scrollable: false,
1935 scrollHeight: null,
1936 scrollWidth: null,
1937 responsive: false,
1938 expandableRows: false,
1939 expandedRowContent: null,
1940 rowExpandMode: 'multiple',
1941 draggableColumns: false,
1942 resizableColumns: false,
1943 columnResizeMode: 'fit',
1944 draggableRows: false,
1945 filterDelay: 300,
1946 stickyHeader: false,
1947 editMode: null,
1948 tabindex: 0,
1949 emptyMessage: 'No records found',
1950 sort: null,
1951 rowSelect: null,
1952 rowUnselect: null,
1953 rowSelectContextMenu: null,
1954 rowCollapse: null,
1955 rowExpand: null,
1956 colReorder: null,
1957 colResize: null,
1958 rowReorder: null,
1959 cellEdit: null
1960 },
1961
1962 _create: function() {
1963 this.id = this.element.attr('id');
1964 if(!this.id) {
1965 this.id = this.element.uniqueId().attr('id');
1966 }
1967
1968 this.element.addClass('ui-datatable ui-widget');
1969 if(this.options.responsive) {
1970 this.element.addClass('ui-datatable-reflow');
1971 }
1972
1973 if(this.options.scrollable) {
1974 this._createScrollableDatatable();
1975 }
1976 else {
1977 this._createRegularDatatable();
1978 }
1979
1980 if(this.options.datasource) {
1981 if($.isArray(this.options.datasource)) {
1982 this._onDataInit(this.options.datasource);
1983 }
1984 else {
1985 if($.type(this.options.datasource) === 'string') {
1986 var $this = this,
1987 dataURL = this.options.datasource;
1988
1989 this.options.datasource = function() {
1990 $.ajax({
1991 type: 'GET',
1992 url: dataURL,
1993 dataType: "json",
1994 context: $this,
1995 success: function (response) {
1996 this._onDataInit(response);
1997 }
1998 });
1999 };
2000 }
2001
2002 if($.type(this.options.datasource) === 'function') {
2003 if(this.options.lazy)
2004 this.options.datasource.call(this, this._onDataInit, {first:0, rows:this._getRows(), sortField:this.options.sortField, sortOrder:this.options.sortOrder, filters: this._createFilterMap()});
2005 else
2006 this.options.datasource.call(this, this._onDataInit);
2007 }
2008 }
2009 }
2010 },
2011
2012 _createRegularDatatable: function() {
2013 this.tableWrapper = $('<div class="ui-datatable-tablewrapper" />').appendTo(this.element);
2014 this.table = $('<table><thead></thead><tbody></tbody></table>').appendTo(this.tableWrapper);
2015 this.thead = this.table.children('thead');
2016 this.tbody = this.table.children('tbody').addClass('ui-datatable-data ui-widget-content');
2017
2018 if(this.containsFooter()) {
2019 this.tfoot = this.thead.after('<tfoot></tfoot>').next();
2020 }
2021 },
2022
2023 _createScrollableDatatable: function() {
2024 this.element.append('<div class="ui-widget-header ui-datatable-scrollable-header"><div class="ui-datatable-scrollable-header-box"><table><thead></thead></table></div></div>')
2025 .append('<div class="ui-datatable-scrollable-body"><table><tbody></tbody></table></div>');
2026
2027 this.thead = this.element.find('> .ui-datatable-scrollable-header > .ui-datatable-scrollable-header-box > table > thead');
2028 this.tbody = this.element.find('> .ui-datatable-scrollable-body > table > tbody');
2029
2030 if(this.containsFooter()) {
2031 this.element.append('<div class="ui-widget-header ui-datatable-scrollable-footer"><div class="ui-datatable-scrollable-footer-box"><table><tfoot></tfoot></table></div></div>');
2032 this.tfoot = this.element.find('> .ui-datatable-scrollable-footer > .ui-datatable-scrollable-footer-box > table > tfoot');
2033 }
2034 },
2035
2036 _initialize: function() {
2037 var $this = this;
2038
2039 this._initHeader();
2040 this._initFooter();
2041
2042 if(this.options.caption) {
2043 this.element.prepend('<div class="ui-datatable-header ui-widget-header">' + this.options.caption + '</div>');
2044 }
2045
2046 if(this.options.paginator) {
2047 this.options.paginator.paginate = function(event, state) {
2048 $this.paginate();
2049 };
2050
2051 this.options.paginator.totalRecords = this.options.lazy ? this.options.paginator.totalRecords : this.data.length;
2052 this.paginator = $('<div></div>').insertAfter(this.tableWrapper).puipaginator(this.options.paginator);
2053 if(this.options.paginator.contentLeft) {
2054 this.paginator.prepend(this.options.paginator.contentLeft.call());
2055 }
2056 if(this.options.paginator.contentRight) {
2057 this.paginator.append(this.options.paginator.contentRight.call());
2058 }
2059 }
2060
2061 if(this.options.footer) {
2062 this.element.append('<div class="ui-datatable-footer ui-widget-header">' + this.options.footer + '</div>');
2063 }
2064
2065 if(this._isSortingEnabled()) {
2066 this._initSorting();
2067 }
2068
2069 if(this.hasFiltering) {
2070 this._initFiltering();
2071 }
2072
2073 if(this.options.selectionMode) {
2074 this._initSelection();
2075 }
2076
2077 if(this.options.expandableRows) {
2078 this._initExpandableRows();
2079 }
2080
2081 if(this.options.draggableColumns) {
2082 this._initDraggableColumns();
2083 }
2084
2085 if(this.options.stickyHeader) {
2086 this._initStickyHeader();
2087 }
2088
2089 if ((this.options.sortField && this.options.sortOrder) || this.options.sortMeta.length) {
2090 this.sortByDefault();
2091 }
2092 else {
2093 this._renderData();
2094 }
2095
2096 if(this.options.scrollable) {
2097 this._initScrolling();
2098 }
2099
2100 if(this.options.resizableColumns) {
2101 this._initResizableColumns();
2102 }
2103
2104 if(this.options.draggableRows) {
2105 this._initDraggableRows();
2106 }
2107
2108 if(this.options.editMode) {
2109 this._initEditing();
2110 }
2111
2112 },
2113
2114 _initHeader: function() {
2115 if(this.options.headerRows) {
2116 for(var i = 0; i < this.options.headerRows.length; i++) {
2117 this._initHeaderColumns(this.options.headerRows[i].columns);
2118 }
2119 }
2120 else if(this.options.columns) {
2121 this._initHeaderColumns(this.options.columns);
2122 }
2123 },
2124
2125 _initFooter: function() {
2126 if(this.containsFooter()) {
2127 if(this.options.footerRows) {
2128 for(var i = 0; i < this.options.footerRows.length; i++) {
2129 this._initFooterColumns(this.options.footerRows[i].columns);
2130 }
2131 }
2132 else if(this.options.columns) {
2133 this._initFooterColumns(this.options.columns);
2134 }
2135 }
2136 },
2137
2138 _initHeaderColumns: function(columns) {
2139 var headerRow = $('<tr class="ui-state-default"></tr>').appendTo(this.thead),
2140 $this = this;
2141
2142 $.each(columns, function(i, col) {
2143 var cell = $('<th class="ui-state-default"><span class="ui-column-title"></span></th>').data('field', col.field).uniqueId().appendTo(headerRow);
2144
2145 if(col.headerClass) {
2146 cell.addClass(col.headerClass);
2147 }
2148
2149 if(col.headerStyle) {
2150 cell.attr('style', col.headerStyle);
2151 }
2152
2153 if(col.headerText)
2154 cell.children('.ui-column-title').text(col.headerText);
2155 else if(col.headerContent)
2156 cell.children('.ui-column-title').append(col.headerContent.call(this, col));
2157
2158 if(col.rowspan) {
2159 cell.attr('rowspan', col.rowspan);
2160 }
2161
2162 if(col.colspan) {
2163 cell.attr('colspan', col.colspan);
2164 }
2165
2166 if(col.sortable) {
2167 cell.addClass('ui-sortable-column')
2168 .data('order', 0)
2169 .append('<span class="ui-sortable-column-icon fa fa-fw fa-sort"></span>');
2170 }
2171
2172 if(col.filter) {
2173 $this.hasFiltering = true;
2174
2175 var filterElement = $('<input type="text" class="ui-column-filter" />').puiinputtext().data({
2176 'field': col.field,
2177 'filtermatchmode': col.filterMatchMode||'startsWith'
2178 }).appendTo(cell);
2179
2180 if(col.filterFunction) {
2181 filterElement.on('filter', function(event, dataValue, filterValue) {
2182 return col.filterFunction.call($this, dataValue, filterValue);
2183 });
2184 }
2185 }
2186 });
2187 },
2188
2189 _initFooterColumns: function(columns) {
2190 var footerRow = $('<tr></tr>').appendTo(this.tfoot);
2191 $.each(columns, function(i, col) {
2192 var cell = $('<td class="ui-state-default"></td>');
2193 if(col.footerText) {
2194 cell.text(col.footerText);
2195 }
2196
2197 if(col.rowspan) {
2198 cell.attr('rowspan', col.rowspan);
2199 }
2200
2201 if(col.colspan) {
2202 cell.attr('colspan', col.colspan);
2203 }
2204
2205 cell.appendTo(footerRow);
2206 });
2207 },
2208
2209 _indicateInitialSortColumn: function(sortField, sortOrder) {
2210 var $this = this;
2211
2212 $.each(this.sortableColumns, function(i, column) {
2213 var $column = $(column),
2214 data = $column.data();
2215
2216 if (sortField === data.field) {
2217 var sortIcon = $column.children('.ui-sortable-column-icon');
2218 $column.data('order', sortOrder).removeClass('ui-state-hover').addClass('ui-state-active');
2219
2220 if(sortOrder == -1)
2221 sortIcon.removeClass('fa-sort fa-sort-asc').addClass('fa-sort-desc');
2222 else if(sortOrder == 1)
2223 sortIcon.removeClass('fa-sort fa-sort-desc').addClass('fa-sort-asc');
2224 }
2225 });
2226 },
2227
2228 _indicateInitialSortColumns: function() {
2229 var $this = this;
2230
2231 for(var i = 0; i < this.options.sortMeta.length; i++) {
2232 var meta = this.options.sortMeta[i];
2233 this._indicateInitialSortColumn(meta.field, meta.order);
2234 }
2235 },
2236
2237 _onDataInit: function(data) {
2238 this.data = data;
2239 if(!this.data) {
2240 this.data = [];
2241 }
2242
2243 this._initialize();
2244 },
2245
2246 _onDataUpdate: function(data) {
2247 this.data = data;
2248 if(!this.data) {
2249 this.data = [];
2250 }
2251
2252 this.reset();
2253
2254 this._renderData();
2255 },
2256
2257 _onLazyLoad: function(data) {
2258 this.data = data;
2259 if(!this.data) {
2260 this.data = [];
2261 }
2262
2263 this._renderData();
2264 },
2265
2266 reset: function() {
2267 if(this.options.selectionMode) {
2268 this.selection = [];
2269 }
2270
2271 if(this.paginator) {
2272 this.paginator.puipaginator('setState', {
2273 page: 0,
2274 totalRecords: this.options.lazy ? this.options.paginator.totalRecords : this.data.length
2275 });
2276 }
2277
2278 this.thead.find('> tr > th.ui-sortable-column').data('order', 0).filter('.ui-state-active').removeClass('ui-state-active')
2279 .children('span.ui-sortable-column-icon').removeClass('fa-sort-asc fa-sort-desc').addClass('fa-sort');
2280 },
2281
2282 _isMultiSort: function() {
2283 if(this.options.sortMode === 'multiple')
2284 return true;
2285 else
2286 return false;
2287 },
2288
2289 _resetSortState: function(column) {
2290 this.sortableColumns.filter('.ui-state-active').data('order', 0).removeClass('ui-state-active').children('span.ui-sortable-column-icon')
2291 .removeClass('fa-sort-asc fa-sort-desc').addClass('fa-sort');
2292 },
2293
2294 _initSorting: function() {
2295 var $this = this;
2296 this.sortableColumns = this.thead.find('> tr > th.ui-sortable-column');
2297
2298 this.sortableColumns.on('mouseover.puidatatable', function() {
2299 var column = $(this);
2300
2301 if(!column.hasClass('ui-state-active'))
2302 column.addClass('ui-state-hover');
2303 })
2304 .on('mouseout.puidatatable', function() {
2305 var column = $(this);
2306
2307 if(!column.hasClass('ui-state-active'))
2308 column.removeClass('ui-state-hover');
2309 })
2310 .on('click.puidatatable', function(event) {
2311 if(!$(event.target).is('th,span')) {
2312 return;
2313 }
2314
2315 var column = $(this),
2316 sortField = column.data('field'),
2317 order = column.data('order'),
2318 sortOrder = (order === 0) ? 1 : (order * -1),
2319 sortIcon = column.children('.ui-sortable-column-icon'),
2320 metaKey = event.metaKey||event.ctrlKey;
2321
2322 if($this._isMultiSort()) {
2323 if(metaKey) {
2324 $this._addSortMeta({field: sortField, order: sortOrder});
2325 $this.sort();
2326 }
2327 else {
2328 $this.options.sortMeta = [];
2329 $this._addSortMeta({field: sortField, order: sortOrder});
2330 $this._resetSortState(column);
2331 $this.sort();
2332 }
2333 }
2334 else {
2335 //update state
2336 $this.options.sortField = sortField;
2337 $this.options.sortOrder = sortOrder;
2338
2339 $this._resetSortState(column);
2340 $this.sort();
2341 }
2342
2343 //update visuals
2344 column.data('order', sortOrder).removeClass('ui-state-hover').addClass('ui-state-active');
2345 if(sortOrder === -1)
2346 sortIcon.removeClass('fa-sort fa-sort-asc').addClass('fa-sort-desc');
2347 else if(sortOrder === 1)
2348 sortIcon.removeClass('fa-sort fa-sort-desc').addClass('fa-sort-asc');
2349
2350 $this._trigger('sort', event, {'sortOrder': sortOrder, 'sortField': sortField});
2351 });
2352 },
2353
2354 _addSortMeta: function(meta) {
2355 var index = -1;
2356 for(var i = 0; i < this.options.sortMeta.length; i++) {
2357 if(this.options.sortMeta[i].field === meta.field) {
2358 index = i;
2359 }
2360 }
2361
2362 if(index >= 0)
2363 this.options.sortMeta[index] = meta;
2364 else
2365 this.options.sortMeta.push(meta);
2366 },
2367
2368 paginate: function() {
2369 if(this.options.lazy) {
2370 this.options.datasource.call(this, this._onLazyLoad, this._createStateMeta());
2371 }
2372 else {
2373 this._renderData();
2374 }
2375 },
2376
2377 _multipleSort: function() {
2378 var $this = this;
2379
2380 function multisort(data1,data2,sortMeta,index) {
2381 var value1 = data1[sortMeta[index].field],
2382 value2 = data2[sortMeta[index].field],
2383 result = null;
2384
2385 if (typeof value1 == 'string' || value1 instanceof String) {
2386 if (value1.localeCompare && (value1 != value2)) {
2387 return (sortMeta[index].order * value1.localeCompare(value2));
2388 }
2389 }
2390 else {
2391 result = (value1 < value2) ? -1 : 1;
2392 }
2393
2394 if(value1 == value2) {
2395 return (sortMeta.length - 1) > (index) ? (multisort(data1, data2, sortMeta, index + 1)) : 0;
2396 }
2397
2398 return (sortMeta[index].order * result);
2399 }
2400
2401 this.data.sort(function (data1,data2) {
2402 return multisort(data1, data2, $this.options.sortMeta, 0);
2403 });
2404
2405 this._renderData();
2406 },
2407
2408 sort: function() {
2409 if(this.options.lazy) {
2410 this.options.datasource.call(this, this._onLazyLoad, this._createStateMeta());
2411 }
2412 else {
2413 if(this._isMultiSort())
2414 this._multipleSort();
2415 else
2416 this._singleSort();
2417 }
2418 },
2419
2420 _singleSort: function() {
2421 var $this = this;
2422
2423 this.data.sort(function(data1, data2) {
2424 var value1 = data1[$this.options.sortField], value2 = data2[$this.options.sortField],
2425 result = null;
2426
2427 if (typeof value1 == 'string' || value1 instanceof String) {
2428 if ( value1.localeCompare ) {
2429 return ($this.options.sortOrder * value1.localeCompare(value2));
2430 }
2431 else {
2432 if (value1.toLowerCase) {
2433 value1 = value1.toLowerCase();
2434 }
2435 if (value2.toLowerCase) {
2436 value2 = value2.toLowerCase();
2437 }
2438 result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
2439
2440 }
2441 }
2442 else {
2443 result = (value1 < value2) ? -1 : (value1 > value2) ? 1 : 0;
2444 }
2445
2446 return ($this.options.sortOrder * result);
2447 });
2448
2449 if(this.paginator) {
2450 this.paginator.puipaginator('option', 'page', 0);
2451 }
2452
2453 this._renderData();
2454 },
2455
2456 sortByField: function(a, b) {
2457 var aName = a.name.toLowerCase();
2458 var bName = b.name.toLowerCase();
2459 return ((aName < bName) ? -1 : ((aName > bName) ? 1 : 0));
2460 },
2461
2462 sortByDefault: function() {
2463 if(this._isMultiSort()) {
2464 if(this.options.sortMeta) {
2465 this._indicateInitialSortColumns();
2466 this.sort();
2467 }
2468 }
2469 else {
2470 this._indicateInitialSortColumn(this.options.sortField, this.options.sortOrder);
2471 this.sort();
2472 }
2473 },
2474
2475 _renderData: function() {
2476 this.tbody.html('');
2477
2478 var dataToRender = this.filteredData||this.data;
2479 if(dataToRender && dataToRender.length) {
2480 var firstNonLazy = this._getFirst(),
2481 first = this.options.lazy ? 0 : firstNonLazy,
2482 rows = this._getRows();
2483
2484 for(var i = first; i < (first + rows); i++) {
2485 var rowData = dataToRender[i];
2486
2487 if(rowData) {
2488 var row = $('<tr class="ui-widget-content" />').appendTo(this.tbody),
2489 zebraStyle = (i%2 === 0) ? 'ui-datatable-even' : 'ui-datatable-odd',
2490 rowIndex = i;
2491
2492 row.addClass(zebraStyle);
2493 row.data('rowdata', rowData);
2494
2495 if(this.options.selectionMode && this._isSelected(rowData)) {
2496 row.addClass("ui-state-highlight");
2497 }
2498
2499 for(var j = 0; j < this.options.columns.length; j++) {
2500 var column = $('<td />').appendTo(row),
2501 columnOptions = this.options.columns[j];
2502
2503 if(columnOptions.bodyClass) {
2504 column.addClass(columnOptions.bodyClass);
2505 }
2506
2507 if(columnOptions.bodyStyle) {
2508 column.attr('style', columnOptions.bodyStyle);
2509 }
2510
2511 if(columnOptions.editor) {
2512 column.addClass('ui-editable-column').data({
2513 'editor': columnOptions.editor,
2514 'rowdata': rowData,
2515 'field': columnOptions.field
2516 });
2517 }
2518
2519 if(columnOptions.content) {
2520 var content = columnOptions.content.call(this, rowData, columnOptions);
2521 if($.type(content) === 'string')
2522 column.html(content);
2523 else
2524 column.append(content);
2525 }
2526 else if(columnOptions.rowToggler) {
2527 column.append('<div class="ui-row-toggler fa fa-fw fa-chevron-circle-right ui-c"></div>');
2528 }
2529 else if(columnOptions.field) {
2530 column.text(rowData[columnOptions.field]);
2531 }
2532
2533 if(this.options.responsive && columnOptions.headerText) {
2534 column.prepend('<span class="ui-column-title">' + columnOptions.headerText + '</span>');
2535 }
2536 }
2537 }
2538 }
2539 }
2540 else {
2541 var emptyRow = $('<tr class="ui-widget-content"></tr>').appendTo(this.tbody);
2542 var emptyColumn = $('<td></td>').attr('colspan',this.options.columns.length).appendTo(emptyRow);
2543 emptyColumn.html(this.options.emptyMessage);
2544 }
2545 },
2546
2547 _getFirst: function() {
2548 if(this.paginator) {
2549 var page = this.paginator.puipaginator('option', 'page'),
2550 rows = this.paginator.puipaginator('option', 'rows');
2551
2552 return (page * rows);
2553 }
2554 else {
2555 return 0;
2556 }
2557 },
2558
2559 _getRows: function() {
2560 return this.paginator ? this.paginator.puipaginator('option', 'rows') : (this.data ? this.data.length : 0);
2561 },
2562
2563 _isSortingEnabled: function() {
2564 var cols = this.options.columns;
2565 if(cols) {
2566 for(var i = 0; i < cols.length; i++) {
2567 if(cols[i].sortable) {
2568 return true;
2569 }
2570 }
2571 }
2572
2573 return false;
2574 },
2575
2576 _initSelection: function() {
2577 var $this = this;
2578 this.selection = [];
2579 this.rowSelector = '> tr.ui-widget-content:not(.ui-datatable-empty-message,.ui-datatable-unselectable)';
2580
2581 //shift key based range selection
2582 if(this._isMultipleSelection()) {
2583 this.originRowIndex = 0;
2584 this.cursorIndex = null;
2585 }
2586
2587 this.tbody.off('mouseover.puidatatable mouseout.puidatatable mousedown.puidatatable click.puidatatable', this.rowSelector)
2588 .on('mouseover.datatable', this.rowSelector, null, function() {
2589 var element = $(this);
2590
2591 if(!element.hasClass('ui-state-highlight')) {
2592 element.addClass('ui-state-hover');
2593 }
2594 })
2595 .on('mouseout.datatable', this.rowSelector, null, function() {
2596 var element = $(this);
2597
2598 if(!element.hasClass('ui-state-highlight')) {
2599 element.removeClass('ui-state-hover');
2600 }
2601 })
2602 .on('mousedown.datatable', this.rowSelector, null, function() {
2603 $this.mousedownOnRow = true;
2604 })
2605 .on('click.datatable', this.rowSelector, null, function(e) {
2606 $this._onRowClick(e, this);
2607 $this.mousedownOnRow = false;
2608 });
2609
2610 this._bindSelectionKeyEvents();
2611 },
2612
2613 _onRowClick: function(event, rowElement) {
2614 if(!$(event.target).is(':input,:button,a,.ui-c')) {
2615 var row = $(rowElement),
2616 selected = row.hasClass('ui-state-highlight'),
2617 metaKey = event.metaKey||event.ctrlKey,
2618 shiftKey = event.shiftKey;
2619
2620 this.focusedRow = row;
2621
2622 //unselect a selected row if metakey is on
2623 if(selected && metaKey) {
2624 this.unselectRow(row);
2625 }
2626 else {
2627 //unselect previous selection if this is single selection or multiple one with no keys
2628 if(this._isSingleSelection() || (this._isMultipleSelection() && !metaKey && !shiftKey)) {
2629 if (this._isMultipleSelection()) {
2630 var selections = this.getSelection();
2631 for (var i = 0; i < selections.length; i++) {
2632 this._trigger('rowUnselect', null, selections[i]);
2633 }
2634 }
2635
2636 this.unselectAllRows();
2637 }
2638
2639 this.selectRow(row, false, event);
2640 }
2641
2642 PUI.clearSelection();
2643 }
2644 },
2645
2646 onRowRightClick: function(event, rowElement) {
2647 var row = $(rowElement),
2648 selectedData = row.data('rowdata'),
2649 selected = row.hasClass('ui-state-highlight');
2650
2651 if(this._isSingleSelection() || !selected) {
2652 this.unselectAllRows();
2653 }
2654
2655 this.selectRow(row, true);
2656 this.dataSelectedByContextMenu = selectedData;
2657 this._trigger('rowSelectContextMenu', event, selectedData);
2658
2659 PUI.clearSelection();
2660 },
2661
2662 _bindSelectionKeyEvents: function() {
2663 var $this = this;
2664
2665 this.tbody.attr('tabindex', this.options.tabindex).on('focus', function(e) {
2666 //ignore mouse click on row
2667 if(!$this.mousedownOnRow) {
2668 $this.focusedRow = $this.tbody.children('tr.ui-widget-content').eq(0);
2669 $this.focusedRow.addClass('ui-state-hover');
2670 }
2671 })
2672 .on('blur', function() {
2673 if($this.focusedRow) {
2674 $this.focusedRow.removeClass('ui-state-hover');
2675 $this.focusedRow = null;
2676 }
2677 })
2678 .on('keydown', function(e) {
2679 var keyCode = $.ui.keyCode,
2680 key = e.which;
2681
2682 if($this.focusedRow) {
2683 switch(key) {
2684 case keyCode.UP:
2685 var prevRow = $this.focusedRow.prev('tr.ui-widget-content');
2686 if(prevRow.length) {
2687 $this.focusedRow.removeClass('ui-state-hover');
2688 $this.focusedRow = prevRow;
2689 $this.focusedRow.addClass('ui-state-hover');
2690 }
2691 e.preventDefault();
2692 break;
2693
2694 case keyCode.DOWN:
2695 var nextRow = $this.focusedRow.next('tr.ui-widget-content');
2696 if(nextRow.length) {
2697 $this.focusedRow.removeClass('ui-state-hover');
2698 $this.focusedRow = nextRow;
2699 $this.focusedRow.addClass('ui-state-hover');
2700 }
2701 e.preventDefault();
2702 break;
2703
2704 case keyCode.ENTER:
2705 case keyCode.NUMPAD_ENTER:
2706 case keyCode.SPACE:
2707 e.target = $this.focusedRow.children().eq(0).get(0);
2708 $this._onRowClick(e, $this.focusedRow.get(0));
2709 e.preventDefault();
2710 break;
2711
2712 default:
2713 break;
2714 };
2715 }
2716 });
2717
2718 },
2719
2720 _isSingleSelection: function() {
2721 return this.options.selectionMode === 'single';
2722 },
2723
2724 _isMultipleSelection: function() {
2725 return this.options.selectionMode === 'multiple';
2726 },
2727
2728 unselectAllRows: function() {
2729 this.tbody.children('tr.ui-state-highlight').removeClass('ui-state-highlight').attr('aria-selected', false);
2730 this.selection = [];
2731 },
2732
2733 unselectRow: function(row, silent) {
2734 var unselectedData = row.data('rowdata');
2735 row.removeClass('ui-state-highlight').attr('aria-selected', false);
2736
2737 this._removeSelection(unselectedData);
2738
2739 if(!silent) {
2740 this._trigger('rowUnselect', null, unselectedData);
2741 }
2742 },
2743
2744 selectRow: function(row, silent, event) {
2745 var selectedData = row.data('rowdata');
2746 row.removeClass('ui-state-hover').addClass('ui-state-highlight').attr('aria-selected', true);
2747
2748 this._addSelection(selectedData);
2749
2750 if(!silent) {
2751 this._trigger('rowSelect', event, selectedData);
2752 }
2753 },
2754
2755 getSelection: function() {
2756 return this.selection;
2757 },
2758
2759 _removeSelection: function(rowData) {
2760 this.selection = $.grep(this.selection, function(value) {
2761 return value !== rowData;
2762 });
2763 },
2764
2765 _addSelection: function(rowData) {
2766 if(!this._isSelected(rowData)) {
2767 this.selection.push(rowData);
2768 }
2769 },
2770
2771 _isSelected: function(rowData) {
2772 return PUI.inArray(this.selection, rowData);
2773 },
2774
2775 _initExpandableRows: function() {
2776 var $this = this,
2777 togglerSelector = '> tr > td > div.ui-row-toggler';
2778
2779 this.tbody.off('click', togglerSelector)
2780 .on('click', togglerSelector, null, function() {
2781 $this.toggleExpansion($(this));
2782 })
2783 .on('keydown', togglerSelector, null, function(e) {
2784 var key = e.which,
2785 keyCode = $.ui.keyCode;
2786
2787 if((key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER)) {
2788 $this.toggleExpansion($(this));
2789 e.preventDefault();
2790 }
2791 });
2792 },
2793
2794 toggleExpansion: function(toggler) {
2795 var row = toggler.closest('tr'),
2796 expanded = toggler.hasClass('fa-chevron-circle-down');
2797
2798 if(expanded) {
2799 toggler.addClass('fa-chevron-circle-right').removeClass('fa-chevron-circle-down').attr('aria-expanded', false);
2800
2801 this.collapseRow(row);
2802 this._trigger('rowCollapse', null, row.data('rowdata'));
2803 }
2804 else {
2805 if(this.options.rowExpandMode === 'single') {
2806 this.collapseAllRows();
2807 }
2808
2809 toggler.addClass('fa-chevron-circle-down').removeClass('fa-chevron-circle-right').attr('aria-expanded', true);
2810
2811 this.loadExpandedRowContent(row);
2812 }
2813 },
2814
2815 loadExpandedRowContent: function(row) {
2816 var expandedRow = $('<tr class="ui-expanded-row-content ui-datatable-unselectable ui-widget-content"><td colspan="' + this.options.columns.length + '"></td></tr>');
2817 expandedRow.children('td').append(this.options.expandedRowContent.call(this, row.data('rowdata')));
2818
2819 row.addClass('ui-expanded-row').after(expandedRow);
2820 this._trigger('rowExpand', null, row.data('rowdata'));
2821 },
2822
2823 collapseRow: function(row) {
2824 row.removeClass('ui-expanded-row').next('.ui-expanded-row-content').remove();
2825 },
2826
2827 collapseAllRows: function() {
2828 var $this = this;
2829
2830 this.getExpandedRows().each(function () {
2831 var expandedRow = $(this);
2832 $this.collapseRow(expandedRow);
2833
2834 var columns = expandedRow.children('td');
2835 for (var i = 0; i < columns.length; i++) {
2836 var column = columns.eq(i),
2837 toggler = column.children('.ui-row-toggler');
2838
2839 if (toggler.length) {
2840 toggler.addClass('fa-chevron-circle-right').removeClass('fa-chevron-circle-down');
2841 }
2842 }
2843 });
2844 },
2845
2846 getExpandedRows: function () {
2847 return this.tbody.children('.ui-expanded-row');
2848 },
2849
2850 _createStateMeta: function() {
2851 var state = {
2852 first: this._getFirst(),
2853 rows: this._getRows(),
2854 sortField: this.options.sortField,
2855 sortOrder: this.options.sortOrder,
2856 sortMeta: this.options.sortMeta,
2857 filters: this.filterMetaMap
2858 };
2859
2860 return state;
2861 },
2862
2863 _updateDatasource: function(datasource) {
2864 this.options.datasource = datasource;
2865
2866 if($.isArray(this.options.datasource)) {
2867 this._onDataUpdate(this.options.datasource);
2868 }
2869 else if($.type(this.options.datasource) === 'function') {
2870 if(this.options.lazy)
2871 this.options.datasource.call(this, this._onDataUpdate, {first:0, rows: this._getRows(), sortField:this.options.sortField, sortorder:this.options.sortOrder, filters: this._createFilterMap()});
2872 else
2873 this.options.datasource.call(this, this._onDataUpdate);
2874 }
2875 },
2876
2877 _setOption: function(key, value) {
2878 if(key === 'datasource') {
2879 this._updateDatasource(value);
2880 }
2881 else {
2882 $.Widget.prototype._setOption.apply(this, arguments);
2883 }
2884 },
2885
2886 _initScrolling: function() {
2887 this.scrollHeader = this.element.children('.ui-datatable-scrollable-header');
2888 this.scrollBody = this.element.children('.ui-datatable-scrollable-body');
2889 this.scrollHeaderBox = this.scrollHeader.children('.ui-datatable-scrollable-header-box');
2890 this.headerTable = this.scrollHeaderBox.children('table');
2891 this.bodyTable = this.scrollBody.children('table');
2892 this.percentageScrollHeight = this.options.scrollHeight && (this.options.scrollHeight.indexOf('%') !== -1);
2893 this.percentageScrollWidth = this.options.scrollWidth && (this.options.scrollWidth.indexOf('%') !== -1);
2894 var $this = this,
2895 scrollBarWidth = this.getScrollbarWidth() + 'px';
2896
2897 if(this.options.scrollHeight) {
2898 if(this.percentageScrollHeight)
2899 this.adjustScrollHeight();
2900 else
2901 this.scrollBody.css('max-height', this.options.scrollHeight + 'px');
2902
2903 if(this.hasVerticalOverflow()) {
2904 this.scrollHeaderBox.css('margin-right', scrollBarWidth);
2905 }
2906 }
2907
2908 this.fixColumnWidths();
2909
2910 if(this.options.scrollWidth) {
2911 if(this.percentageScrollWidth)
2912 this.adjustScrollWidth();
2913 else
2914 this.setScrollWidth(parseInt(this.options.scrollWidth));
2915 }
2916
2917 this.cloneHead();
2918
2919 this.scrollBody.on('scroll.dataTable', function() {
2920 var scrollLeft = $this.scrollBody.scrollLeft();
2921 $this.scrollHeaderBox.css('margin-left', -scrollLeft);
2922 });
2923
2924 this.scrollHeader.on('scroll.dataTable', function() {
2925 $this.scrollHeader.scrollLeft(0);
2926 });
2927
2928 var resizeNS = 'resize.' + this.id;
2929 $(window).off(resizeNS).on(resizeNS, function() {
2930 if($this.element.is(':visible')) {
2931 if($this.percentageScrollHeight)
2932 $this.adjustScrollHeight();
2933
2934 if($this.percentageScrollWidth)
2935 $this.adjustScrollWidth();
2936 }
2937 });
2938 },
2939
2940 cloneHead: function() {
2941 this.theadClone = this.thead.clone();
2942 this.theadClone.find('th').each(function() {
2943 var header = $(this);
2944 header.attr('id', header.attr('id') + '_clone');
2945 $(this).children().not('.ui-column-title').remove();
2946 });
2947 this.theadClone.removeAttr('id').addClass('ui-datatable-scrollable-theadclone').height(0).prependTo(this.bodyTable);
2948
2949 //align horizontal scroller on keyboard tab
2950 if(this.options.scrollWidth) {
2951 var clonedSortableColumns = this.theadClone.find('> tr > th.ui-sortable-column');
2952 clonedSortableColumns.each(function() {
2953 $(this).data('original', $(this).attr('id').split('_clone')[0]);
2954 });
2955
2956 clonedSortableColumns.on('blur.dataTable', function() {
2957 $(PUI.escapeClientId($(this).data('original'))).removeClass('ui-state-focus');
2958 })
2959 .on('focus.dataTable', function() {
2960 $(PUI.escapeClientId($(this).data('original'))).addClass('ui-state-focus');
2961 })
2962 .on('keydown.dataTable', function(e) {
2963 var key = e.which,
2964 keyCode = $.ui.keyCode;
2965
2966 if((key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER) && $(e.target).is(':not(:input)')) {
2967 $(PUI.escapeClientId($(this).data('original'))).trigger('click.dataTable', (e.metaKey||e.ctrlKey));
2968 e.preventDefault();
2969 }
2970 });
2971 }
2972 },
2973
2974 adjustScrollHeight: function() {
2975 var relativeHeight = this.element.parent().innerHeight() * (parseInt(this.options.scrollHeight) / 100),
2976 tableHeaderHeight = this.element.children('.ui-datatable-header').outerHeight(true),
2977 tableFooterHeight = this.element.children('.ui-datatable-footer').outerHeight(true),
2978 scrollersHeight = (this.scrollHeader.outerHeight(true) + this.scrollFooter.outerHeight(true)),
2979 paginatorsHeight = this.paginator ? this.paginator.getContainerHeight(true) : 0,
2980 height = (relativeHeight - (scrollersHeight + paginatorsHeight + tableHeaderHeight + tableFooterHeight));
2981
2982 this.scrollBody.css('max-height', height + 'px');
2983 },
2984
2985 adjustScrollWidth: function() {
2986 var width = parseInt((this.element.parent().innerWidth() * (parseInt(this.options.scrollWidth) / 100)));
2987 this.setScrollWidth(width);
2988 },
2989
2990 setOuterWidth: function(element, width) {
2991 var diff = element.outerWidth() - element.width();
2992 element.width(width - diff);
2993 },
2994
2995 setScrollWidth: function(width) {
2996 var $this = this;
2997 this.element.children('.ui-widget-header').each(function() {
2998 $this.setOuterWidth($(this), width);
2999 });
3000 this.scrollHeader.width(width);
3001 this.scrollBody.css('margin-right', 0).width(width);
3002 },
3003
3004 alignScrollBody: function() {
3005 var marginRight = this.hasVerticalOverflow() ? this.getScrollbarWidth() + 'px' : '0px';
3006
3007 this.scrollHeaderBox.css('margin-right', marginRight);
3008 },
3009
3010 getScrollbarWidth: function() {
3011 if(!this.scrollbarWidth) {
3012 this.scrollbarWidth = PUI.browser.webkit ? '15' : PUI.calculateScrollbarWidth();
3013 }
3014
3015 return this.scrollbarWidth;
3016 },
3017
3018 hasVerticalOverflow: function() {
3019 return (this.options.scrollHeight && this.bodyTable.outerHeight() > this.scrollBody.outerHeight())
3020 },
3021
3022 restoreScrollState: function() {
3023 var scrollState = this.scrollStateHolder.val(),
3024 scrollValues = scrollState.split(',');
3025
3026 this.scrollBody.scrollLeft(scrollValues[0]);
3027 this.scrollBody.scrollTop(scrollValues[1]);
3028 },
3029
3030 saveScrollState: function() {
3031 var scrollState = this.scrollBody.scrollLeft() + ',' + this.scrollBody.scrollTop();
3032
3033 this.scrollStateHolder.val(scrollState);
3034 },
3035
3036 clearScrollState: function() {
3037 this.scrollStateHolder.val('0,0');
3038 },
3039
3040 fixColumnWidths: function() {
3041 if(!this.columnWidthsFixed) {
3042 if(this.options.scrollable) {
3043 this.scrollHeaderBox.find('> table > thead > tr > th').each(function() {
3044 var headerCol = $(this),
3045 width = headerCol.width();
3046 headerCol.width(width);
3047 });
3048 }
3049 else {
3050 this.element.find('> .ui-datatable-tablewrapper > table > thead > tr > th').each(function() {
3051 var col = $(this);
3052 col.width(col.width());
3053 });
3054 }
3055
3056 this.columnWidthsFixed = true;
3057 }
3058 },
3059
3060 _initDraggableColumns: function() {
3061 var $this = this;
3062
3063 this.dragIndicatorTop = $('<span class="fa fa-arrow-down" style="position:absolute"/></span>').hide().appendTo(this.element);
3064 this.dragIndicatorBottom = $('<span class="fa fa-arrow-up" style="position:absolute"/></span>').hide().appendTo(this.element);
3065
3066 this.thead.find('> tr > th').draggable({
3067 appendTo: 'body',
3068 opacity: 0.75,
3069 cursor: 'move',
3070 scope: this.id,
3071 cancel: ':input,.ui-column-resizer',
3072 drag: function(event, ui) {
3073 var droppable = ui.helper.data('droppable-column');
3074
3075 if(droppable) {
3076 var droppableOffset = droppable.offset(),
3077 topArrowY = droppableOffset.top - 10,
3078 bottomArrowY = droppableOffset.top + droppable.height() + 8,
3079 arrowX = null;
3080
3081 //calculate coordinates of arrow depending on mouse location
3082 if(event.originalEvent.pageX >= droppableOffset.left + (droppable.width() / 2)) {
3083 var nextDroppable = droppable.next();
3084 if(nextDroppable.length == 1)
3085 arrowX = nextDroppable.offset().left - 9;
3086 else
3087 arrowX = droppable.offset().left + droppable.innerWidth() - 9;
3088
3089 ui.helper.data('drop-location', 1); //right
3090 }
3091 else {
3092 arrowX = droppableOffset.left - 9;
3093 ui.helper.data('drop-location', -1); //left
3094 }
3095
3096 $this.dragIndicatorTop.offset({
3097 'left': arrowX,
3098 'top': topArrowY - 3
3099 }).show();
3100
3101 $this.dragIndicatorBottom.offset({
3102 'left': arrowX,
3103 'top': bottomArrowY - 3
3104 }).show();
3105 }
3106 },
3107 stop: function(event, ui) {
3108 //hide dnd arrows
3109 $this.dragIndicatorTop.css({
3110 'left':0,
3111 'top':0
3112 }).hide();
3113
3114 $this.dragIndicatorBottom.css({
3115 'left':0,
3116 'top':0
3117 }).hide();
3118 },
3119 helper: function() {
3120 var header = $(this),
3121 helper = $('<div class="ui-widget ui-state-default" style="padding:4px 10px;text-align:center;"></div>');
3122
3123 helper.width(header.width());
3124 helper.height(header.height());
3125
3126 helper.html(header.html());
3127
3128 return helper.get(0);
3129 }
3130 }).droppable({
3131 hoverClass:'ui-state-highlight',
3132 tolerance:'pointer',
3133 scope: this.id,
3134 over: function(event, ui) {
3135 ui.helper.data('droppable-column', $(this));
3136 },
3137 drop: function(event, ui) {
3138 var draggedColumnHeader = ui.draggable,
3139 dropLocation = ui.helper.data('drop-location'),
3140 droppedColumnHeader = $(this),
3141 draggedColumnFooter = null,
3142 droppedColumnFooter = null;
3143
3144 var draggedCells = $this.tbody.find('> tr:not(.ui-expanded-row-content) > td:nth-child(' + (draggedColumnHeader.index() + 1) + ')'),
3145 droppedCells = $this.tbody.find('> tr:not(.ui-expanded-row-content) > td:nth-child(' + (droppedColumnHeader.index() + 1) + ')');
3146
3147 if($this.containsFooter()) {
3148 var footerColumns = $this.tfoot.find('> tr > td'),
3149 draggedColumnFooter = footerColumns.eq(draggedColumnHeader.index()),
3150 droppedColumnFooter = footerColumns.eq(droppedColumnHeader.index());
3151 }
3152
3153 //drop right
3154 if(dropLocation > 0) {
3155 /* TODO :Resizable columns
3156 * if($this.options.resizableColumns) {
3157 if(droppedColumnHeader.next().length) {
3158 droppedColumnHeader.children('span.ui-column-resizer').show();
3159 draggedColumnHeader.children('span.ui-column-resizer').hide();
3160 }
3161 }*/
3162
3163 draggedColumnHeader.insertAfter(droppedColumnHeader);
3164
3165 draggedCells.each(function(i, item) {
3166 $(this).insertAfter(droppedCells.eq(i));
3167 });
3168
3169 if(draggedColumnFooter && droppedColumnFooter) {
3170 draggedColumnFooter.insertAfter(droppedColumnFooter);
3171 }
3172
3173 //sync clone
3174 if($this.options.scrollable) {
3175 var draggedColumnClone = $(document.getElementById(draggedColumnHeader.attr('id') + '_clone')),
3176 droppedColumnClone = $(document.getElementById(droppedColumnHeader.attr('id') + '_clone'));
3177 draggedColumnClone.insertAfter(droppedColumnClone);
3178 }
3179 }
3180 //drop left
3181 else {
3182 draggedColumnHeader.insertBefore(droppedColumnHeader);
3183
3184 draggedCells.each(function(i, item) {
3185 $(this).insertBefore(droppedCells.eq(i));
3186 });
3187
3188 if(draggedColumnFooter && droppedColumnFooter) {
3189 draggedColumnFooter.insertBefore(droppedColumnFooter);
3190 }
3191
3192 //sync clone
3193 if($this.options.scrollable) {
3194 var draggedColumnClone = $(document.getElementById(draggedColumnHeader.attr('id') + '_clone')),
3195 droppedColumnClone = $(document.getElementById(droppedColumnHeader.attr('id') + '_clone'));
3196 draggedColumnClone.insertBefore(droppedColumnClone);
3197 }
3198 }
3199
3200 //fire colReorder event
3201 $this._trigger('colReorder', null, {
3202 dragIndex: draggedColumnHeader.index(),
3203 dropIndex: droppedColumnHeader.index()
3204 });
3205 }
3206 });
3207 },
3208
3209 containsFooter: function() {
3210 if(this.hasFooter === undefined) {
3211 this.hasFooter = this.options.footerRows !== undefined;
3212 if(!this.hasFooter) {
3213 if(this.options.columns) {
3214 for(var i = 0; i < this.options.columns.length; i++) {
3215 if(this.options.columns[i].footerText !== undefined) {
3216 this.hasFooter = true;
3217 break;
3218 }
3219 }
3220 }
3221 }
3222 }
3223
3224 return this.hasFooter;
3225 },
3226
3227 _initResizableColumns: function() {
3228 this.element.addClass('ui-datatable-resizable');
3229 this.thead.find('> tr > th').addClass('ui-resizable-column');
3230 this.resizerHelper = $('<div class="ui-column-resizer-helper ui-state-highlight"></div>').appendTo(this.element);
3231 this.addResizers();
3232 var resizers = this.thead.find('> tr > th > span.ui-column-resizer'),
3233 $this = this;
3234
3235 setTimeout(function() {
3236 $this.fixColumnWidths();
3237 }, 5);
3238
3239 resizers.draggable({
3240 axis: 'x',
3241 start: function(event, ui) {
3242 ui.helper.data('originalposition', ui.helper.offset());
3243
3244 var height = $this.options.scrollable ? $this.scrollBody.height() : $this.thead.parent().height() - $this.thead.height() - 1;
3245 $this.resizerHelper.height(height);
3246 $this.resizerHelper.show();
3247 },
3248 drag: function(event, ui) {
3249 $this.resizerHelper.offset({
3250 left: ui.helper.offset().left + ui.helper.width() / 2,
3251 top: $this.thead.offset().top + $this.thead.height()
3252 });
3253 },
3254 stop: function(event, ui) {
3255 ui.helper.css({
3256 'left': '',
3257 'top': '0px',
3258 'right': '0px'
3259 });
3260
3261 $this.resize(event, ui);
3262 $this.resizerHelper.hide();
3263
3264 if($this.options.columnResizeMode === 'expand') {
3265 setTimeout(function() {
3266 $this._trigger('colResize', null, {element: ui.helper.parent()});
3267 }, 5);
3268 }
3269 else {
3270 $this._trigger('colResize', null, {element: ui.helper.parent()});
3271 }
3272
3273 if($this.options.stickyHeader) {
3274 $this.thead.find('.ui-column-filter').prop('disabled', false);
3275 $this.clone = $this.thead.clone(true);
3276 $this.cloneContainer.find('thead').remove();
3277 $this.cloneContainer.children('table').append($this.clone);
3278 $this.thead.find('.ui-column-filter').prop('disabled', true);
3279 }
3280 },
3281 containment: this.element
3282 });
3283 },
3284
3285 resize: function(event, ui) {
3286 var columnHeader, nextColumnHeader, change = null, newWidth = null, nextColumnWidth = null,
3287 expandMode = (this.options.columnResizeMode === 'expand'),
3288 table = this.thead.parent(),
3289 columnHeader = ui.helper.parent(),
3290 nextColumnHeader = columnHeader.next();
3291
3292 change = (ui.position.left - ui.originalPosition.left),
3293 newWidth = (columnHeader.width() + change),
3294 nextColumnWidth = (nextColumnHeader.width() - change);
3295
3296 if((newWidth > 15 && nextColumnWidth > 15) || (expandMode && newWidth > 15)) {
3297 if(expandMode) {
3298 table.width(table.width() + change);
3299 setTimeout(function() {
3300 columnHeader.width(newWidth);
3301 }, 1);
3302 }
3303 else {
3304 columnHeader.width(newWidth);
3305 nextColumnHeader.width(nextColumnWidth);
3306 }
3307
3308 if(this.options.scrollable) {
3309 var cloneTable = this.theadClone.parent(),
3310 colIndex = columnHeader.index();
3311
3312 if(expandMode) {
3313 var $this = this;
3314
3315 //body
3316 cloneTable.width(cloneTable.width() + change);
3317
3318 //footer
3319 this.footerTable.width(this.footerTable.width() + change);
3320
3321 setTimeout(function() {
3322 if($this.hasColumnGroup) {
3323 $this.theadClone.find('> tr:first').children('th').eq(colIndex).width(newWidth); //body
3324 $this.footerTable.find('> tfoot > tr:first').children('th').eq(colIndex).width(newWidth); //footer
3325 }
3326 else {
3327 $this.theadClone.find(PUI.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth); //body
3328 $this.footerCols.eq(colIndex).width(newWidth); //footer
3329 }
3330 }, 1);
3331 }
3332 else {
3333 //body
3334 this.theadClone.find(PUI.escapeClientId(columnHeader.attr('id') + '_clone')).width(newWidth);
3335 this.theadClone.find(PUI.escapeClientId(nextColumnHeader.attr('id') + '_clone')).width(nextColumnWidth);
3336
3337 //footer
3338 /*if(this.footerCols.length > 0) {
3339 var footerCol = this.footerCols.eq(colIndex),
3340 nextFooterCol = footerCol.next();
3341
3342 footerCol.width(newWidth);
3343 nextFooterCol.width(nextColumnWidth);
3344 }*/
3345 }
3346 }
3347 }
3348 },
3349
3350 addResizers: function() {
3351 var resizableColumns = this.thead.find('> tr > th.ui-resizable-column');
3352 resizableColumns.prepend('<span class="ui-column-resizer">&nbsp;</span>');
3353
3354 if(this.options.columnResizeMode === 'fit') {
3355 resizableColumns.filter(':last-child').children('span.ui-column-resizer').hide();
3356 }
3357 },
3358
3359 _initDraggableRows: function() {
3360 var $this = this;
3361
3362 this.tbody.sortable({
3363 placeholder: 'ui-datatable-rowordering ui-state-active',
3364 cursor: 'move',
3365 handle: 'td,span:not(.ui-c)',
3366 appendTo: document.body,
3367 helper: function(event, ui) {
3368 var cells = ui.children(),
3369 helper = $('<div class="ui-datatable ui-widget"><table><tbody></tbody></table></div>'),
3370 helperRow = ui.clone(),
3371 helperCells = helperRow.children();
3372
3373 for(var i = 0; i < helperCells.length; i++) {
3374 helperCells.eq(i).width(cells.eq(i).width());
3375 }
3376
3377 helperRow.appendTo(helper.find('tbody'));
3378
3379 return helper;
3380 },
3381 update: function(event, ui) {
3382 $this.syncRowParity();
3383
3384 $this._trigger('rowReorder', null, {
3385 fromIndex: ui.item.data('ri'),
3386 toIndex: $this._getFirst() + ui.item.index()
3387 });
3388 },
3389 change: function(event, ui) {
3390 if($this.options.scrollable) {
3391 PUI.scrollInView($this.scrollBody, ui.placeholder);
3392 }
3393 }
3394 });
3395 },
3396
3397 syncRowParity: function() {
3398 var rows = this.tbody.children('tr.ui-widget-content');
3399
3400 for(var i = this._getFirst(); i < rows.length; i++) {
3401 var row = rows.eq(i);
3402
3403 row.data('ri', i).removeClass('ui-datatable-even ui-datatable-odd');
3404
3405 if(i % 2 === 0)
3406 row.addClass('ui-datatable-even');
3407 else
3408 row.addClass('ui-datatable-odd');
3409
3410 }
3411 },
3412
3413 getContextMenuSelection: function(data) {
3414 return this.dataSelectedByContextMenu;
3415 },
3416
3417 _initFiltering: function() {
3418 var $this = this;
3419 this.filterElements = this.thead.find('.ui-column-filter');
3420
3421 this.filterElements.on('keyup', function() {
3422 if($this.filterTimeout) {
3423 clearTimeout($this.filterTimeout);
3424 }
3425
3426 $this.filterTimeout = setTimeout(function() {
3427 $this.filter();
3428 $this.filterTimeout = null;
3429 },
3430 $this.options.filterDelay);
3431 });
3432
3433 if(this.options.globalFilter) {
3434 $(this.options.globalFilter).on('keyup.puidatatable', function() {
3435 $this.filter();
3436 });
3437 }
3438 },
3439
3440 filter: function() {
3441 this.filterMetaMap = [];
3442
3443 for(var i = 0; i < this.filterElements.length; i++) {
3444 var filterElement = this.filterElements.eq(i),
3445 filterElementValue = filterElement.val();
3446
3447 this.filterMetaMap.push({
3448 field: filterElement.data('field'),
3449 filterMatchMode: filterElement.data('filtermatchmode'),
3450 value: filterElementValue.toLowerCase(),
3451 element: filterElement
3452 });
3453 }
3454
3455 if(this.options.lazy) {
3456 this.options.datasource.call(this, this._onLazyLoad, this._createStateMeta());
3457 }
3458 else {
3459 var globalFilterValue = $(this.options.globalFilter).val();
3460 this.filteredData = [];
3461
3462 for(var i = 0; i < this.data.length; i++) {
3463 var localMatch = true;
3464 var globalMatch = false;
3465
3466 for(var j = 0; j < this.filterMetaMap.length; j++) {
3467 var filterMeta = this.filterMetaMap[j],
3468 filterValue = filterMeta.value,
3469 filterField = filterMeta.field,
3470 dataFieldValue = this.data[i][filterField];
3471
3472 var filterConstraint = this.filterConstraints[filterMeta.filterMatchMode];
3473
3474 //global
3475 if(this.options.globalFilter && !globalMatch) {
3476 var filterConstraint = this.filterConstraints['contains'];
3477 globalMatch = filterConstraint(dataFieldValue, globalFilterValue);
3478
3479 }
3480
3481 //local
3482 if(filterMeta.filterMatchMode === 'custom') {
3483 localMatch = filterMeta.element.triggerHandler('filter', [dataFieldValue, filterValue]);
3484 }
3485 else {
3486 var filterConstraint = this.filterConstraints[filterMeta.filterMatchMode];
3487 if(!filterConstraint(dataFieldValue, filterValue)) {
3488 localMatch = false;
3489 }
3490 }
3491
3492 if(!localMatch) {
3493 break;
3494 }
3495 }
3496
3497 var matches = localMatch;
3498
3499 if(this.options.globalFilter) {
3500 matches = localMatch && globalMatch;
3501 }
3502
3503 if(matches) {
3504 this.filteredData.push(this.data[i]);
3505 }
3506 }
3507
3508 if(this.filteredData.length === this.data.length) {
3509 this.filteredData = null;
3510 }
3511
3512 if(this.paginator) {
3513 this.paginator.puipaginator('option', 'totalRecords', this.filteredData ? this.filteredData.length : this.data ? this.data.length : 0);
3514 }
3515
3516 this._renderData();
3517 }
3518 },
3519
3520 filterConstraints: {
3521
3522 startsWith: function(value, filter) {
3523 if(filter === undefined || filter === null || $.trim(filter) === '') {
3524 return true;
3525 }
3526
3527 if(value === undefined || value === null) {
3528 return false;
3529 }
3530
3531 return value.toString().toLowerCase().slice(0, filter.length) === filter;
3532 },
3533
3534 contains: function(value, filter) {
3535 if(filter === undefined || filter === null || $.trim(filter) === '') {
3536 return true;
3537 }
3538
3539 if(value === undefined || value === null) {
3540 return false;
3541 }
3542
3543 return value.toString().toLowerCase().indexOf(filter) !== -1;
3544 }
3545
3546 },
3547
3548 _initStickyHeader: function() {
3549 var table = this.thead.parent(),
3550 offset = table.offset(),
3551 win = $(window),
3552 $this = this,
3553 stickyNS = 'scroll.' + this.id,
3554 resizeNS = 'resize.sticky-' + this.id;
3555
3556 this.cloneContainer = $('<div class="ui-datatable ui-datatable-sticky ui-widget"><table></table></div>');
3557 this.clone = this.thead.clone(true);
3558 this.cloneContainer.children('table').append(this.clone);
3559
3560 this.cloneContainer.css({
3561 position: 'absolute',
3562 width: table.outerWidth(),
3563 top: offset.top,
3564 left: offset.left,
3565 'z-index': ++PUI.zindex
3566 })
3567 .appendTo(this.element);
3568
3569 win.off(stickyNS).on(stickyNS, function() {
3570 var scrollTop = win.scrollTop(),
3571 tableOffset = table.offset();
3572
3573 if(scrollTop > tableOffset.top) {
3574 $this.cloneContainer.css({
3575 'position': 'fixed',
3576 'top': '0px'
3577 })
3578 .addClass('ui-shadow ui-sticky');
3579
3580 if(scrollTop >= (tableOffset.top + $this.tbody.height()))
3581 $this.cloneContainer.hide();
3582 else
3583 $this.cloneContainer.show();
3584 }
3585 else {
3586 $this.cloneContainer.css({
3587 'position': 'absolute',
3588 'top': tableOffset.top
3589 })
3590 .removeClass('ui-shadow ui-sticky');
3591 }
3592 })
3593 .off(resizeNS).on(resizeNS, function() {
3594 $this.cloneContainer.width(table.outerWidth());
3595 });
3596
3597 //filter support
3598 this.thead.find('.ui-column-filter').prop('disabled', true);
3599 },
3600
3601 _initEditing: function() {
3602 var cellSelector = '> tr > td.ui-editable-column',
3603 $this = this;
3604
3605 this.tbody.off('click', cellSelector)
3606 .on('click', cellSelector, null, function(e) {
3607 var cell = $(this);
3608 if(!cell.hasClass('ui-cell-editing')) {
3609 $this._showCellEditor(cell);
3610 e.stopPropagation();
3611 }
3612 });
3613 },
3614
3615 _showCellEditor: function(cell) {
3616 var editor = this.editors[cell.data('editor')].call(),
3617 $this = this;
3618
3619 editor.val(cell.data('rowdata')[cell.data('field')]);
3620
3621 cell.addClass('ui-cell-editing').html('').append(editor);
3622
3623 editor.focus().on('change', function() {
3624 $this._onCellEditorChange(cell);
3625 })
3626 .on('blur', function() {
3627 $this._onCellEditorBlur(cell);
3628 })
3629 .on('keydown', function(e) {
3630 var key = e.which,
3631 keyCode = $.ui.keyCode;
3632
3633 if((key === keyCode.ENTER||key === keyCode.NUMPAD_ENTER)) {
3634 $(this).trigger('change').trigger('blur');
3635 e.preventDefault();
3636 }
3637 else if(key === keyCode.TAB) {
3638 if(e.shiftKey) {
3639 var prevCell = cell.prevAll('td.ui-editable-column').eq(0);
3640 if(!prevCell.length) {
3641 prevCell = cell.parent().prev('tr').children('td.ui-editable-column:last');
3642 }
3643 if(prevCell.length) {
3644 $this._showCellEditor(prevCell);
3645 }
3646 }
3647 else {
3648 var nextCell = cell.nextAll('td.ui-editable-column').eq(0);
3649 if(!nextCell.length) {
3650 nextCell = cell.parent().next('tr').children('td.ui-editable-column').eq(0);
3651 }
3652 if(nextCell.length) {
3653 $this._showCellEditor(nextCell);
3654 }
3655 }
3656
3657 e.preventDefault();
3658 } else if(key === keyCode.ESCAPE) {
3659 $this._onCellEditorBlur(cell);
3660 }
3661
3662 });
3663 },
3664
3665 _onCellEditorChange: function(cell) {
3666 var newCellValue = cell.children('.ui-cell-editor').val();
3667
3668 var retVal = this._trigger('cellEdit', null, {
3669 oldValue: cell.data('rowdata')[cell.data('field')],
3670 newValue: newCellValue,
3671 data: cell.data('rowdata'),
3672 field: cell.data('field')
3673 });
3674
3675 if(retVal !== false) {
3676 cell.data('rowdata')[cell.data('field')] = newCellValue;
3677 }
3678 },
3679
3680 _onCellEditorBlur: function(cell) {
3681 cell.removeClass('ui-cell-editing').text(cell.data('rowdata')[cell.data('field')])
3682 .children('.ui-cell-editor').remove();
3683 },
3684
3685 reload: function() {
3686 this._updateDatasource(this.options.datasource);
3687 },
3688
3689 getPaginator: function() {
3690 return this.paginator;
3691 },
3692
3693 setTotalRecords: function(val) {
3694 this.paginator.puipaginator('option','totalRecords', val);
3695 },
3696
3697 _createFilterMap: function() {
3698 var filters = null;
3699 if(this.filterElements) {
3700 filters = {};
3701 for(var i = 0; i < this.filterElements.length; i++) {
3702 var filterElement = this.filterElements.eq(i),
3703 value = filterElement.val();
3704 if($.trim(value).length) {
3705 filters[filterElement.data('field')] = value;
3706 }
3707 }
3708 }
3709
3710 return filters;
3711 },
3712
3713 editors: {
3714
3715 'input': function() {
3716 return $('<input type="text" class="ui-cell-editor"/>');
3717 }
3718 }
3719
3720 });
3721})();
3722
3723/**
3724 * PrimeUI Dialog Widget
3725 */
3726(function() {
3727
3728 $.widget("primeui.puidialog", {
3729
3730 options: {
3731 draggable: true,
3732 resizable: true,
3733 location: 'center',
3734 minWidth: 150,
3735 minHeight: 25,
3736 height: 'auto',
3737 width: '300px',
3738 visible: false,
3739 modal: false,
3740 showEffect: null,
3741 hideEffect: null,
3742 effectOptions: {},
3743 effectSpeed: 'normal',
3744 closeOnEscape: true,
3745 rtl: false,
3746 closable: true,
3747 minimizable: false,
3748 maximizable: false,
3749 appendTo: null,
3750 buttons: null,
3751 responsive: false,
3752 title: null,
3753 enhanced: false
3754 },
3755
3756 _create: function() {
3757 this.id = this.element.attr('id');
3758 if(!this.id) {
3759 this.id = this.element.uniqueId().attr('id');
3760 }
3761
3762 //container
3763 if(!this.options.enhanced) {
3764 this.element.addClass('ui-dialog ui-widget ui-widget-content ui-helper-hidden ui-corner-all ui-shadow')
3765 .contents().wrapAll('<div class="ui-dialog-content ui-widget-content" />');
3766
3767 //header
3768 var title = this.options.title||this.element.attr('title');
3769 this.element.prepend('<div class="ui-dialog-titlebar ui-widget-header ui-helper-clearfix ui-corner-top">' +
3770 '<span id="' + this.element.attr('id') + '_label" class="ui-dialog-title">' + title + '</span>')
3771 .removeAttr('title');
3772
3773 //footer
3774 if(this.options.buttons) {
3775 this.footer = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"></div>').appendTo(this.element);
3776 for(var i = 0; i < this.options.buttons.length; i++) {
3777 var buttonMeta = this.options.buttons[i],
3778 button = $('<button type="button"></button>').appendTo(this.footer);
3779 if(buttonMeta.text) {
3780 button.text(buttonMeta.text);
3781 }
3782
3783 button.puibutton(buttonMeta);
3784 }
3785 }
3786
3787 if(this.options.rtl) {
3788 this.element.addClass('ui-dialog-rtl');
3789 }
3790 }
3791
3792 //elements
3793 this.content = this.element.children('.ui-dialog-content');
3794 this.titlebar = this.element.children('.ui-dialog-titlebar');
3795
3796 if(!this.options.enhanced) {
3797 if(this.options.closable) {
3798 this._renderHeaderIcon('ui-dialog-titlebar-close', 'fa-close');
3799 }
3800
3801 if(this.options.maximizable) {
3802 this._renderHeaderIcon('ui-dialog-titlebar-maximize', 'fa-sort');
3803 }
3804
3805 if(this.options.minimizable) {
3806 this._renderHeaderIcon('ui-dialog-titlebar-minimize', 'fa-minus');
3807 }
3808 }
3809
3810 //icons
3811 this.icons = this.titlebar.children('.ui-dialog-titlebar-icon');
3812 this.closeIcon = this.titlebar.children('.ui-dialog-titlebar-close');
3813 this.minimizeIcon = this.titlebar.children('.ui-dialog-titlebar-minimize');
3814 this.maximizeIcon = this.titlebar.children('.ui-dialog-titlebar-maximize');
3815
3816 this.blockEvents = 'focus.puidialog mousedown.puidialog mouseup.puidialog keydown.puidialog keyup.puidialog';
3817 this.parent = this.element.parent();
3818
3819 //size
3820 this.element.css({'width': this.options.width, 'height': 'auto'});
3821 this.content.height(this.options.height);
3822
3823 //events
3824 this._bindEvents();
3825
3826 if(this.options.draggable) {
3827 this._setupDraggable();
3828 }
3829
3830 if(this.options.resizable) {
3831 this._setupResizable();
3832 }
3833
3834 if(this.options.appendTo) {
3835 this.element.appendTo(this.options.appendTo);
3836 }
3837
3838 if(this.options.responsive) {
3839 this.resizeNS = 'resize.' + this.id;
3840 }
3841
3842 //docking zone
3843 if($(document.body).children('.ui-dialog-docking-zone').length === 0) {
3844 $(document.body).append('<div class="ui-dialog-docking-zone"></div>');
3845 }
3846
3847 //aria
3848 this._applyARIA();
3849
3850 if(this.options.visible) {
3851 this.show();
3852 }
3853 },
3854
3855 _destroy: function() {
3856 //restore dom
3857 if(!this.options.enhanced) {
3858 this.element.removeClass('ui-dialog ui-widget ui-widget-content ui-helper-hidden ui-corner-all ui-shadow');
3859
3860 if(this.options.buttons) {
3861 this.footer.children('button').puibutton('destroy');
3862 this.footer.remove();
3863 }
3864
3865 if(this.options.rtl) {
3866 this.element.removeClass('ui-dialog-rtl');
3867 }
3868
3869 var title = this.titlebar.children('.ui-dialog-title').text()||this.options.title;
3870 if(title) {
3871 this.element.attr('title', title);
3872 }
3873 this.titlebar.remove();
3874
3875 this.content.contents().unwrap();
3876 }
3877
3878 //remove events
3879 this._unbindEvents();
3880
3881 if(this.options.draggable) {
3882 this.element.draggable('destroy');
3883 }
3884
3885 if(this.options.resizable) {
3886 this.element.resizable('destroy');
3887 }
3888
3889 if(this.options.appendTo) {
3890 this.element.appendTo(this.parent);
3891 }
3892
3893 this._unbindResizeListener();
3894
3895 if(this.options.modal) {
3896 this._disableModality();
3897 }
3898
3899 this._removeARIA();
3900 this.element.css({
3901 'width': 'auto',
3902 'height': 'auto'
3903 });
3904 },
3905
3906 _renderHeaderIcon: function(styleClass, icon) {
3907 this.titlebar.append('<a class="ui-dialog-titlebar-icon ' + styleClass + ' ui-corner-all" href="#" role="button">' +
3908 '<span class="fa fa-fw ' + icon + '"></span></a>');
3909 },
3910
3911 _enableModality: function() {
3912 var $this = this,
3913 doc = $(document);
3914
3915 this.modality = $('<div id="' + this.element.attr('id') + '_modal" class="ui-widget-overlay ui-dialog-mask"></div>').appendTo(document.body)
3916 .css('z-index', this.element.css('z-index') - 1);
3917
3918 //Disable tabbing out of modal dialog and stop events from targets outside of dialog
3919 doc.on('keydown.puidialog',
3920 function(event) {
3921 if(event.keyCode == $.ui.keyCode.TAB) {
3922 var tabbables = $this.content.find(':tabbable'),
3923 first = tabbables.filter(':first'),
3924 last = tabbables.filter(':last');
3925
3926 if(event.target === last[0] && !event.shiftKey) {
3927 first.focus(1);
3928 return false;
3929 }
3930 else if (event.target === first[0] && event.shiftKey) {
3931 last.focus(1);
3932 return false;
3933 }
3934 }
3935 })
3936 .bind(this.blockEvents, function(event) {
3937 if ($(event.target).zIndex() < $this.element.zIndex()) {
3938 return false;
3939 }
3940 });
3941 },
3942
3943 _disableModality: function() {
3944 if(this.modality) {
3945 this.modality.remove();
3946 this.modality = null;
3947 }
3948
3949 $(document).off(this.blockEvents).off('keydown.dialog');
3950 },
3951
3952 show: function() {
3953 if(this.element.is(':visible')) {
3954 return;
3955 }
3956
3957 if(!this.positionInitialized) {
3958 this._initPosition();
3959 }
3960
3961 this._trigger('beforeShow', null);
3962
3963 if(this.options.showEffect) {
3964 var $this = this;
3965
3966 this.element.show(this.options.showEffect, this.options.effectOptions, this.options.effectSpeed, function() {
3967 $this._postShow();
3968 });
3969 }
3970 else {
3971 this.element.show();
3972
3973 this._postShow();
3974 }
3975
3976 this._moveToTop();
3977
3978 if(this.options.modal) {
3979 this._enableModality();
3980 }
3981 },
3982
3983 _postShow: function() {
3984 //execute user defined callback
3985 this._trigger('afterShow', null);
3986
3987 this.element.attr({
3988 'aria-hidden': false,
3989 'aria-live': 'polite'
3990 });
3991
3992 this._applyFocus();
3993
3994 if(this.options.responsive) {
3995 this._bindResizeListener();
3996 }
3997 },
3998
3999 hide: function() {
4000 if(this.element.is(':hidden')) {
4001 return;
4002 }
4003
4004 this._trigger('beforeHide', null);
4005
4006 if(this.options.hideEffect) {
4007 var _self = this;
4008
4009 this.element.hide(this.options.hideEffect, this.options.effectOptions, this.options.effectSpeed, function() {
4010 _self._postHide();
4011 });
4012 }
4013 else {
4014 this.element.hide();
4015
4016 this._postHide();
4017 }
4018
4019 if(this.options.modal) {
4020 this._disableModality();
4021 }
4022 },
4023
4024 _postHide: function() {
4025 //execute user defined callback
4026 this._trigger('afterHide', null);
4027
4028 this.element.attr({
4029 'aria-hidden': true,
4030 'aria-live': 'off'
4031 });
4032
4033 if(this.options.responsive) {
4034 this._unbindResizeListener();
4035 }
4036 },
4037
4038 _applyFocus: function() {
4039 this.element.find(':not(:submit):not(:button):input:visible:enabled:first').focus();
4040 },
4041
4042 _bindEvents: function() {
4043 var $this = this;
4044 this.element.on('mousedown.puidialog', function(e) {
4045 if(!$(e.target).data('ui-widget-overlay')) {
4046 $this._moveToTop();
4047 }
4048 });
4049
4050 this.icons.mouseover(function() {
4051 $(this).addClass('ui-state-hover');
4052 }).mouseout(function() {
4053 $(this).removeClass('ui-state-hover');
4054 });
4055
4056 this.closeIcon.on('click.puidialog', function(e) {
4057 $this.hide();
4058 $this._trigger('clickClose');
4059 e.preventDefault();
4060 });
4061
4062 this.maximizeIcon.click(function(e) {
4063 $this.toggleMaximize();
4064 e.preventDefault();
4065 });
4066
4067 this.minimizeIcon.click(function(e) {
4068 $this.toggleMinimize();
4069 e.preventDefault();
4070 });
4071
4072 if(this.options.closeOnEscape) {
4073 $(document).on('keydown.dialog_' + this.id, function(e) {
4074 var keyCode = $.ui.keyCode,
4075 active = parseInt($this.element.css('z-index'), 10) === PUI.zindex;
4076
4077 if(e.which === keyCode.ESCAPE && $this.element.is(':visible') && active) {
4078 $this.hide();
4079 $this._trigger('hideWithEscape');
4080 }
4081 });
4082 }
4083 },
4084
4085 _unbindEvents: function() {
4086 this.element.off('mousedown.puidialog');
4087 this.icons.off();
4088 $(document).off('keydown.dialog_' + this.id);
4089 },
4090
4091 _setupDraggable: function() {
4092 this.element.draggable({
4093 cancel: '.ui-dialog-content, .ui-dialog-titlebar-close',
4094 handle: '.ui-dialog-titlebar',
4095 containment : 'document'
4096 });
4097 },
4098
4099 _setupResizable: function() {
4100 var $this = this;
4101
4102 this.element.resizable({
4103 minWidth : this.options.minWidth,
4104 minHeight : this.options.minHeight,
4105 alsoResize : this.content,
4106 containment: 'document',
4107 start: function(event, ui) {
4108 $this.element.data('offset', $this.element.offset());
4109 },
4110 stop: function(event, ui) {
4111 var offset = $this.element.data('offset');
4112
4113 $this.element.css('position', 'fixed');
4114 $this.element.offset(offset);
4115 }
4116 });
4117
4118 this.resizers = this.element.children('.ui-resizable-handle');
4119 },
4120
4121 _initPosition: function() {
4122 //reset
4123 this.element.css({left:0,top:0});
4124
4125 if(/(center|left|top|right|bottom)/.test(this.options.location)) {
4126 this.options.location = this.options.location.replace(',', ' ');
4127
4128 this.element.position({
4129 my: 'center',
4130 at: this.options.location,
4131 collision: 'fit',
4132 of: window,
4133 //make sure dialog stays in viewport
4134 using: function(pos) {
4135 var l = pos.left < 0 ? 0 : pos.left,
4136 t = pos.top < 0 ? 0 : pos.top;
4137
4138 $(this).css({
4139 left: l,
4140 top: t
4141 });
4142 }
4143 });
4144 }
4145 else {
4146 var coords = this.options.position.split(','),
4147 x = $.trim(coords[0]),
4148 y = $.trim(coords[1]);
4149
4150 this.element.offset({
4151 left: x,
4152 top: y
4153 });
4154 }
4155
4156 this.positionInitialized = true;
4157 },
4158
4159 _moveToTop: function() {
4160 this.element.css('z-index',++PUI.zindex);
4161 },
4162
4163 toggleMaximize: function() {
4164 if(this.minimized) {
4165 this.toggleMinimize();
4166 }
4167
4168 if(this.maximized) {
4169 this.element.removeClass('ui-dialog-maximized');
4170 this._restoreState();
4171
4172 this.maximizeIcon.removeClass('ui-state-hover');
4173 this.maximized = false;
4174 }
4175 else {
4176 this._saveState();
4177
4178 var win = $(window);
4179
4180 this.element.addClass('ui-dialog-maximized').css({
4181 'width': win.width() - 6,
4182 'height': win.height()
4183 }).offset({
4184 top: win.scrollTop(),
4185 left: win.scrollLeft()
4186 });
4187
4188 //maximize content
4189 this.content.css({
4190 width: 'auto',
4191 height: 'auto'
4192 });
4193
4194 this.maximizeIcon.removeClass('ui-state-hover');
4195 this.maximized = true;
4196 this._trigger('maximize');
4197 }
4198 },
4199
4200 toggleMinimize: function() {
4201 var animate = true,
4202 dockingZone = $(document.body).children('.ui-dialog-docking-zone');
4203
4204 if(this.maximized) {
4205 this.toggleMaximize();
4206 animate = false;
4207 }
4208
4209 var $this = this;
4210
4211 if(this.minimized) {
4212 this.element.appendTo(this.parent).removeClass('ui-dialog-minimized').css({'position':'fixed', 'float':'none'});
4213 this._restoreState();
4214 this.content.show();
4215 this.minimizeIcon.removeClass('ui-state-hover').children('.fa').removeClass('fa-plus').addClass('fa-minus');
4216 this.minimized = false;
4217
4218 if(this.options.resizable) {
4219 this.resizers.show();
4220 }
4221
4222 if(this.footer) {
4223 this.footer.show();
4224 }
4225 }
4226 else {
4227 this._saveState();
4228
4229 if(animate) {
4230 this.element.effect('transfer', {
4231 to: dockingZone,
4232 className: 'ui-dialog-minimizing'
4233 }, 500,
4234 function() {
4235 $this._dock(dockingZone);
4236 $this.element.addClass('ui-dialog-minimized');
4237 });
4238 }
4239 else {
4240 this._dock(dockingZone);
4241 }
4242 }
4243 },
4244
4245 _dock: function(zone) {
4246 this.element.appendTo(zone).css('position', 'static');
4247 this.element.css({'height':'auto', 'width':'auto', 'float': 'left'});
4248 this.content.hide();
4249 this.minimizeIcon.removeClass('ui-state-hover').children('.fa').removeClass('fa-minus').addClass('fa-plus');
4250 this.minimized = true;
4251
4252 if(this.options.resizable) {
4253 this.resizers.hide();
4254 }
4255
4256 if(this.footer) {
4257 this.footer.hide();
4258 }
4259
4260 zone.css('z-index',++PUI.zindex);
4261
4262 this._trigger('minimize');
4263 },
4264
4265 _saveState: function() {
4266 this.state = {
4267 width: this.element.width(),
4268 height: this.element.height()
4269 };
4270
4271 var win = $(window);
4272 this.state.offset = this.element.offset();
4273 this.state.windowScrollLeft = win.scrollLeft();
4274 this.state.windowScrollTop = win.scrollTop();
4275 },
4276
4277 _restoreState: function() {
4278 this.element.width(this.state.width).height(this.state.height);
4279
4280 var win = $(window);
4281 this.element.offset({
4282 top: this.state.offset.top + (win.scrollTop() - this.state.windowScrollTop),
4283 left: this.state.offset.left + (win.scrollLeft() - this.state.windowScrollLeft)
4284 });
4285 },
4286
4287 _applyARIA: function() {
4288 this.element.attr({
4289 'role': 'dialog',
4290 'aria-labelledby': this.element.attr('id') + '_title',
4291 'aria-hidden': !this.options.visible
4292 });
4293
4294 this.titlebar.children('a.ui-dialog-titlebar-icon').attr('role', 'button');
4295 },
4296
4297 _removeARIA: function() {
4298 this.element.removeAttr('role').removeAttr('aria-labelledby').removeAttr('aria-hidden')
4299 .removeAttr('aria-live').removeAttr('aria-hidden');
4300 },
4301
4302 _bindResizeListener: function() {
4303 var $this = this;
4304 $(window).on(this.resizeNS, function(e) {
4305 if(e.target === window) {
4306 $this._initPosition();
4307 }
4308 });
4309 },
4310
4311 _unbindResizeListener: function() {
4312 $(window).off(this.resizeNS);
4313 },
4314
4315 _setOption: function(key, value) {
4316 if(key === 'visible') {
4317 if(value)
4318 this.show();
4319 else
4320 this.hide();
4321 }
4322 else {
4323 $.Widget.prototype._setOption.apply(this, arguments);
4324 }
4325 }
4326 });
4327})();
4328/**
4329 * PrimeUI dropdown widget
4330 */
4331(function() {
4332
4333 $.widget("primeui.puidropdown", {
4334
4335 options: {
4336 effect: 'fade',
4337 effectSpeed: 'normal',
4338 filter: false,
4339 filterMatchMode: 'startsWith',
4340 caseSensitiveFilter: false,
4341 filterFunction: null,
4342 data: null,
4343 content: null,
4344 scrollHeight: 200,
4345 appendTo: 'body',
4346 editable: false,
4347 value: null,
4348 style: null,
4349 styleClass: null
4350 },
4351
4352 _create: function() {
4353 this.id = this.element.attr('id');
4354 if(!this.id) {
4355 this.id = this.element.uniqueId().attr('id');
4356 }
4357
4358 if(!this.options.enhanced) {
4359 if(this.options.data) {
4360 if($.isArray(this.options.data)) {
4361 this._generateOptionElements(this.options.data);
4362 }
4363 else {
4364 if($.type(this.options.data) === 'function') {
4365 this.options.data.call(this, this._onRemoteOptionsLoad);
4366 return;
4367 }
4368 else {
4369 if($.type(this.options.data) === 'string') {
4370 var $this = this,
4371 dataURL = this.options.data;
4372
4373 var loader = function() {
4374 $.ajax({
4375 type: 'GET',
4376 url: dataURL,
4377 dataType: "json",
4378 context: $this,
4379 success: function (response) {
4380 this._onRemoteOptionsLoad(response);
4381 }
4382 });
4383 };
4384 loader.call(this);
4385 }
4386 }
4387 return;
4388 }
4389 }
4390
4391 this._render();
4392 }
4393 else {
4394 this.choices = this.element.children('option');
4395 this.container = this.element.closest('.ui-dropdown');
4396 this.focusElementContainer = this.container.children('.ui-helper-hidden-accessible:last');
4397 this.focusElement = this.focusElementContainer.children('input');
4398 this.label = this.container.children('.ui-dropdown-label');
4399 this.menuIcon = this.container.children('.ui-dropdown-trigger');
4400 this.panel = this.container.children('.ui-dropdown-panel');
4401 this.itemsWrapper = this.panel.children('.ui-dropdown-items-wrapper');
4402 this.itemsContainer = this.itemsWrapper.children('ul');
4403 this.itemsContainer.addClass('ui-dropdown-items ui-dropdown-list ui-widget-content ui-widget ui-corner-all ui-helper-reset');
4404 this.items = this.itemsContainer.children('li').addClass('ui-dropdown-item ui-dropdown-list-item ui-corner-all');
4405
4406 var $this = this;
4407 this.items.each(function(i) {
4408 $(this).data('label', $this.choices.eq(i).text());
4409 });
4410
4411 if(this.options.filter) {
4412 this.filterContainer = this.panel.children('.ui-dropdown-filter-container');
4413 this.filterInput = this.filterContainer.children('input');
4414 }
4415 }
4416
4417 this._postRender();
4418 },
4419
4420 _render: function() {
4421 this.choices = this.element.children('option');
4422 this.element.attr('tabindex', '-1').wrap('<div class="ui-dropdown ui-widget ui-state-default ui-corner-all ui-helper-clearfix" />')
4423 .wrap('<div class="ui-helper-hidden-accessible" />');
4424
4425 this.container = this.element.closest('.ui-dropdown');
4426 this.focusElementContainer = $('<div class="ui-helper-hidden-accessible"><input type="text" /></div>').appendTo(this.container);
4427 this.focusElement = this.focusElementContainer.children('input');
4428 this.label = this.options.editable ? $('<input type="text" class="ui-dropdown-label ui-inputtext ui-corner-all"">')
4429 : $('<label class="ui-dropdown-label ui-inputtext ui-corner-all"/>');
4430 this.label.appendTo(this.container);
4431 this.menuIcon = $('<div class="ui-dropdown-trigger ui-state-default ui-corner-right"><span class="fa fa-fw fa-caret-down"></span></div>')
4432 .appendTo(this.container);
4433
4434 //panel
4435 this.panel = $('<div class="ui-dropdown-panel ui-widget-content ui-corner-all ui-helper-hidden ui-shadow" />');
4436 this.itemsWrapper = $('<div class="ui-dropdown-items-wrapper" />').appendTo(this.panel);
4437 this.itemsContainer = $('<ul class="ui-dropdown-items ui-dropdown-list ui-widget-content ui-widget ui-corner-all ui-helper-reset"></ul>')
4438 .appendTo(this.itemsWrapper);
4439
4440 this.optGroupsSize = this.itemsContainer.children('li.puiselectonemenu-item-group').length;
4441
4442 if(this.options.filter) {
4443 this.filterContainer = $('<div class="ui-dropdown-filter-container" />').prependTo(this.panel);
4444 this.filterInput = $('<input type="text" autocomplete="off" class="ui-dropdown-filter ui-inputtext ui-widget ui-state-default ui-corner-all" />')
4445 .appendTo(this.filterContainer);
4446 this.filterContainer.append('<span class="fa fa-search"></span>');
4447 }
4448
4449 this._generateItems();
4450 },
4451
4452 _postRender: function() {
4453 if(this.options.style) {
4454 this.container.attr('style', this.options.style);
4455 }
4456
4457 if(this.options.styleClass) {
4458 this.container.addClass(this.options.styleClass);
4459 }
4460
4461 this.disabled = this.element.prop('disabled')||this.options.disabled;
4462
4463 if(this.options.appendTo === 'self')
4464 this.panel.appendTo(this.container);
4465 else
4466 this.panel.appendTo(this.options.appendTo);
4467
4468 if(this.options.scrollHeight && this.panel.outerHeight() > this.options.scrollHeight) {
4469 this.itemsWrapper.height(this.options.scrollHeight);
4470 }
4471
4472 var $this = this;
4473
4474 //preselection via value option
4475 if(this.options.value) {
4476 this.choices.filter('[value="'+this.options.value+'"]').prop('selected', true);
4477 }
4478
4479 var selectedOption = this.choices.filter(':selected');
4480
4481 //disable options
4482 this.choices.filter(':disabled').each(function() {
4483 $this.items.eq($(this).index()).addClass('ui-state-disabled');
4484 });
4485
4486 //triggers
4487 this.triggers = this.options.editable ? this.menuIcon : this.container.children('.ui-dropdown-trigger, .ui-dropdown-label');
4488
4489 //activate selected
4490 if(this.options.editable) {
4491 var customInputVal = this.label.val();
4492
4493 //predefined input
4494 if(customInputVal === selectedOption.text()) {
4495 this._highlightItem(this.items.eq(selectedOption.index()));
4496 }
4497 //custom input
4498 else {
4499 this.items.eq(0).addClass('ui-state-highlight');
4500 this.customInput = true;
4501 this.customInputVal = customInputVal;
4502 }
4503 }
4504 else {
4505 this._highlightItem(this.items.eq(selectedOption.index()));
4506 }
4507
4508 if(!this.disabled) {
4509 this._bindEvents();
4510 this._bindConstantEvents();
4511 }
4512 },
4513
4514 _onRemoteOptionsLoad: function(data) {
4515 this._generateOptionElements(data);
4516 this._render();
4517 this._postRender();
4518 },
4519
4520 _generateOptionElements: function(data) {
4521 for(var i = 0; i < data.length; i++) {
4522 var choice = data[i];
4523 if(choice.label)
4524 this.element.append('<option value="' + choice.value + '">' + choice.label + '</option>');
4525 else
4526 this.element.append('<option value="' + choice + '">' + choice + '</option>');
4527 }
4528 },
4529
4530 _generateItems: function() {
4531 for(var i = 0; i < this.choices.length; i++) {
4532 var option = this.choices.eq(i),
4533 optionLabel = option.text(),
4534 content = this.options.content ? this.options.content.call(this, this.options.data[i]) : optionLabel;
4535
4536 this.itemsContainer.append('<li data-label="' + optionLabel + '" class="ui-dropdown-item ui-dropdown-list-item ui-corner-all">' + content + '</li>');
4537 }
4538
4539 this.items = this.itemsContainer.children('.ui-dropdown-item');
4540 },
4541
4542 _bindEvents: function() {
4543 var $this = this;
4544
4545 this.items.filter(':not(.ui-state-disabled)').each(function(i, item) {
4546 $this._bindItemEvents($(item));
4547 });
4548
4549 this.triggers.on('mouseenter.puidropdown', function() {
4550 if(!$this.container.hasClass('ui-state-focus')) {
4551 $this.container.addClass('ui-state-hover');
4552 $this.menuIcon.addClass('ui-state-hover');
4553 }
4554 })
4555 .on('mouseleave.puidropdown', function() {
4556 $this.container.removeClass('ui-state-hover');
4557 $this.menuIcon.removeClass('ui-state-hover');
4558 })
4559 .on('click.puidropdown', function(e) {
4560 if($this.panel.is(":hidden")) {
4561 $this._show();
4562 }
4563 else {
4564 $this._hide();
4565
4566 $this._revert();
4567 }
4568
4569 $this.container.removeClass('ui-state-hover');
4570 $this.menuIcon.removeClass('ui-state-hover');
4571 $this.focusElement.trigger('focus.puidropdown');
4572 e.preventDefault();
4573 });
4574
4575 this.focusElement.on('focus.puidropdown', function() {
4576 $this.container.addClass('ui-state-focus');
4577 $this.menuIcon.addClass('ui-state-focus');
4578 })
4579 .on('blur.puidropdown', function() {
4580 $this.container.removeClass('ui-state-focus');
4581 $this.menuIcon.removeClass('ui-state-focus');
4582 });
4583
4584 if(this.options.editable) {
4585 this.label.on('change.ui-dropdown', function() {
4586 $this._triggerChange(true);
4587 $this.customInput = true;
4588 $this.customInputVal = $(this).val();
4589 $this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
4590 $this.items.eq(0).addClass('ui-state-highlight');
4591 });
4592 }
4593
4594 this._bindKeyEvents();
4595
4596 if(this.options.filter) {
4597 this._setupFilterMatcher();
4598
4599 this.filterInput.puiinputtext();
4600
4601 this.filterInput.on('keyup.ui-dropdown', function() {
4602 $this._filter($(this).val());
4603 });
4604 }
4605 },
4606
4607 _bindItemEvents: function(item) {
4608 var $this = this;
4609
4610 item.on('mouseover.puidropdown', function() {
4611 var el = $(this);
4612
4613 if(!el.hasClass('ui-state-highlight'))
4614 $(this).addClass('ui-state-hover');
4615 })
4616 .on('mouseout.puidropdown', function() {
4617 $(this).removeClass('ui-state-hover');
4618 })
4619 .on('click.puidropdown', function() {
4620 $this._selectItem($(this));
4621 });
4622 },
4623
4624 _bindConstantEvents: function() {
4625 var $this = this;
4626
4627 $(document.body).on('mousedown.ui-dropdown-' + this.id, function (e) {
4628 if($this.panel.is(":hidden")) {
4629 return;
4630 }
4631
4632 var offset = $this.panel.offset();
4633 if (e.target === $this.label.get(0) ||
4634 e.target === $this.menuIcon.get(0) ||
4635 e.target === $this.menuIcon.children().get(0)) {
4636 return;
4637 }
4638
4639 if (e.pageX < offset.left ||
4640 e.pageX > offset.left + $this.panel.width() ||
4641 e.pageY < offset.top ||
4642 e.pageY > offset.top + $this.panel.height()) {
4643
4644 $this._hide();
4645 $this._revert();
4646 }
4647 });
4648
4649 this.resizeNS = 'resize.' + this.id;
4650 this._unbindResize();
4651 this._bindResize();
4652 },
4653
4654 _bindKeyEvents: function() {
4655 var $this = this;
4656
4657 this.focusElement.on('keydown.puidropdown', function(e) {
4658 var keyCode = $.ui.keyCode,
4659 key = e.which,
4660 activeItem;
4661
4662 switch(key) {
4663 case keyCode.UP:
4664 case keyCode.LEFT:
4665 activeItem = $this._getActiveItem();
4666 var prev = activeItem.prevAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):first');
4667
4668 if(prev.length == 1) {
4669 if($this.panel.is(':hidden')) {
4670 $this._selectItem(prev);
4671 }
4672 else {
4673 $this._highlightItem(prev);
4674 PUI.scrollInView($this.itemsWrapper, prev);
4675 }
4676 }
4677
4678 e.preventDefault();
4679 break;
4680
4681 case keyCode.DOWN:
4682 case keyCode.RIGHT:
4683 activeItem = $this._getActiveItem();
4684 var next = activeItem.nextAll(':not(.ui-state-disabled,.ui-selectonemenu-item-group):first');
4685
4686 if(next.length == 1) {
4687 if($this.panel.is(':hidden')) {
4688 if(e.altKey) {
4689 $this._show();
4690 } else {
4691 $this._selectItem(next);
4692 }
4693 }
4694 else {
4695 $this._highlightItem(next);
4696 PUI.scrollInView($this.itemsWrapper, next);
4697 }
4698 }
4699
4700 e.preventDefault();
4701 break;
4702
4703 case keyCode.ENTER:
4704 case keyCode.NUMPAD_ENTER:
4705 if($this.panel.is(':hidden')) {
4706 $this._show();
4707 }
4708 else {
4709 $this._selectItem($this._getActiveItem());
4710 }
4711
4712 e.preventDefault();
4713 break;
4714
4715 case keyCode.TAB:
4716 if($this.panel.is(':visible')) {
4717 $this._revert();
4718 $this._hide();
4719 }
4720 break;
4721
4722 case keyCode.ESCAPE:
4723 if($this.panel.is(':visible')) {
4724 $this._revert();
4725 $this._hide();
4726 }
4727 break;
4728
4729 default:
4730 var k = String.fromCharCode((96 <= key && key <= 105)? key-48 : key),
4731 currentItem = $this.items.filter('.ui-state-highlight');
4732
4733 //Search items forward from current to end and on no result, search from start until current
4734 var highlightItem = $this._search(k, currentItem.index() + 1, $this.options.length);
4735 if(!highlightItem) {
4736 highlightItem = $this._search(k, 0, currentItem.index());
4737 }
4738
4739 if(highlightItem) {
4740 if($this.panel.is(':hidden')) {
4741 $this._selectItem(highlightItem);
4742 }
4743 else {
4744 $this._highlightItem(highlightItem);
4745 PUI.scrollInView($this.itemsWrapper, highlightItem);
4746 }
4747 }
4748
4749 break;
4750 }
4751 });
4752 },
4753
4754 _unbindEvents: function() {
4755 this.items.off('mouseover.puidropdown mouseout.puidropdown click.puidropdown');
4756 this.triggers.off('mouseenter.puidropdown mouseleave.puidropdown click.puidropdown');
4757 this.focusElement.off('keydown.puidropdown focus.puidropdown blur.puidropdown');
4758
4759 if(this.options.editable) {
4760 this.label.off('change.puidropdown');
4761 }
4762
4763 if(this.options.filter) {
4764 this.filterInput.off('keyup.ui-dropdown');
4765 }
4766
4767 $(document.body).off('mousedown.ui-dropdown-' + this.id);
4768 this._unbindResize();
4769 },
4770
4771 _selectItem: function(item, silent) {
4772 var selectedOption = this.choices.eq(this._resolveItemIndex(item)),
4773 currentOption = this.choices.filter(':selected'),
4774 sameOption = selectedOption.val() == currentOption.val(),
4775 shouldChange = null;
4776
4777 if(this.options.editable) {
4778 shouldChange = (!sameOption)||(selectedOption.text() != this.label.val());
4779 }
4780 else {
4781 shouldChange = !sameOption;
4782 }
4783
4784 if(shouldChange) {
4785 this._highlightItem(item);
4786 this.element.val(selectedOption.val());
4787
4788 this._triggerChange();
4789
4790 if(this.options.editable) {
4791 this.customInput = false;
4792 }
4793 }
4794
4795 if(!silent) {
4796 this.focusElement.trigger('focus.puidropdown');
4797 }
4798
4799 if(this.panel.is(':visible')) {
4800 this._hide();
4801 }
4802 },
4803
4804 _highlightItem: function(item) {
4805 this.items.filter('.ui-state-highlight').removeClass('ui-state-highlight');
4806
4807 if(item.length) {
4808 item.addClass('ui-state-highlight');
4809
4810 this._setLabel(item.data('label'));
4811 }
4812 else {
4813 this._setLabel('&nbsp;');
4814 }
4815 },
4816
4817 _triggerChange: function(edited) {
4818 this.changed = false;
4819 var selectedOption = this.choices.filter(':selected');
4820
4821 if(this.options.change) {
4822 this._trigger('change', null, {
4823 value: selectedOption.val(),
4824 index: selectedOption.index()
4825 });
4826 }
4827
4828 if(!edited) {
4829 this.value = this.choices.filter(':selected').val();
4830 }
4831 },
4832
4833 _resolveItemIndex: function(item) {
4834 if(this.optGroupsSize === 0) {
4835 return item.index();
4836 }
4837 else {
4838 return item.index() - item.prevAll('li.ui-dropdown-item-group').length;
4839 }
4840 },
4841
4842 _setLabel: function(value) {
4843 if(this.options.editable) {
4844 this.label.val(value);
4845 }
4846 else {
4847 if(value === '&nbsp;') {
4848 this.label.html('&nbsp;');
4849 }
4850 else {
4851 this.label.text(value);
4852 }
4853 }
4854 },
4855
4856 _bindResize: function() {
4857 var $this = this;
4858
4859 $(window).bind(this.resizeNS, function(e) {
4860 if($this.panel.is(':visible')) {
4861 $this._alignPanel();
4862 }
4863 });
4864 },
4865
4866 _unbindResize: function() {
4867 $(window).unbind(this.resizeNS);
4868 },
4869
4870 _alignPanelWidth: function() {
4871 if(!this.panelWidthAdjusted) {
4872 var jqWidth = this.container.outerWidth();
4873 if(this.panel.outerWidth() < jqWidth) {
4874 this.panel.width(jqWidth);
4875 }
4876
4877 this.panelWidthAdjusted = true;
4878 }
4879 },
4880
4881 _alignPanel: function() {
4882 if(this.panel.parent().is(this.container)) {
4883 this.panel.css({
4884 left: '0px',
4885 top: this.container.outerHeight() + 'px'
4886 })
4887 .width(this.container.outerWidth());
4888 }
4889 else {
4890 this._alignPanelWidth();
4891 this.panel.css({left:'', top:''}).position({
4892 my: 'left top',
4893 at: 'left bottom',
4894 of: this.container,
4895 collision: 'flipfit'
4896 });
4897 }
4898 },
4899
4900 _show: function() {
4901 this._alignPanel();
4902
4903 this.panel.css('z-index', ++PUI.zindex);
4904
4905 if(this.options.effect !== 'none') {
4906 this.panel.show(this.options.effect, {}, this.options.effectSpeed);
4907 }
4908 else {
4909 this.panel.show();
4910 }
4911 this.preShowValue = this.choices.filter(':selected');
4912 },
4913
4914 _hide: function() {
4915 this.panel.hide();
4916 },
4917
4918 _revert: function() {
4919 if(this.options.editable && this.customInput) {
4920 this._setLabel(this.customInputVal);
4921 this.items.filter('.ui-state-active').removeClass('ui-state-active');
4922 this.items.eq(0).addClass('ui-state-active');
4923 }
4924 else {
4925 this._highlightItem(this.items.eq(this.preShowValue.index()));
4926 }
4927 },
4928
4929 _getActiveItem: function() {
4930 return this.items.filter('.ui-state-highlight');
4931 },
4932
4933 _setupFilterMatcher: function() {
4934 this.filterMatchers = {
4935 'startsWith': this._startsWithFilter,
4936 'contains': this._containsFilter,
4937 'endsWith': this._endsWithFilter,
4938 'custom': this.options.filterFunction
4939 };
4940
4941 this.filterMatcher = this.filterMatchers[this.options.filterMatchMode];
4942 },
4943
4944 _startsWithFilter: function(value, filter) {
4945 return value.indexOf(filter) === 0;
4946 },
4947
4948 _containsFilter: function(value, filter) {
4949 return value.indexOf(filter) !== -1;
4950 },
4951
4952 _endsWithFilter: function(value, filter) {
4953 return value.indexOf(filter, value.length - filter.length) !== -1;
4954 },
4955
4956 _filter: function(value) {
4957 this.initialHeight = this.initialHeight||this.itemsWrapper.height();
4958 var filterValue = this.options.caseSensitiveFilter ? $.trim(value) : $.trim(value).toLowerCase();
4959
4960 if(filterValue === '') {
4961 this.items.filter(':hidden').show();
4962 }
4963 else {
4964 for(var i = 0; i < this.choices.length; i++) {
4965 var option = this.choices.eq(i),
4966 itemLabel = this.options.caseSensitiveFilter ? option.text() : option.text().toLowerCase(),
4967 item = this.items.eq(i);
4968
4969 if(this.filterMatcher(itemLabel, filterValue))
4970 item.show();
4971 else
4972 item.hide();
4973 }
4974 }
4975
4976 if(this.itemsContainer.height() < this.initialHeight) {
4977 this.itemsWrapper.css('height', 'auto');
4978 }
4979 else {
4980 this.itemsWrapper.height(this.initialHeight);
4981 }
4982
4983 this._alignPanel();
4984 },
4985
4986 _search: function(text, start, end) {
4987 for(var i = start; i < end; i++) {
4988 var option = this.choices.eq(i);
4989
4990 if(option.text().indexOf(text) === 0) {
4991 return this.items.eq(i);
4992 }
4993 }
4994
4995 return null;
4996 },
4997
4998 getSelectedValue: function() {
4999 return this.element.val();

8964 more lines aren’t shown here.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t