OSDN Git Service

[TMP] Before implement websocket at front-end
[eos/zephyr.git] / front-end / dist / bundle.js
1 /******/ (function(modules) { // webpackBootstrap
2 /******/        // The module cache
3 /******/        var installedModules = {};
4
5 /******/        // The require function
6 /******/        function __webpack_require__(moduleId) {
7
8 /******/                // Check if module is in cache
9 /******/                if(installedModules[moduleId])
10 /******/                        return installedModules[moduleId].exports;
11
12 /******/                // Create a new module (and put it into the cache)
13 /******/                var module = installedModules[moduleId] = {
14 /******/                        exports: {},
15 /******/                        id: moduleId,
16 /******/                        loaded: false
17 /******/                };
18
19 /******/                // Execute the module function
20 /******/                modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21
22 /******/                // Flag the module as loaded
23 /******/                module.loaded = true;
24
25 /******/                // Return the exports of the module
26 /******/                return module.exports;
27 /******/        }
28
29
30 /******/        // expose the modules object (__webpack_modules__)
31 /******/        __webpack_require__.m = modules;
32
33 /******/        // expose the module cache
34 /******/        __webpack_require__.c = installedModules;
35
36 /******/        // __webpack_public_path__
37 /******/        __webpack_require__.p = "";
38
39 /******/        // Load entry module and return exports
40 /******/        return __webpack_require__(0);
41 /******/ })
42 /************************************************************************/
43 /******/ ([
44 /* 0 */
45 /***/ function(module, exports, __webpack_require__) {
46
47         __webpack_require__(1);
48         __webpack_require__(3);
49         __webpack_require__(4);
50         __webpack_require__(6);
51         __webpack_require__(8);
52         __webpack_require__(9);
53         __webpack_require__(10);
54         __webpack_require__(11);
55         __webpack_require__(12);
56         __webpack_require__(13);
57         __webpack_require__(14);
58         __webpack_require__(15);
59         __webpack_require__(16);
60         __webpack_require__(17);
61         __webpack_require__(18);
62         __webpack_require__(19);
63         __webpack_require__(20);
64
65
66 /***/ },
67 /* 1 */
68 /***/ function(module, exports, __webpack_require__) {
69
70         __webpack_require__(2);
71         module.exports = angular;
72
73
74 /***/ },
75 /* 2 */
76 /***/ function(module, exports) {
77
78         /**
79          * @license AngularJS v1.4.8
80          * (c) 2010-2015 Google, Inc. http://angularjs.org
81          * License: MIT
82          */
83         (function(window, document, undefined) {'use strict';
84
85         /**
86          * @description
87          *
88          * This object provides a utility for producing rich Error messages within
89          * Angular. It can be called as follows:
90          *
91          * var exampleMinErr = minErr('example');
92          * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
93          *
94          * The above creates an instance of minErr in the example namespace. The
95          * resulting error will have a namespaced error code of example.one.  The
96          * resulting error will replace {0} with the value of foo, and {1} with the
97          * value of bar. The object is not restricted in the number of arguments it can
98          * take.
99          *
100          * If fewer arguments are specified than necessary for interpolation, the extra
101          * interpolation markers will be preserved in the final string.
102          *
103          * Since data will be parsed statically during a build step, some restrictions
104          * are applied with respect to how minErr instances are created and called.
105          * Instances should have names of the form namespaceMinErr for a minErr created
106          * using minErr('namespace') . Error codes, namespaces and template strings
107          * should all be static strings, not variables or general expressions.
108          *
109          * @param {string} module The namespace to use for the new minErr instance.
110          * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
111          *   error from returned function, for cases when a particular type of error is useful.
112          * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
113          */
114
115         function minErr(module, ErrorConstructor) {
116           ErrorConstructor = ErrorConstructor || Error;
117           return function() {
118             var SKIP_INDEXES = 2;
119
120             var templateArgs = arguments,
121               code = templateArgs[0],
122               message = '[' + (module ? module + ':' : '') + code + '] ',
123               template = templateArgs[1],
124               paramPrefix, i;
125
126             message += template.replace(/\{\d+\}/g, function(match) {
127               var index = +match.slice(1, -1),
128                 shiftedIndex = index + SKIP_INDEXES;
129
130               if (shiftedIndex < templateArgs.length) {
131                 return toDebugString(templateArgs[shiftedIndex]);
132               }
133
134               return match;
135             });
136
137             message += '\nhttp://errors.angularjs.org/1.4.8/' +
138               (module ? module + '/' : '') + code;
139
140             for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
141               message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
142                 encodeURIComponent(toDebugString(templateArgs[i]));
143             }
144
145             return new ErrorConstructor(message);
146           };
147         }
148
149         /* We need to tell jshint what variables are being exported */
150         /* global angular: true,
151           msie: true,
152           jqLite: true,
153           jQuery: true,
154           slice: true,
155           splice: true,
156           push: true,
157           toString: true,
158           ngMinErr: true,
159           angularModule: true,
160           uid: true,
161           REGEX_STRING_REGEXP: true,
162           VALIDITY_STATE_PROPERTY: true,
163
164           lowercase: true,
165           uppercase: true,
166           manualLowercase: true,
167           manualUppercase: true,
168           nodeName_: true,
169           isArrayLike: true,
170           forEach: true,
171           forEachSorted: true,
172           reverseParams: true,
173           nextUid: true,
174           setHashKey: true,
175           extend: true,
176           toInt: true,
177           inherit: true,
178           merge: true,
179           noop: true,
180           identity: true,
181           valueFn: true,
182           isUndefined: true,
183           isDefined: true,
184           isObject: true,
185           isBlankObject: true,
186           isString: true,
187           isNumber: true,
188           isDate: true,
189           isArray: true,
190           isFunction: true,
191           isRegExp: true,
192           isWindow: true,
193           isScope: true,
194           isFile: true,
195           isFormData: true,
196           isBlob: true,
197           isBoolean: true,
198           isPromiseLike: true,
199           trim: true,
200           escapeForRegexp: true,
201           isElement: true,
202           makeMap: true,
203           includes: true,
204           arrayRemove: true,
205           copy: true,
206           shallowCopy: true,
207           equals: true,
208           csp: true,
209           jq: true,
210           concat: true,
211           sliceArgs: true,
212           bind: true,
213           toJsonReplacer: true,
214           toJson: true,
215           fromJson: true,
216           convertTimezoneToLocal: true,
217           timezoneToOffset: true,
218           startingTag: true,
219           tryDecodeURIComponent: true,
220           parseKeyValue: true,
221           toKeyValue: true,
222           encodeUriSegment: true,
223           encodeUriQuery: true,
224           angularInit: true,
225           bootstrap: true,
226           getTestability: true,
227           snake_case: true,
228           bindJQuery: true,
229           assertArg: true,
230           assertArgFn: true,
231           assertNotHasOwnProperty: true,
232           getter: true,
233           getBlockNodes: true,
234           hasOwnProperty: true,
235           createMap: true,
236
237           NODE_TYPE_ELEMENT: true,
238           NODE_TYPE_ATTRIBUTE: true,
239           NODE_TYPE_TEXT: true,
240           NODE_TYPE_COMMENT: true,
241           NODE_TYPE_DOCUMENT: true,
242           NODE_TYPE_DOCUMENT_FRAGMENT: true,
243         */
244
245         ////////////////////////////////////
246
247         /**
248          * @ngdoc module
249          * @name ng
250          * @module ng
251          * @description
252          *
253          * # ng (core module)
254          * The ng module is loaded by default when an AngularJS application is started. The module itself
255          * contains the essential components for an AngularJS application to function. The table below
256          * lists a high level breakdown of each of the services/factories, filters, directives and testing
257          * components available within this core module.
258          *
259          * <div doc-module-components="ng"></div>
260          */
261
262         var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
263
264         // The name of a form control's ValidityState property.
265         // This is used so that it's possible for internal tests to create mock ValidityStates.
266         var VALIDITY_STATE_PROPERTY = 'validity';
267
268         /**
269          * @ngdoc function
270          * @name angular.lowercase
271          * @module ng
272          * @kind function
273          *
274          * @description Converts the specified string to lowercase.
275          * @param {string} string String to be converted to lowercase.
276          * @returns {string} Lowercased string.
277          */
278         var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
279         var hasOwnProperty = Object.prototype.hasOwnProperty;
280
281         /**
282          * @ngdoc function
283          * @name angular.uppercase
284          * @module ng
285          * @kind function
286          *
287          * @description Converts the specified string to uppercase.
288          * @param {string} string String to be converted to uppercase.
289          * @returns {string} Uppercased string.
290          */
291         var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
292
293
294         var manualLowercase = function(s) {
295           /* jshint bitwise: false */
296           return isString(s)
297               ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
298               : s;
299         };
300         var manualUppercase = function(s) {
301           /* jshint bitwise: false */
302           return isString(s)
303               ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
304               : s;
305         };
306
307
308         // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
309         // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
310         // with correct but slower alternatives.
311         if ('i' !== 'I'.toLowerCase()) {
312           lowercase = manualLowercase;
313           uppercase = manualUppercase;
314         }
315
316
317         var
318             msie,             // holds major version number for IE, or NaN if UA is not IE.
319             jqLite,           // delay binding since jQuery could be loaded after us.
320             jQuery,           // delay binding
321             slice             = [].slice,
322             splice            = [].splice,
323             push              = [].push,
324             toString          = Object.prototype.toString,
325             getPrototypeOf    = Object.getPrototypeOf,
326             ngMinErr          = minErr('ng'),
327
328             /** @name angular */
329             angular           = window.angular || (window.angular = {}),
330             angularModule,
331             uid               = 0;
332
333         /**
334          * documentMode is an IE-only property
335          * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
336          */
337         msie = document.documentMode;
338
339
340         /**
341          * @private
342          * @param {*} obj
343          * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
344          *                   String ...)
345          */
346         function isArrayLike(obj) {
347
348           // `null`, `undefined` and `window` are not array-like
349           if (obj == null || isWindow(obj)) return false;
350
351           // arrays, strings and jQuery/jqLite objects are array like
352           // * jqLite is either the jQuery or jqLite constructor function
353           // * we have to check the existance of jqLite first as this method is called
354           //   via the forEach method when constructing the jqLite object in the first place
355           if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
356
357           // Support: iOS 8.2 (not reproducible in simulator)
358           // "length" in obj used to prevent JIT error (gh-11508)
359           var length = "length" in Object(obj) && obj.length;
360
361           // NodeList objects (with `item` method) and
362           // other objects with suitable length characteristics are array-like
363           return isNumber(length) &&
364             (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
365         }
366
367         /**
368          * @ngdoc function
369          * @name angular.forEach
370          * @module ng
371          * @kind function
372          *
373          * @description
374          * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
375          * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
376          * is the value of an object property or an array element, `key` is the object property key or
377          * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
378          *
379          * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
380          * using the `hasOwnProperty` method.
381          *
382          * Unlike ES262's
383          * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
384          * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
385          * return the value provided.
386          *
387            ```js
388              var values = {name: 'misko', gender: 'male'};
389              var log = [];
390              angular.forEach(values, function(value, key) {
391                this.push(key + ': ' + value);
392              }, log);
393              expect(log).toEqual(['name: misko', 'gender: male']);
394            ```
395          *
396          * @param {Object|Array} obj Object to iterate over.
397          * @param {Function} iterator Iterator function.
398          * @param {Object=} context Object to become context (`this`) for the iterator function.
399          * @returns {Object|Array} Reference to `obj`.
400          */
401
402         function forEach(obj, iterator, context) {
403           var key, length;
404           if (obj) {
405             if (isFunction(obj)) {
406               for (key in obj) {
407                 // Need to check if hasOwnProperty exists,
408                 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
409                 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
410                   iterator.call(context, obj[key], key, obj);
411                 }
412               }
413             } else if (isArray(obj) || isArrayLike(obj)) {
414               var isPrimitive = typeof obj !== 'object';
415               for (key = 0, length = obj.length; key < length; key++) {
416                 if (isPrimitive || key in obj) {
417                   iterator.call(context, obj[key], key, obj);
418                 }
419               }
420             } else if (obj.forEach && obj.forEach !== forEach) {
421                 obj.forEach(iterator, context, obj);
422             } else if (isBlankObject(obj)) {
423               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
424               for (key in obj) {
425                 iterator.call(context, obj[key], key, obj);
426               }
427             } else if (typeof obj.hasOwnProperty === 'function') {
428               // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
429               for (key in obj) {
430                 if (obj.hasOwnProperty(key)) {
431                   iterator.call(context, obj[key], key, obj);
432                 }
433               }
434             } else {
435               // Slow path for objects which do not have a method `hasOwnProperty`
436               for (key in obj) {
437                 if (hasOwnProperty.call(obj, key)) {
438                   iterator.call(context, obj[key], key, obj);
439                 }
440               }
441             }
442           }
443           return obj;
444         }
445
446         function forEachSorted(obj, iterator, context) {
447           var keys = Object.keys(obj).sort();
448           for (var i = 0; i < keys.length; i++) {
449             iterator.call(context, obj[keys[i]], keys[i]);
450           }
451           return keys;
452         }
453
454
455         /**
456          * when using forEach the params are value, key, but it is often useful to have key, value.
457          * @param {function(string, *)} iteratorFn
458          * @returns {function(*, string)}
459          */
460         function reverseParams(iteratorFn) {
461           return function(value, key) { iteratorFn(key, value); };
462         }
463
464         /**
465          * A consistent way of creating unique IDs in angular.
466          *
467          * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
468          * we hit number precision issues in JavaScript.
469          *
470          * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
471          *
472          * @returns {number} an unique alpha-numeric string
473          */
474         function nextUid() {
475           return ++uid;
476         }
477
478
479         /**
480          * Set or clear the hashkey for an object.
481          * @param obj object
482          * @param h the hashkey (!truthy to delete the hashkey)
483          */
484         function setHashKey(obj, h) {
485           if (h) {
486             obj.$$hashKey = h;
487           } else {
488             delete obj.$$hashKey;
489           }
490         }
491
492
493         function baseExtend(dst, objs, deep) {
494           var h = dst.$$hashKey;
495
496           for (var i = 0, ii = objs.length; i < ii; ++i) {
497             var obj = objs[i];
498             if (!isObject(obj) && !isFunction(obj)) continue;
499             var keys = Object.keys(obj);
500             for (var j = 0, jj = keys.length; j < jj; j++) {
501               var key = keys[j];
502               var src = obj[key];
503
504               if (deep && isObject(src)) {
505                 if (isDate(src)) {
506                   dst[key] = new Date(src.valueOf());
507                 } else if (isRegExp(src)) {
508                   dst[key] = new RegExp(src);
509                 } else if (src.nodeName) {
510                   dst[key] = src.cloneNode(true);
511                 } else if (isElement(src)) {
512                   dst[key] = src.clone();
513                 } else {
514                   if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
515                   baseExtend(dst[key], [src], true);
516                 }
517               } else {
518                 dst[key] = src;
519               }
520             }
521           }
522
523           setHashKey(dst, h);
524           return dst;
525         }
526
527         /**
528          * @ngdoc function
529          * @name angular.extend
530          * @module ng
531          * @kind function
532          *
533          * @description
534          * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
535          * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
536          * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
537          *
538          * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
539          * {@link angular.merge} for this.
540          *
541          * @param {Object} dst Destination object.
542          * @param {...Object} src Source object(s).
543          * @returns {Object} Reference to `dst`.
544          */
545         function extend(dst) {
546           return baseExtend(dst, slice.call(arguments, 1), false);
547         }
548
549
550         /**
551         * @ngdoc function
552         * @name angular.merge
553         * @module ng
554         * @kind function
555         *
556         * @description
557         * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
558         * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
559         * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
560         *
561         * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
562         * objects, performing a deep copy.
563         *
564         * @param {Object} dst Destination object.
565         * @param {...Object} src Source object(s).
566         * @returns {Object} Reference to `dst`.
567         */
568         function merge(dst) {
569           return baseExtend(dst, slice.call(arguments, 1), true);
570         }
571
572
573
574         function toInt(str) {
575           return parseInt(str, 10);
576         }
577
578
579         function inherit(parent, extra) {
580           return extend(Object.create(parent), extra);
581         }
582
583         /**
584          * @ngdoc function
585          * @name angular.noop
586          * @module ng
587          * @kind function
588          *
589          * @description
590          * A function that performs no operations. This function can be useful when writing code in the
591          * functional style.
592            ```js
593              function foo(callback) {
594                var result = calculateResult();
595                (callback || angular.noop)(result);
596              }
597            ```
598          */
599         function noop() {}
600         noop.$inject = [];
601
602
603         /**
604          * @ngdoc function
605          * @name angular.identity
606          * @module ng
607          * @kind function
608          *
609          * @description
610          * A function that returns its first argument. This function is useful when writing code in the
611          * functional style.
612          *
613            ```js
614              function transformer(transformationFn, value) {
615                return (transformationFn || angular.identity)(value);
616              };
617            ```
618           * @param {*} value to be returned.
619           * @returns {*} the value passed in.
620          */
621         function identity($) {return $;}
622         identity.$inject = [];
623
624
625         function valueFn(value) {return function() {return value;};}
626
627         function hasCustomToString(obj) {
628           return isFunction(obj.toString) && obj.toString !== toString;
629         }
630
631
632         /**
633          * @ngdoc function
634          * @name angular.isUndefined
635          * @module ng
636          * @kind function
637          *
638          * @description
639          * Determines if a reference is undefined.
640          *
641          * @param {*} value Reference to check.
642          * @returns {boolean} True if `value` is undefined.
643          */
644         function isUndefined(value) {return typeof value === 'undefined';}
645
646
647         /**
648          * @ngdoc function
649          * @name angular.isDefined
650          * @module ng
651          * @kind function
652          *
653          * @description
654          * Determines if a reference is defined.
655          *
656          * @param {*} value Reference to check.
657          * @returns {boolean} True if `value` is defined.
658          */
659         function isDefined(value) {return typeof value !== 'undefined';}
660
661
662         /**
663          * @ngdoc function
664          * @name angular.isObject
665          * @module ng
666          * @kind function
667          *
668          * @description
669          * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
670          * considered to be objects. Note that JavaScript arrays are objects.
671          *
672          * @param {*} value Reference to check.
673          * @returns {boolean} True if `value` is an `Object` but not `null`.
674          */
675         function isObject(value) {
676           // http://jsperf.com/isobject4
677           return value !== null && typeof value === 'object';
678         }
679
680
681         /**
682          * Determine if a value is an object with a null prototype
683          *
684          * @returns {boolean} True if `value` is an `Object` with a null prototype
685          */
686         function isBlankObject(value) {
687           return value !== null && typeof value === 'object' && !getPrototypeOf(value);
688         }
689
690
691         /**
692          * @ngdoc function
693          * @name angular.isString
694          * @module ng
695          * @kind function
696          *
697          * @description
698          * Determines if a reference is a `String`.
699          *
700          * @param {*} value Reference to check.
701          * @returns {boolean} True if `value` is a `String`.
702          */
703         function isString(value) {return typeof value === 'string';}
704
705
706         /**
707          * @ngdoc function
708          * @name angular.isNumber
709          * @module ng
710          * @kind function
711          *
712          * @description
713          * Determines if a reference is a `Number`.
714          *
715          * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
716          *
717          * If you wish to exclude these then you can use the native
718          * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
719          * method.
720          *
721          * @param {*} value Reference to check.
722          * @returns {boolean} True if `value` is a `Number`.
723          */
724         function isNumber(value) {return typeof value === 'number';}
725
726
727         /**
728          * @ngdoc function
729          * @name angular.isDate
730          * @module ng
731          * @kind function
732          *
733          * @description
734          * Determines if a value is a date.
735          *
736          * @param {*} value Reference to check.
737          * @returns {boolean} True if `value` is a `Date`.
738          */
739         function isDate(value) {
740           return toString.call(value) === '[object Date]';
741         }
742
743
744         /**
745          * @ngdoc function
746          * @name angular.isArray
747          * @module ng
748          * @kind function
749          *
750          * @description
751          * Determines if a reference is an `Array`.
752          *
753          * @param {*} value Reference to check.
754          * @returns {boolean} True if `value` is an `Array`.
755          */
756         var isArray = Array.isArray;
757
758         /**
759          * @ngdoc function
760          * @name angular.isFunction
761          * @module ng
762          * @kind function
763          *
764          * @description
765          * Determines if a reference is a `Function`.
766          *
767          * @param {*} value Reference to check.
768          * @returns {boolean} True if `value` is a `Function`.
769          */
770         function isFunction(value) {return typeof value === 'function';}
771
772
773         /**
774          * Determines if a value is a regular expression object.
775          *
776          * @private
777          * @param {*} value Reference to check.
778          * @returns {boolean} True if `value` is a `RegExp`.
779          */
780         function isRegExp(value) {
781           return toString.call(value) === '[object RegExp]';
782         }
783
784
785         /**
786          * Checks if `obj` is a window object.
787          *
788          * @private
789          * @param {*} obj Object to check
790          * @returns {boolean} True if `obj` is a window obj.
791          */
792         function isWindow(obj) {
793           return obj && obj.window === obj;
794         }
795
796
797         function isScope(obj) {
798           return obj && obj.$evalAsync && obj.$watch;
799         }
800
801
802         function isFile(obj) {
803           return toString.call(obj) === '[object File]';
804         }
805
806
807         function isFormData(obj) {
808           return toString.call(obj) === '[object FormData]';
809         }
810
811
812         function isBlob(obj) {
813           return toString.call(obj) === '[object Blob]';
814         }
815
816
817         function isBoolean(value) {
818           return typeof value === 'boolean';
819         }
820
821
822         function isPromiseLike(obj) {
823           return obj && isFunction(obj.then);
824         }
825
826
827         var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
828         function isTypedArray(value) {
829           return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
830         }
831
832
833         var trim = function(value) {
834           return isString(value) ? value.trim() : value;
835         };
836
837         // Copied from:
838         // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
839         // Prereq: s is a string.
840         var escapeForRegexp = function(s) {
841           return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
842                    replace(/\x08/g, '\\x08');
843         };
844
845
846         /**
847          * @ngdoc function
848          * @name angular.isElement
849          * @module ng
850          * @kind function
851          *
852          * @description
853          * Determines if a reference is a DOM element (or wrapped jQuery element).
854          *
855          * @param {*} value Reference to check.
856          * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
857          */
858         function isElement(node) {
859           return !!(node &&
860             (node.nodeName  // we are a direct element
861             || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
862         }
863
864         /**
865          * @param str 'key1,key2,...'
866          * @returns {object} in the form of {key1:true, key2:true, ...}
867          */
868         function makeMap(str) {
869           var obj = {}, items = str.split(","), i;
870           for (i = 0; i < items.length; i++) {
871             obj[items[i]] = true;
872           }
873           return obj;
874         }
875
876
877         function nodeName_(element) {
878           return lowercase(element.nodeName || (element[0] && element[0].nodeName));
879         }
880
881         function includes(array, obj) {
882           return Array.prototype.indexOf.call(array, obj) != -1;
883         }
884
885         function arrayRemove(array, value) {
886           var index = array.indexOf(value);
887           if (index >= 0) {
888             array.splice(index, 1);
889           }
890           return index;
891         }
892
893         /**
894          * @ngdoc function
895          * @name angular.copy
896          * @module ng
897          * @kind function
898          *
899          * @description
900          * Creates a deep copy of `source`, which should be an object or an array.
901          *
902          * * If no destination is supplied, a copy of the object or array is created.
903          * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
904          *   are deleted and then all elements/properties from the source are copied to it.
905          * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
906          * * If `source` is identical to 'destination' an exception will be thrown.
907          *
908          * @param {*} source The source that will be used to make a copy.
909          *                   Can be any type, including primitives, `null`, and `undefined`.
910          * @param {(Object|Array)=} destination Destination into which the source is copied. If
911          *     provided, must be of the same type as `source`.
912          * @returns {*} The copy or updated `destination`, if `destination` was specified.
913          *
914          * @example
915          <example module="copyExample">
916          <file name="index.html">
917          <div ng-controller="ExampleController">
918          <form novalidate class="simple-form">
919          Name: <input type="text" ng-model="user.name" /><br />
920          E-mail: <input type="email" ng-model="user.email" /><br />
921          Gender: <input type="radio" ng-model="user.gender" value="male" />male
922          <input type="radio" ng-model="user.gender" value="female" />female<br />
923          <button ng-click="reset()">RESET</button>
924          <button ng-click="update(user)">SAVE</button>
925          </form>
926          <pre>form = {{user | json}}</pre>
927          <pre>master = {{master | json}}</pre>
928          </div>
929
930          <script>
931           angular.module('copyExample', [])
932             .controller('ExampleController', ['$scope', function($scope) {
933               $scope.master= {};
934
935               $scope.update = function(user) {
936                 // Example with 1 argument
937                 $scope.master= angular.copy(user);
938               };
939
940               $scope.reset = function() {
941                 // Example with 2 arguments
942                 angular.copy($scope.master, $scope.user);
943               };
944
945               $scope.reset();
946             }]);
947          </script>
948          </file>
949          </example>
950          */
951         function copy(source, destination) {
952           var stackSource = [];
953           var stackDest = [];
954
955           if (destination) {
956             if (isTypedArray(destination)) {
957               throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
958             }
959             if (source === destination) {
960               throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
961             }
962
963             // Empty the destination object
964             if (isArray(destination)) {
965               destination.length = 0;
966             } else {
967               forEach(destination, function(value, key) {
968                 if (key !== '$$hashKey') {
969                   delete destination[key];
970                 }
971               });
972             }
973
974             stackSource.push(source);
975             stackDest.push(destination);
976             return copyRecurse(source, destination);
977           }
978
979           return copyElement(source);
980
981           function copyRecurse(source, destination) {
982             var h = destination.$$hashKey;
983             var result, key;
984             if (isArray(source)) {
985               for (var i = 0, ii = source.length; i < ii; i++) {
986                 destination.push(copyElement(source[i]));
987               }
988             } else if (isBlankObject(source)) {
989               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
990               for (key in source) {
991                 destination[key] = copyElement(source[key]);
992               }
993             } else if (source && typeof source.hasOwnProperty === 'function') {
994               // Slow path, which must rely on hasOwnProperty
995               for (key in source) {
996                 if (source.hasOwnProperty(key)) {
997                   destination[key] = copyElement(source[key]);
998                 }
999               }
1000             } else {
1001               // Slowest path --- hasOwnProperty can't be called as a method
1002               for (key in source) {
1003                 if (hasOwnProperty.call(source, key)) {
1004                   destination[key] = copyElement(source[key]);
1005                 }
1006               }
1007             }
1008             setHashKey(destination, h);
1009             return destination;
1010           }
1011
1012           function copyElement(source) {
1013             // Simple values
1014             if (!isObject(source)) {
1015               return source;
1016             }
1017
1018             // Already copied values
1019             var index = stackSource.indexOf(source);
1020             if (index !== -1) {
1021               return stackDest[index];
1022             }
1023
1024             if (isWindow(source) || isScope(source)) {
1025               throw ngMinErr('cpws',
1026                 "Can't copy! Making copies of Window or Scope instances is not supported.");
1027             }
1028
1029             var needsRecurse = false;
1030             var destination;
1031
1032             if (isArray(source)) {
1033               destination = [];
1034               needsRecurse = true;
1035             } else if (isTypedArray(source)) {
1036               destination = new source.constructor(source);
1037             } else if (isDate(source)) {
1038               destination = new Date(source.getTime());
1039             } else if (isRegExp(source)) {
1040               destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1041               destination.lastIndex = source.lastIndex;
1042             } else if (isFunction(source.cloneNode)) {
1043                 destination = source.cloneNode(true);
1044             } else {
1045               destination = Object.create(getPrototypeOf(source));
1046               needsRecurse = true;
1047             }
1048
1049             stackSource.push(source);
1050             stackDest.push(destination);
1051
1052             return needsRecurse
1053               ? copyRecurse(source, destination)
1054               : destination;
1055           }
1056         }
1057
1058         /**
1059          * Creates a shallow copy of an object, an array or a primitive.
1060          *
1061          * Assumes that there are no proto properties for objects.
1062          */
1063         function shallowCopy(src, dst) {
1064           if (isArray(src)) {
1065             dst = dst || [];
1066
1067             for (var i = 0, ii = src.length; i < ii; i++) {
1068               dst[i] = src[i];
1069             }
1070           } else if (isObject(src)) {
1071             dst = dst || {};
1072
1073             for (var key in src) {
1074               if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1075                 dst[key] = src[key];
1076               }
1077             }
1078           }
1079
1080           return dst || src;
1081         }
1082
1083
1084         /**
1085          * @ngdoc function
1086          * @name angular.equals
1087          * @module ng
1088          * @kind function
1089          *
1090          * @description
1091          * Determines if two objects or two values are equivalent. Supports value types, regular
1092          * expressions, arrays and objects.
1093          *
1094          * Two objects or values are considered equivalent if at least one of the following is true:
1095          *
1096          * * Both objects or values pass `===` comparison.
1097          * * Both objects or values are of the same type and all of their properties are equal by
1098          *   comparing them with `angular.equals`.
1099          * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1100          * * Both values represent the same regular expression (In JavaScript,
1101          *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1102          *   representation matches).
1103          *
1104          * During a property comparison, properties of `function` type and properties with names
1105          * that begin with `$` are ignored.
1106          *
1107          * Scope and DOMWindow objects are being compared only by identify (`===`).
1108          *
1109          * @param {*} o1 Object or value to compare.
1110          * @param {*} o2 Object or value to compare.
1111          * @returns {boolean} True if arguments are equal.
1112          */
1113         function equals(o1, o2) {
1114           if (o1 === o2) return true;
1115           if (o1 === null || o2 === null) return false;
1116           if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1117           var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1118           if (t1 == t2) {
1119             if (t1 == 'object') {
1120               if (isArray(o1)) {
1121                 if (!isArray(o2)) return false;
1122                 if ((length = o1.length) == o2.length) {
1123                   for (key = 0; key < length; key++) {
1124                     if (!equals(o1[key], o2[key])) return false;
1125                   }
1126                   return true;
1127                 }
1128               } else if (isDate(o1)) {
1129                 if (!isDate(o2)) return false;
1130                 return equals(o1.getTime(), o2.getTime());
1131               } else if (isRegExp(o1)) {
1132                 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1133               } else {
1134                 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1135                   isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1136                 keySet = createMap();
1137                 for (key in o1) {
1138                   if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1139                   if (!equals(o1[key], o2[key])) return false;
1140                   keySet[key] = true;
1141                 }
1142                 for (key in o2) {
1143                   if (!(key in keySet) &&
1144                       key.charAt(0) !== '$' &&
1145                       isDefined(o2[key]) &&
1146                       !isFunction(o2[key])) return false;
1147                 }
1148                 return true;
1149               }
1150             }
1151           }
1152           return false;
1153         }
1154
1155         var csp = function() {
1156           if (!isDefined(csp.rules)) {
1157
1158
1159             var ngCspElement = (document.querySelector('[ng-csp]') ||
1160                             document.querySelector('[data-ng-csp]'));
1161
1162             if (ngCspElement) {
1163               var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1164                             ngCspElement.getAttribute('data-ng-csp');
1165               csp.rules = {
1166                 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1167                 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1168               };
1169             } else {
1170               csp.rules = {
1171                 noUnsafeEval: noUnsafeEval(),
1172                 noInlineStyle: false
1173               };
1174             }
1175           }
1176
1177           return csp.rules;
1178
1179           function noUnsafeEval() {
1180             try {
1181               /* jshint -W031, -W054 */
1182               new Function('');
1183               /* jshint +W031, +W054 */
1184               return false;
1185             } catch (e) {
1186               return true;
1187             }
1188           }
1189         };
1190
1191         /**
1192          * @ngdoc directive
1193          * @module ng
1194          * @name ngJq
1195          *
1196          * @element ANY
1197          * @param {string=} ngJq the name of the library available under `window`
1198          * to be used for angular.element
1199          * @description
1200          * Use this directive to force the angular.element library.  This should be
1201          * used to force either jqLite by leaving ng-jq blank or setting the name of
1202          * the jquery variable under window (eg. jQuery).
1203          *
1204          * Since angular looks for this directive when it is loaded (doesn't wait for the
1205          * DOMContentLoaded event), it must be placed on an element that comes before the script
1206          * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1207          * others ignored.
1208          *
1209          * @example
1210          * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1211          ```html
1212          <!doctype html>
1213          <html ng-app ng-jq>
1214          ...
1215          ...
1216          </html>
1217          ```
1218          * @example
1219          * This example shows how to use a jQuery based library of a different name.
1220          * The library name must be available at the top most 'window'.
1221          ```html
1222          <!doctype html>
1223          <html ng-app ng-jq="jQueryLib">
1224          ...
1225          ...
1226          </html>
1227          ```
1228          */
1229         var jq = function() {
1230           if (isDefined(jq.name_)) return jq.name_;
1231           var el;
1232           var i, ii = ngAttrPrefixes.length, prefix, name;
1233           for (i = 0; i < ii; ++i) {
1234             prefix = ngAttrPrefixes[i];
1235             if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1236               name = el.getAttribute(prefix + 'jq');
1237               break;
1238             }
1239           }
1240
1241           return (jq.name_ = name);
1242         };
1243
1244         function concat(array1, array2, index) {
1245           return array1.concat(slice.call(array2, index));
1246         }
1247
1248         function sliceArgs(args, startIndex) {
1249           return slice.call(args, startIndex || 0);
1250         }
1251
1252
1253         /* jshint -W101 */
1254         /**
1255          * @ngdoc function
1256          * @name angular.bind
1257          * @module ng
1258          * @kind function
1259          *
1260          * @description
1261          * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1262          * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1263          * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1264          * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1265          *
1266          * @param {Object} self Context which `fn` should be evaluated in.
1267          * @param {function()} fn Function to be bound.
1268          * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1269          * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1270          */
1271         /* jshint +W101 */
1272         function bind(self, fn) {
1273           var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1274           if (isFunction(fn) && !(fn instanceof RegExp)) {
1275             return curryArgs.length
1276               ? function() {
1277                   return arguments.length
1278                     ? fn.apply(self, concat(curryArgs, arguments, 0))
1279                     : fn.apply(self, curryArgs);
1280                 }
1281               : function() {
1282                   return arguments.length
1283                     ? fn.apply(self, arguments)
1284                     : fn.call(self);
1285                 };
1286           } else {
1287             // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1288             return fn;
1289           }
1290         }
1291
1292
1293         function toJsonReplacer(key, value) {
1294           var val = value;
1295
1296           if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1297             val = undefined;
1298           } else if (isWindow(value)) {
1299             val = '$WINDOW';
1300           } else if (value &&  document === value) {
1301             val = '$DOCUMENT';
1302           } else if (isScope(value)) {
1303             val = '$SCOPE';
1304           }
1305
1306           return val;
1307         }
1308
1309
1310         /**
1311          * @ngdoc function
1312          * @name angular.toJson
1313          * @module ng
1314          * @kind function
1315          *
1316          * @description
1317          * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1318          * stripped since angular uses this notation internally.
1319          *
1320          * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1321          * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1322          *    If set to an integer, the JSON output will contain that many spaces per indentation.
1323          * @returns {string|undefined} JSON-ified string representing `obj`.
1324          */
1325         function toJson(obj, pretty) {
1326           if (typeof obj === 'undefined') return undefined;
1327           if (!isNumber(pretty)) {
1328             pretty = pretty ? 2 : null;
1329           }
1330           return JSON.stringify(obj, toJsonReplacer, pretty);
1331         }
1332
1333
1334         /**
1335          * @ngdoc function
1336          * @name angular.fromJson
1337          * @module ng
1338          * @kind function
1339          *
1340          * @description
1341          * Deserializes a JSON string.
1342          *
1343          * @param {string} json JSON string to deserialize.
1344          * @returns {Object|Array|string|number} Deserialized JSON string.
1345          */
1346         function fromJson(json) {
1347           return isString(json)
1348               ? JSON.parse(json)
1349               : json;
1350         }
1351
1352
1353         function timezoneToOffset(timezone, fallback) {
1354           var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1355           return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1356         }
1357
1358
1359         function addDateMinutes(date, minutes) {
1360           date = new Date(date.getTime());
1361           date.setMinutes(date.getMinutes() + minutes);
1362           return date;
1363         }
1364
1365
1366         function convertTimezoneToLocal(date, timezone, reverse) {
1367           reverse = reverse ? -1 : 1;
1368           var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1369           return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1370         }
1371
1372
1373         /**
1374          * @returns {string} Returns the string representation of the element.
1375          */
1376         function startingTag(element) {
1377           element = jqLite(element).clone();
1378           try {
1379             // turns out IE does not let you set .html() on elements which
1380             // are not allowed to have children. So we just ignore it.
1381             element.empty();
1382           } catch (e) {}
1383           var elemHtml = jqLite('<div>').append(element).html();
1384           try {
1385             return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1386                 elemHtml.
1387                   match(/^(<[^>]+>)/)[1].
1388                   replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1389           } catch (e) {
1390             return lowercase(elemHtml);
1391           }
1392
1393         }
1394
1395
1396         /////////////////////////////////////////////////
1397
1398         /**
1399          * Tries to decode the URI component without throwing an exception.
1400          *
1401          * @private
1402          * @param str value potential URI component to check.
1403          * @returns {boolean} True if `value` can be decoded
1404          * with the decodeURIComponent function.
1405          */
1406         function tryDecodeURIComponent(value) {
1407           try {
1408             return decodeURIComponent(value);
1409           } catch (e) {
1410             // Ignore any invalid uri component
1411           }
1412         }
1413
1414
1415         /**
1416          * Parses an escaped url query string into key-value pairs.
1417          * @returns {Object.<string,boolean|Array>}
1418          */
1419         function parseKeyValue(/**string*/keyValue) {
1420           var obj = {};
1421           forEach((keyValue || "").split('&'), function(keyValue) {
1422             var splitPoint, key, val;
1423             if (keyValue) {
1424               key = keyValue = keyValue.replace(/\+/g,'%20');
1425               splitPoint = keyValue.indexOf('=');
1426               if (splitPoint !== -1) {
1427                 key = keyValue.substring(0, splitPoint);
1428                 val = keyValue.substring(splitPoint + 1);
1429               }
1430               key = tryDecodeURIComponent(key);
1431               if (isDefined(key)) {
1432                 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1433                 if (!hasOwnProperty.call(obj, key)) {
1434                   obj[key] = val;
1435                 } else if (isArray(obj[key])) {
1436                   obj[key].push(val);
1437                 } else {
1438                   obj[key] = [obj[key],val];
1439                 }
1440               }
1441             }
1442           });
1443           return obj;
1444         }
1445
1446         function toKeyValue(obj) {
1447           var parts = [];
1448           forEach(obj, function(value, key) {
1449             if (isArray(value)) {
1450               forEach(value, function(arrayValue) {
1451                 parts.push(encodeUriQuery(key, true) +
1452                            (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1453               });
1454             } else {
1455             parts.push(encodeUriQuery(key, true) +
1456                        (value === true ? '' : '=' + encodeUriQuery(value, true)));
1457             }
1458           });
1459           return parts.length ? parts.join('&') : '';
1460         }
1461
1462
1463         /**
1464          * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1465          * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1466          * segments:
1467          *    segment       = *pchar
1468          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1469          *    pct-encoded   = "%" HEXDIG HEXDIG
1470          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1471          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1472          *                     / "*" / "+" / "," / ";" / "="
1473          */
1474         function encodeUriSegment(val) {
1475           return encodeUriQuery(val, true).
1476                      replace(/%26/gi, '&').
1477                      replace(/%3D/gi, '=').
1478                      replace(/%2B/gi, '+');
1479         }
1480
1481
1482         /**
1483          * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1484          * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1485          * encoded per http://tools.ietf.org/html/rfc3986:
1486          *    query       = *( pchar / "/" / "?" )
1487          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1488          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1489          *    pct-encoded   = "%" HEXDIG HEXDIG
1490          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1491          *                     / "*" / "+" / "," / ";" / "="
1492          */
1493         function encodeUriQuery(val, pctEncodeSpaces) {
1494           return encodeURIComponent(val).
1495                      replace(/%40/gi, '@').
1496                      replace(/%3A/gi, ':').
1497                      replace(/%24/g, '$').
1498                      replace(/%2C/gi, ',').
1499                      replace(/%3B/gi, ';').
1500                      replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1501         }
1502
1503         var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1504
1505         function getNgAttribute(element, ngAttr) {
1506           var attr, i, ii = ngAttrPrefixes.length;
1507           for (i = 0; i < ii; ++i) {
1508             attr = ngAttrPrefixes[i] + ngAttr;
1509             if (isString(attr = element.getAttribute(attr))) {
1510               return attr;
1511             }
1512           }
1513           return null;
1514         }
1515
1516         /**
1517          * @ngdoc directive
1518          * @name ngApp
1519          * @module ng
1520          *
1521          * @element ANY
1522          * @param {angular.Module} ngApp an optional application
1523          *   {@link angular.module module} name to load.
1524          * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1525          *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1526          *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1527          *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1528          *   tracking down the root of these bugs.
1529          *
1530          * @description
1531          *
1532          * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1533          * designates the **root element** of the application and is typically placed near the root element
1534          * of the page - e.g. on the `<body>` or `<html>` tags.
1535          *
1536          * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1537          * found in the document will be used to define the root element to auto-bootstrap as an
1538          * application. To run multiple applications in an HTML document you must manually bootstrap them using
1539          * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1540          *
1541          * You can specify an **AngularJS module** to be used as the root module for the application.  This
1542          * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1543          * should contain the application code needed or have dependencies on other modules that will
1544          * contain the code. See {@link angular.module} for more information.
1545          *
1546          * In the example below if the `ngApp` directive were not placed on the `html` element then the
1547          * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1548          * would not be resolved to `3`.
1549          *
1550          * `ngApp` is the easiest, and most common way to bootstrap an application.
1551          *
1552          <example module="ngAppDemo">
1553            <file name="index.html">
1554            <div ng-controller="ngAppDemoController">
1555              I can add: {{a}} + {{b}} =  {{ a+b }}
1556            </div>
1557            </file>
1558            <file name="script.js">
1559            angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1560              $scope.a = 1;
1561              $scope.b = 2;
1562            });
1563            </file>
1564          </example>
1565          *
1566          * Using `ngStrictDi`, you would see something like this:
1567          *
1568          <example ng-app-included="true">
1569            <file name="index.html">
1570            <div ng-app="ngAppStrictDemo" ng-strict-di>
1571                <div ng-controller="GoodController1">
1572                    I can add: {{a}} + {{b}} =  {{ a+b }}
1573
1574                    <p>This renders because the controller does not fail to
1575                       instantiate, by using explicit annotation style (see
1576                       script.js for details)
1577                    </p>
1578                </div>
1579
1580                <div ng-controller="GoodController2">
1581                    Name: <input ng-model="name"><br />
1582                    Hello, {{name}}!
1583
1584                    <p>This renders because the controller does not fail to
1585                       instantiate, by using explicit annotation style
1586                       (see script.js for details)
1587                    </p>
1588                </div>
1589
1590                <div ng-controller="BadController">
1591                    I can add: {{a}} + {{b}} =  {{ a+b }}
1592
1593                    <p>The controller could not be instantiated, due to relying
1594                       on automatic function annotations (which are disabled in
1595                       strict mode). As such, the content of this section is not
1596                       interpolated, and there should be an error in your web console.
1597                    </p>
1598                </div>
1599            </div>
1600            </file>
1601            <file name="script.js">
1602            angular.module('ngAppStrictDemo', [])
1603              // BadController will fail to instantiate, due to relying on automatic function annotation,
1604              // rather than an explicit annotation
1605              .controller('BadController', function($scope) {
1606                $scope.a = 1;
1607                $scope.b = 2;
1608              })
1609              // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1610              // due to using explicit annotations using the array style and $inject property, respectively.
1611              .controller('GoodController1', ['$scope', function($scope) {
1612                $scope.a = 1;
1613                $scope.b = 2;
1614              }])
1615              .controller('GoodController2', GoodController2);
1616              function GoodController2($scope) {
1617                $scope.name = "World";
1618              }
1619              GoodController2.$inject = ['$scope'];
1620            </file>
1621            <file name="style.css">
1622            div[ng-controller] {
1623                margin-bottom: 1em;
1624                -webkit-border-radius: 4px;
1625                border-radius: 4px;
1626                border: 1px solid;
1627                padding: .5em;
1628            }
1629            div[ng-controller^=Good] {
1630                border-color: #d6e9c6;
1631                background-color: #dff0d8;
1632                color: #3c763d;
1633            }
1634            div[ng-controller^=Bad] {
1635                border-color: #ebccd1;
1636                background-color: #f2dede;
1637                color: #a94442;
1638                margin-bottom: 0;
1639            }
1640            </file>
1641          </example>
1642          */
1643         function angularInit(element, bootstrap) {
1644           var appElement,
1645               module,
1646               config = {};
1647
1648           // The element `element` has priority over any other element
1649           forEach(ngAttrPrefixes, function(prefix) {
1650             var name = prefix + 'app';
1651
1652             if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1653               appElement = element;
1654               module = element.getAttribute(name);
1655             }
1656           });
1657           forEach(ngAttrPrefixes, function(prefix) {
1658             var name = prefix + 'app';
1659             var candidate;
1660
1661             if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1662               appElement = candidate;
1663               module = candidate.getAttribute(name);
1664             }
1665           });
1666           if (appElement) {
1667             config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1668             bootstrap(appElement, module ? [module] : [], config);
1669           }
1670         }
1671
1672         /**
1673          * @ngdoc function
1674          * @name angular.bootstrap
1675          * @module ng
1676          * @description
1677          * Use this function to manually start up angular application.
1678          *
1679          * See: {@link guide/bootstrap Bootstrap}
1680          *
1681          * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1682          * They must use {@link ng.directive:ngApp ngApp}.
1683          *
1684          * Angular will detect if it has been loaded into the browser more than once and only allow the
1685          * first loaded script to be bootstrapped and will report a warning to the browser console for
1686          * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1687          * multiple instances of Angular try to work on the DOM.
1688          *
1689          * ```html
1690          * <!doctype html>
1691          * <html>
1692          * <body>
1693          * <div ng-controller="WelcomeController">
1694          *   {{greeting}}
1695          * </div>
1696          *
1697          * <script src="angular.js"></script>
1698          * <script>
1699          *   var app = angular.module('demo', [])
1700          *   .controller('WelcomeController', function($scope) {
1701          *       $scope.greeting = 'Welcome!';
1702          *   });
1703          *   angular.bootstrap(document, ['demo']);
1704          * </script>
1705          * </body>
1706          * </html>
1707          * ```
1708          *
1709          * @param {DOMElement} element DOM element which is the root of angular application.
1710          * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1711          *     Each item in the array should be the name of a predefined module or a (DI annotated)
1712          *     function that will be invoked by the injector as a `config` block.
1713          *     See: {@link angular.module modules}
1714          * @param {Object=} config an object for defining configuration options for the application. The
1715          *     following keys are supported:
1716          *
1717          * * `strictDi` - disable automatic function annotation for the application. This is meant to
1718          *   assist in finding bugs which break minified code. Defaults to `false`.
1719          *
1720          * @returns {auto.$injector} Returns the newly created injector for this app.
1721          */
1722         function bootstrap(element, modules, config) {
1723           if (!isObject(config)) config = {};
1724           var defaultConfig = {
1725             strictDi: false
1726           };
1727           config = extend(defaultConfig, config);
1728           var doBootstrap = function() {
1729             element = jqLite(element);
1730
1731             if (element.injector()) {
1732               var tag = (element[0] === document) ? 'document' : startingTag(element);
1733               //Encode angle brackets to prevent input from being sanitized to empty string #8683
1734               throw ngMinErr(
1735                   'btstrpd',
1736                   "App Already Bootstrapped with this Element '{0}'",
1737                   tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1738             }
1739
1740             modules = modules || [];
1741             modules.unshift(['$provide', function($provide) {
1742               $provide.value('$rootElement', element);
1743             }]);
1744
1745             if (config.debugInfoEnabled) {
1746               // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1747               modules.push(['$compileProvider', function($compileProvider) {
1748                 $compileProvider.debugInfoEnabled(true);
1749               }]);
1750             }
1751
1752             modules.unshift('ng');
1753             var injector = createInjector(modules, config.strictDi);
1754             injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1755                function bootstrapApply(scope, element, compile, injector) {
1756                 scope.$apply(function() {
1757                   element.data('$injector', injector);
1758                   compile(element)(scope);
1759                 });
1760               }]
1761             );
1762             return injector;
1763           };
1764
1765           var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1766           var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1767
1768           if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1769             config.debugInfoEnabled = true;
1770             window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1771           }
1772
1773           if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1774             return doBootstrap();
1775           }
1776
1777           window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1778           angular.resumeBootstrap = function(extraModules) {
1779             forEach(extraModules, function(module) {
1780               modules.push(module);
1781             });
1782             return doBootstrap();
1783           };
1784
1785           if (isFunction(angular.resumeDeferredBootstrap)) {
1786             angular.resumeDeferredBootstrap();
1787           }
1788         }
1789
1790         /**
1791          * @ngdoc function
1792          * @name angular.reloadWithDebugInfo
1793          * @module ng
1794          * @description
1795          * Use this function to reload the current application with debug information turned on.
1796          * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1797          *
1798          * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1799          */
1800         function reloadWithDebugInfo() {
1801           window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1802           window.location.reload();
1803         }
1804
1805         /**
1806          * @name angular.getTestability
1807          * @module ng
1808          * @description
1809          * Get the testability service for the instance of Angular on the given
1810          * element.
1811          * @param {DOMElement} element DOM element which is the root of angular application.
1812          */
1813         function getTestability(rootElement) {
1814           var injector = angular.element(rootElement).injector();
1815           if (!injector) {
1816             throw ngMinErr('test',
1817               'no injector found for element argument to getTestability');
1818           }
1819           return injector.get('$$testability');
1820         }
1821
1822         var SNAKE_CASE_REGEXP = /[A-Z]/g;
1823         function snake_case(name, separator) {
1824           separator = separator || '_';
1825           return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1826             return (pos ? separator : '') + letter.toLowerCase();
1827           });
1828         }
1829
1830         var bindJQueryFired = false;
1831         var skipDestroyOnNextJQueryCleanData;
1832         function bindJQuery() {
1833           var originalCleanData;
1834
1835           if (bindJQueryFired) {
1836             return;
1837           }
1838
1839           // bind to jQuery if present;
1840           var jqName = jq();
1841           jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1842                    !jqName             ? undefined     :   // use jqLite
1843                                          window[jqName];   // use jQuery specified by `ngJq`
1844
1845           // Use jQuery if it exists with proper functionality, otherwise default to us.
1846           // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1847           // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1848           // versions. It will not work for sure with jQuery <1.7, though.
1849           if (jQuery && jQuery.fn.on) {
1850             jqLite = jQuery;
1851             extend(jQuery.fn, {
1852               scope: JQLitePrototype.scope,
1853               isolateScope: JQLitePrototype.isolateScope,
1854               controller: JQLitePrototype.controller,
1855               injector: JQLitePrototype.injector,
1856               inheritedData: JQLitePrototype.inheritedData
1857             });
1858
1859             // All nodes removed from the DOM via various jQuery APIs like .remove()
1860             // are passed through jQuery.cleanData. Monkey-patch this method to fire
1861             // the $destroy event on all removed nodes.
1862             originalCleanData = jQuery.cleanData;
1863             jQuery.cleanData = function(elems) {
1864               var events;
1865               if (!skipDestroyOnNextJQueryCleanData) {
1866                 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1867                   events = jQuery._data(elem, "events");
1868                   if (events && events.$destroy) {
1869                     jQuery(elem).triggerHandler('$destroy');
1870                   }
1871                 }
1872               } else {
1873                 skipDestroyOnNextJQueryCleanData = false;
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          */
1890         function assertArg(arg, name, reason) {
1891           if (!arg) {
1892             throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1893           }
1894           return arg;
1895         }
1896
1897         function 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          */
1912         function 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
1926         function 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          */
1950         function 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          */
1980         function createMap() {
1981           return Object.create(null);
1982         }
1983
1984         var NODE_TYPE_ELEMENT = 1;
1985         var NODE_TYPE_ATTRIBUTE = 2;
1986         var NODE_TYPE_TEXT = 3;
1987         var NODE_TYPE_COMMENT = 8;
1988         var NODE_TYPE_DOCUMENT = 9;
1989         var 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
2000         function 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 {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} The name of the service to decorate.
2189                    * @param {Function} 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#config
2276                    * @module ng
2277                    * @param {Function} configFn Execute this function on module load. Useful for service
2278                    *    configuration.
2279                    * @description
2280                    * Use this method to register work which needs to be performed on module loading.
2281                    * For more about how to configure services, see
2282                    * {@link providers#provider-recipe Provider Recipe}.
2283                    */
2284                   config: config,
2285
2286                   /**
2287                    * @ngdoc method
2288                    * @name angular.Module#run
2289                    * @module ng
2290                    * @param {Function} initializationFn Execute this function after injector creation.
2291                    *    Useful for application initialization.
2292                    * @description
2293                    * Use this method to register work which should be performed when the injector is done
2294                    * loading all modules.
2295                    */
2296                   run: function(block) {
2297                     runBlocks.push(block);
2298                     return this;
2299                   }
2300                 };
2301
2302                 if (configFn) {
2303                   config(configFn);
2304                 }
2305
2306                 return moduleInstance;
2307
2308                 /**
2309                  * @param {string} provider
2310                  * @param {string} method
2311                  * @param {String=} insertMethod
2312                  * @returns {angular.Module}
2313                  */
2314                 function invokeLater(provider, method, insertMethod, queue) {
2315                   if (!queue) queue = invokeQueue;
2316                   return function() {
2317                     queue[insertMethod || 'push']([provider, method, arguments]);
2318                     return moduleInstance;
2319                   };
2320                 }
2321
2322                 /**
2323                  * @param {string} provider
2324                  * @param {string} method
2325                  * @returns {angular.Module}
2326                  */
2327                 function invokeLaterAndSetModuleName(provider, method) {
2328                   return function(recipeName, factoryFunction) {
2329                     if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2330                     invokeQueue.push([provider, method, arguments]);
2331                     return moduleInstance;
2332                   };
2333                 }
2334               });
2335             };
2336           });
2337
2338         }
2339
2340         /* global: toDebugString: true */
2341
2342         function serializeObject(obj) {
2343           var seen = [];
2344
2345           return JSON.stringify(obj, function(key, val) {
2346             val = toJsonReplacer(key, val);
2347             if (isObject(val)) {
2348
2349               if (seen.indexOf(val) >= 0) return '...';
2350
2351               seen.push(val);
2352             }
2353             return val;
2354           });
2355         }
2356
2357         function toDebugString(obj) {
2358           if (typeof obj === 'function') {
2359             return obj.toString().replace(/ \{[\s\S]*$/, '');
2360           } else if (isUndefined(obj)) {
2361             return 'undefined';
2362           } else if (typeof obj !== 'string') {
2363             return serializeObject(obj);
2364           }
2365           return obj;
2366         }
2367
2368         /* global angularModule: true,
2369           version: true,
2370
2371           $CompileProvider,
2372
2373           htmlAnchorDirective,
2374           inputDirective,
2375           inputDirective,
2376           formDirective,
2377           scriptDirective,
2378           selectDirective,
2379           styleDirective,
2380           optionDirective,
2381           ngBindDirective,
2382           ngBindHtmlDirective,
2383           ngBindTemplateDirective,
2384           ngClassDirective,
2385           ngClassEvenDirective,
2386           ngClassOddDirective,
2387           ngCloakDirective,
2388           ngControllerDirective,
2389           ngFormDirective,
2390           ngHideDirective,
2391           ngIfDirective,
2392           ngIncludeDirective,
2393           ngIncludeFillContentDirective,
2394           ngInitDirective,
2395           ngNonBindableDirective,
2396           ngPluralizeDirective,
2397           ngRepeatDirective,
2398           ngShowDirective,
2399           ngStyleDirective,
2400           ngSwitchDirective,
2401           ngSwitchWhenDirective,
2402           ngSwitchDefaultDirective,
2403           ngOptionsDirective,
2404           ngTranscludeDirective,
2405           ngModelDirective,
2406           ngListDirective,
2407           ngChangeDirective,
2408           patternDirective,
2409           patternDirective,
2410           requiredDirective,
2411           requiredDirective,
2412           minlengthDirective,
2413           minlengthDirective,
2414           maxlengthDirective,
2415           maxlengthDirective,
2416           ngValueDirective,
2417           ngModelOptionsDirective,
2418           ngAttributeAliasDirectives,
2419           ngEventDirectives,
2420
2421           $AnchorScrollProvider,
2422           $AnimateProvider,
2423           $CoreAnimateCssProvider,
2424           $$CoreAnimateQueueProvider,
2425           $$CoreAnimateRunnerProvider,
2426           $BrowserProvider,
2427           $CacheFactoryProvider,
2428           $ControllerProvider,
2429           $DocumentProvider,
2430           $ExceptionHandlerProvider,
2431           $FilterProvider,
2432           $$ForceReflowProvider,
2433           $InterpolateProvider,
2434           $IntervalProvider,
2435           $$HashMapProvider,
2436           $HttpProvider,
2437           $HttpParamSerializerProvider,
2438           $HttpParamSerializerJQLikeProvider,
2439           $HttpBackendProvider,
2440           $xhrFactoryProvider,
2441           $LocationProvider,
2442           $LogProvider,
2443           $ParseProvider,
2444           $RootScopeProvider,
2445           $QProvider,
2446           $$QProvider,
2447           $$SanitizeUriProvider,
2448           $SceProvider,
2449           $SceDelegateProvider,
2450           $SnifferProvider,
2451           $TemplateCacheProvider,
2452           $TemplateRequestProvider,
2453           $$TestabilityProvider,
2454           $TimeoutProvider,
2455           $$RAFProvider,
2456           $WindowProvider,
2457           $$jqLiteProvider,
2458           $$CookieReaderProvider
2459         */
2460
2461
2462         /**
2463          * @ngdoc object
2464          * @name angular.version
2465          * @module ng
2466          * @description
2467          * An object that contains information about the current AngularJS version.
2468          *
2469          * This object has the following properties:
2470          *
2471          * - `full` – `{string}` – Full version string, such as "0.9.18".
2472          * - `major` – `{number}` – Major version number, such as "0".
2473          * - `minor` – `{number}` – Minor version number, such as "9".
2474          * - `dot` – `{number}` – Dot version number, such as "18".
2475          * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2476          */
2477         var version = {
2478           full: '1.4.8',    // all of these placeholder strings will be replaced by grunt's
2479           major: 1,    // package task
2480           minor: 4,
2481           dot: 8,
2482           codeName: 'ice-manipulation'
2483         };
2484
2485
2486         function publishExternalAPI(angular) {
2487           extend(angular, {
2488             'bootstrap': bootstrap,
2489             'copy': copy,
2490             'extend': extend,
2491             'merge': merge,
2492             'equals': equals,
2493             'element': jqLite,
2494             'forEach': forEach,
2495             'injector': createInjector,
2496             'noop': noop,
2497             'bind': bind,
2498             'toJson': toJson,
2499             'fromJson': fromJson,
2500             'identity': identity,
2501             'isUndefined': isUndefined,
2502             'isDefined': isDefined,
2503             'isString': isString,
2504             'isFunction': isFunction,
2505             'isObject': isObject,
2506             'isNumber': isNumber,
2507             'isElement': isElement,
2508             'isArray': isArray,
2509             'version': version,
2510             'isDate': isDate,
2511             'lowercase': lowercase,
2512             'uppercase': uppercase,
2513             'callbacks': {counter: 0},
2514             'getTestability': getTestability,
2515             '$$minErr': minErr,
2516             '$$csp': csp,
2517             'reloadWithDebugInfo': reloadWithDebugInfo
2518           });
2519
2520           angularModule = setupModuleLoader(window);
2521
2522           angularModule('ng', ['ngLocale'], ['$provide',
2523             function ngModule($provide) {
2524               // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2525               $provide.provider({
2526                 $$sanitizeUri: $$SanitizeUriProvider
2527               });
2528               $provide.provider('$compile', $CompileProvider).
2529                 directive({
2530                     a: htmlAnchorDirective,
2531                     input: inputDirective,
2532                     textarea: inputDirective,
2533                     form: formDirective,
2534                     script: scriptDirective,
2535                     select: selectDirective,
2536                     style: styleDirective,
2537                     option: optionDirective,
2538                     ngBind: ngBindDirective,
2539                     ngBindHtml: ngBindHtmlDirective,
2540                     ngBindTemplate: ngBindTemplateDirective,
2541                     ngClass: ngClassDirective,
2542                     ngClassEven: ngClassEvenDirective,
2543                     ngClassOdd: ngClassOddDirective,
2544                     ngCloak: ngCloakDirective,
2545                     ngController: ngControllerDirective,
2546                     ngForm: ngFormDirective,
2547                     ngHide: ngHideDirective,
2548                     ngIf: ngIfDirective,
2549                     ngInclude: ngIncludeDirective,
2550                     ngInit: ngInitDirective,
2551                     ngNonBindable: ngNonBindableDirective,
2552                     ngPluralize: ngPluralizeDirective,
2553                     ngRepeat: ngRepeatDirective,
2554                     ngShow: ngShowDirective,
2555                     ngStyle: ngStyleDirective,
2556                     ngSwitch: ngSwitchDirective,
2557                     ngSwitchWhen: ngSwitchWhenDirective,
2558                     ngSwitchDefault: ngSwitchDefaultDirective,
2559                     ngOptions: ngOptionsDirective,
2560                     ngTransclude: ngTranscludeDirective,
2561                     ngModel: ngModelDirective,
2562                     ngList: ngListDirective,
2563                     ngChange: ngChangeDirective,
2564                     pattern: patternDirective,
2565                     ngPattern: patternDirective,
2566                     required: requiredDirective,
2567                     ngRequired: requiredDirective,
2568                     minlength: minlengthDirective,
2569                     ngMinlength: minlengthDirective,
2570                     maxlength: maxlengthDirective,
2571                     ngMaxlength: maxlengthDirective,
2572                     ngValue: ngValueDirective,
2573                     ngModelOptions: ngModelOptionsDirective
2574                 }).
2575                 directive({
2576                   ngInclude: ngIncludeFillContentDirective
2577                 }).
2578                 directive(ngAttributeAliasDirectives).
2579                 directive(ngEventDirectives);
2580               $provide.provider({
2581                 $anchorScroll: $AnchorScrollProvider,
2582                 $animate: $AnimateProvider,
2583                 $animateCss: $CoreAnimateCssProvider,
2584                 $$animateQueue: $$CoreAnimateQueueProvider,
2585                 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2586                 $browser: $BrowserProvider,
2587                 $cacheFactory: $CacheFactoryProvider,
2588                 $controller: $ControllerProvider,
2589                 $document: $DocumentProvider,
2590                 $exceptionHandler: $ExceptionHandlerProvider,
2591                 $filter: $FilterProvider,
2592                 $$forceReflow: $$ForceReflowProvider,
2593                 $interpolate: $InterpolateProvider,
2594                 $interval: $IntervalProvider,
2595                 $http: $HttpProvider,
2596                 $httpParamSerializer: $HttpParamSerializerProvider,
2597                 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2598                 $httpBackend: $HttpBackendProvider,
2599                 $xhrFactory: $xhrFactoryProvider,
2600                 $location: $LocationProvider,
2601                 $log: $LogProvider,
2602                 $parse: $ParseProvider,
2603                 $rootScope: $RootScopeProvider,
2604                 $q: $QProvider,
2605                 $$q: $$QProvider,
2606                 $sce: $SceProvider,
2607                 $sceDelegate: $SceDelegateProvider,
2608                 $sniffer: $SnifferProvider,
2609                 $templateCache: $TemplateCacheProvider,
2610                 $templateRequest: $TemplateRequestProvider,
2611                 $$testability: $$TestabilityProvider,
2612                 $timeout: $TimeoutProvider,
2613                 $window: $WindowProvider,
2614                 $$rAF: $$RAFProvider,
2615                 $$jqLite: $$jqLiteProvider,
2616                 $$HashMap: $$HashMapProvider,
2617                 $$cookieReader: $$CookieReaderProvider
2618               });
2619             }
2620           ]);
2621         }
2622
2623         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2624          *     Any commits to this file should be reviewed with security in mind.  *
2625          *   Changes to this file can potentially create security vulnerabilities. *
2626          *          An approval from 2 Core members with history of modifying      *
2627          *                         this file is required.                          *
2628          *                                                                         *
2629          *  Does the change somehow allow for arbitrary javascript to be executed? *
2630          *    Or allows for someone to change the prototype of built-in objects?   *
2631          *     Or gives undesired access to variables likes document or window?    *
2632          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2633
2634         /* global JQLitePrototype: true,
2635           addEventListenerFn: true,
2636           removeEventListenerFn: true,
2637           BOOLEAN_ATTR: true,
2638           ALIASED_ATTR: true,
2639         */
2640
2641         //////////////////////////////////
2642         //JQLite
2643         //////////////////////////////////
2644
2645         /**
2646          * @ngdoc function
2647          * @name angular.element
2648          * @module ng
2649          * @kind function
2650          *
2651          * @description
2652          * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2653          *
2654          * If jQuery is available, `angular.element` is an alias for the
2655          * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2656          * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2657          *
2658          * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2659          * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2660          * commonly needed functionality with the goal of having a very small footprint.</div>
2661          *
2662          * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2663          *
2664          * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2665          * jqLite; they are never raw DOM references.</div>
2666          *
2667          * ## Angular's jqLite
2668          * jqLite provides only the following jQuery methods:
2669          *
2670          * - [`addClass()`](http://api.jquery.com/addClass/)
2671          * - [`after()`](http://api.jquery.com/after/)
2672          * - [`append()`](http://api.jquery.com/append/)
2673          * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2674          * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2675          * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2676          * - [`clone()`](http://api.jquery.com/clone/)
2677          * - [`contents()`](http://api.jquery.com/contents/)
2678          * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2679          * - [`data()`](http://api.jquery.com/data/)
2680          * - [`detach()`](http://api.jquery.com/detach/)
2681          * - [`empty()`](http://api.jquery.com/empty/)
2682          * - [`eq()`](http://api.jquery.com/eq/)
2683          * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2684          * - [`hasClass()`](http://api.jquery.com/hasClass/)
2685          * - [`html()`](http://api.jquery.com/html/)
2686          * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2687          * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2688          * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2689          * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2690          * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2691          * - [`prepend()`](http://api.jquery.com/prepend/)
2692          * - [`prop()`](http://api.jquery.com/prop/)
2693          * - [`ready()`](http://api.jquery.com/ready/)
2694          * - [`remove()`](http://api.jquery.com/remove/)
2695          * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2696          * - [`removeClass()`](http://api.jquery.com/removeClass/)
2697          * - [`removeData()`](http://api.jquery.com/removeData/)
2698          * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2699          * - [`text()`](http://api.jquery.com/text/)
2700          * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2701          * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2702          * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2703          * - [`val()`](http://api.jquery.com/val/)
2704          * - [`wrap()`](http://api.jquery.com/wrap/)
2705          *
2706          * ## jQuery/jqLite Extras
2707          * Angular also provides the following additional methods and events to both jQuery and jqLite:
2708          *
2709          * ### Events
2710          * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2711          *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2712          *    element before it is removed.
2713          *
2714          * ### Methods
2715          * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2716          *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2717          *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2718          *   `'ngModel'`).
2719          * - `injector()` - retrieves the injector of the current element or its parent.
2720          * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2721          *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2722          *   be enabled.
2723          * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2724          *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2725          *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2726          *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2727          * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2728          *   parent element is reached.
2729          *
2730          * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2731          * @returns {Object} jQuery object.
2732          */
2733
2734         JQLite.expando = 'ng339';
2735
2736         var jqCache = JQLite.cache = {},
2737             jqId = 1,
2738             addEventListenerFn = function(element, type, fn) {
2739               element.addEventListener(type, fn, false);
2740             },
2741             removeEventListenerFn = function(element, type, fn) {
2742               element.removeEventListener(type, fn, false);
2743             };
2744
2745         /*
2746          * !!! This is an undocumented "private" function !!!
2747          */
2748         JQLite._data = function(node) {
2749           //jQuery always returns an object on cache miss
2750           return this.cache[node[this.expando]] || {};
2751         };
2752
2753         function jqNextId() { return ++jqId; }
2754
2755
2756         var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2757         var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2758         var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2759         var jqLiteMinErr = minErr('jqLite');
2760
2761         /**
2762          * Converts snake_case to camelCase.
2763          * Also there is special case for Moz prefix starting with upper case letter.
2764          * @param name Name to normalize
2765          */
2766         function camelCase(name) {
2767           return name.
2768             replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2769               return offset ? letter.toUpperCase() : letter;
2770             }).
2771             replace(MOZ_HACK_REGEXP, 'Moz$1');
2772         }
2773
2774         var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2775         var HTML_REGEXP = /<|&#?\w+;/;
2776         var TAG_NAME_REGEXP = /<([\w:-]+)/;
2777         var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2778
2779         var wrapMap = {
2780           'option': [1, '<select multiple="multiple">', '</select>'],
2781
2782           'thead': [1, '<table>', '</table>'],
2783           'col': [2, '<table><colgroup>', '</colgroup></table>'],
2784           'tr': [2, '<table><tbody>', '</tbody></table>'],
2785           'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2786           '_default': [0, "", ""]
2787         };
2788
2789         wrapMap.optgroup = wrapMap.option;
2790         wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2791         wrapMap.th = wrapMap.td;
2792
2793
2794         function jqLiteIsTextNode(html) {
2795           return !HTML_REGEXP.test(html);
2796         }
2797
2798         function jqLiteAcceptsData(node) {
2799           // The window object can accept data but has no nodeType
2800           // Otherwise we are only interested in elements (1) and documents (9)
2801           var nodeType = node.nodeType;
2802           return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2803         }
2804
2805         function jqLiteHasData(node) {
2806           for (var key in jqCache[node.ng339]) {
2807             return true;
2808           }
2809           return false;
2810         }
2811
2812         function jqLiteBuildFragment(html, context) {
2813           var tmp, tag, wrap,
2814               fragment = context.createDocumentFragment(),
2815               nodes = [], i;
2816
2817           if (jqLiteIsTextNode(html)) {
2818             // Convert non-html into a text node
2819             nodes.push(context.createTextNode(html));
2820           } else {
2821             // Convert html into DOM nodes
2822             tmp = tmp || fragment.appendChild(context.createElement("div"));
2823             tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2824             wrap = wrapMap[tag] || wrapMap._default;
2825             tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2826
2827             // Descend through wrappers to the right content
2828             i = wrap[0];
2829             while (i--) {
2830               tmp = tmp.lastChild;
2831             }
2832
2833             nodes = concat(nodes, tmp.childNodes);
2834
2835             tmp = fragment.firstChild;
2836             tmp.textContent = "";
2837           }
2838
2839           // Remove wrapper from fragment
2840           fragment.textContent = "";
2841           fragment.innerHTML = ""; // Clear inner HTML
2842           forEach(nodes, function(node) {
2843             fragment.appendChild(node);
2844           });
2845
2846           return fragment;
2847         }
2848
2849         function jqLiteParseHTML(html, context) {
2850           context = context || document;
2851           var parsed;
2852
2853           if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2854             return [context.createElement(parsed[1])];
2855           }
2856
2857           if ((parsed = jqLiteBuildFragment(html, context))) {
2858             return parsed.childNodes;
2859           }
2860
2861           return [];
2862         }
2863
2864
2865         // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2866         var jqLiteContains = Node.prototype.contains || function(arg) {
2867           // jshint bitwise: false
2868           return !!(this.compareDocumentPosition(arg) & 16);
2869           // jshint bitwise: true
2870         };
2871
2872         /////////////////////////////////////////////
2873         function JQLite(element) {
2874           if (element instanceof JQLite) {
2875             return element;
2876           }
2877
2878           var argIsString;
2879
2880           if (isString(element)) {
2881             element = trim(element);
2882             argIsString = true;
2883           }
2884           if (!(this instanceof JQLite)) {
2885             if (argIsString && element.charAt(0) != '<') {
2886               throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2887             }
2888             return new JQLite(element);
2889           }
2890
2891           if (argIsString) {
2892             jqLiteAddNodes(this, jqLiteParseHTML(element));
2893           } else {
2894             jqLiteAddNodes(this, element);
2895           }
2896         }
2897
2898         function jqLiteClone(element) {
2899           return element.cloneNode(true);
2900         }
2901
2902         function jqLiteDealoc(element, onlyDescendants) {
2903           if (!onlyDescendants) jqLiteRemoveData(element);
2904
2905           if (element.querySelectorAll) {
2906             var descendants = element.querySelectorAll('*');
2907             for (var i = 0, l = descendants.length; i < l; i++) {
2908               jqLiteRemoveData(descendants[i]);
2909             }
2910           }
2911         }
2912
2913         function jqLiteOff(element, type, fn, unsupported) {
2914           if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2915
2916           var expandoStore = jqLiteExpandoStore(element);
2917           var events = expandoStore && expandoStore.events;
2918           var handle = expandoStore && expandoStore.handle;
2919
2920           if (!handle) return; //no listeners registered
2921
2922           if (!type) {
2923             for (type in events) {
2924               if (type !== '$destroy') {
2925                 removeEventListenerFn(element, type, handle);
2926               }
2927               delete events[type];
2928             }
2929           } else {
2930
2931             var removeHandler = function(type) {
2932               var listenerFns = events[type];
2933               if (isDefined(fn)) {
2934                 arrayRemove(listenerFns || [], fn);
2935               }
2936               if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2937                 removeEventListenerFn(element, type, handle);
2938                 delete events[type];
2939               }
2940             };
2941
2942             forEach(type.split(' '), function(type) {
2943               removeHandler(type);
2944               if (MOUSE_EVENT_MAP[type]) {
2945                 removeHandler(MOUSE_EVENT_MAP[type]);
2946               }
2947             });
2948           }
2949         }
2950
2951         function jqLiteRemoveData(element, name) {
2952           var expandoId = element.ng339;
2953           var expandoStore = expandoId && jqCache[expandoId];
2954
2955           if (expandoStore) {
2956             if (name) {
2957               delete expandoStore.data[name];
2958               return;
2959             }
2960
2961             if (expandoStore.handle) {
2962               if (expandoStore.events.$destroy) {
2963                 expandoStore.handle({}, '$destroy');
2964               }
2965               jqLiteOff(element);
2966             }
2967             delete jqCache[expandoId];
2968             element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2969           }
2970         }
2971
2972
2973         function jqLiteExpandoStore(element, createIfNecessary) {
2974           var expandoId = element.ng339,
2975               expandoStore = expandoId && jqCache[expandoId];
2976
2977           if (createIfNecessary && !expandoStore) {
2978             element.ng339 = expandoId = jqNextId();
2979             expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2980           }
2981
2982           return expandoStore;
2983         }
2984
2985
2986         function jqLiteData(element, key, value) {
2987           if (jqLiteAcceptsData(element)) {
2988
2989             var isSimpleSetter = isDefined(value);
2990             var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2991             var massGetter = !key;
2992             var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2993             var data = expandoStore && expandoStore.data;
2994
2995             if (isSimpleSetter) { // data('key', value)
2996               data[key] = value;
2997             } else {
2998               if (massGetter) {  // data()
2999                 return data;
3000               } else {
3001                 if (isSimpleGetter) { // data('key')
3002                   // don't force creation of expandoStore if it doesn't exist yet
3003                   return data && data[key];
3004                 } else { // mass-setter: data({key1: val1, key2: val2})
3005                   extend(data, key);
3006                 }
3007               }
3008             }
3009           }
3010         }
3011
3012         function jqLiteHasClass(element, selector) {
3013           if (!element.getAttribute) return false;
3014           return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3015               indexOf(" " + selector + " ") > -1);
3016         }
3017
3018         function jqLiteRemoveClass(element, cssClasses) {
3019           if (cssClasses && element.setAttribute) {
3020             forEach(cssClasses.split(' '), function(cssClass) {
3021               element.setAttribute('class', trim(
3022                   (" " + (element.getAttribute('class') || '') + " ")
3023                   .replace(/[\n\t]/g, " ")
3024                   .replace(" " + trim(cssClass) + " ", " "))
3025               );
3026             });
3027           }
3028         }
3029
3030         function jqLiteAddClass(element, cssClasses) {
3031           if (cssClasses && element.setAttribute) {
3032             var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3033                                     .replace(/[\n\t]/g, " ");
3034
3035             forEach(cssClasses.split(' '), function(cssClass) {
3036               cssClass = trim(cssClass);
3037               if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3038                 existingClasses += cssClass + ' ';
3039               }
3040             });
3041
3042             element.setAttribute('class', trim(existingClasses));
3043           }
3044         }
3045
3046
3047         function jqLiteAddNodes(root, elements) {
3048           // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3049
3050           if (elements) {
3051
3052             // if a Node (the most common case)
3053             if (elements.nodeType) {
3054               root[root.length++] = elements;
3055             } else {
3056               var length = elements.length;
3057
3058               // if an Array or NodeList and not a Window
3059               if (typeof length === 'number' && elements.window !== elements) {
3060                 if (length) {
3061                   for (var i = 0; i < length; i++) {
3062                     root[root.length++] = elements[i];
3063                   }
3064                 }
3065               } else {
3066                 root[root.length++] = elements;
3067               }
3068             }
3069           }
3070         }
3071
3072
3073         function jqLiteController(element, name) {
3074           return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3075         }
3076
3077         function jqLiteInheritedData(element, name, value) {
3078           // if element is the document object work with the html element instead
3079           // this makes $(document).scope() possible
3080           if (element.nodeType == NODE_TYPE_DOCUMENT) {
3081             element = element.documentElement;
3082           }
3083           var names = isArray(name) ? name : [name];
3084
3085           while (element) {
3086             for (var i = 0, ii = names.length; i < ii; i++) {
3087               if (isDefined(value = jqLite.data(element, names[i]))) return value;
3088             }
3089
3090             // If dealing with a document fragment node with a host element, and no parent, use the host
3091             // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3092             // to lookup parent controllers.
3093             element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3094           }
3095         }
3096
3097         function jqLiteEmpty(element) {
3098           jqLiteDealoc(element, true);
3099           while (element.firstChild) {
3100             element.removeChild(element.firstChild);
3101           }
3102         }
3103
3104         function jqLiteRemove(element, keepData) {
3105           if (!keepData) jqLiteDealoc(element);
3106           var parent = element.parentNode;
3107           if (parent) parent.removeChild(element);
3108         }
3109
3110
3111         function jqLiteDocumentLoaded(action, win) {
3112           win = win || window;
3113           if (win.document.readyState === 'complete') {
3114             // Force the action to be run async for consistent behaviour
3115             // from the action's point of view
3116             // i.e. it will definitely not be in a $apply
3117             win.setTimeout(action);
3118           } else {
3119             // No need to unbind this handler as load is only ever called once
3120             jqLite(win).on('load', action);
3121           }
3122         }
3123
3124         //////////////////////////////////////////
3125         // Functions which are declared directly.
3126         //////////////////////////////////////////
3127         var JQLitePrototype = JQLite.prototype = {
3128           ready: function(fn) {
3129             var fired = false;
3130
3131             function trigger() {
3132               if (fired) return;
3133               fired = true;
3134               fn();
3135             }
3136
3137             // check if document is already loaded
3138             if (document.readyState === 'complete') {
3139               setTimeout(trigger);
3140             } else {
3141               this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3142               // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3143               // jshint -W064
3144               JQLite(window).on('load', trigger); // fallback to window.onload for others
3145               // jshint +W064
3146             }
3147           },
3148           toString: function() {
3149             var value = [];
3150             forEach(this, function(e) { value.push('' + e);});
3151             return '[' + value.join(', ') + ']';
3152           },
3153
3154           eq: function(index) {
3155               return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3156           },
3157
3158           length: 0,
3159           push: push,
3160           sort: [].sort,
3161           splice: [].splice
3162         };
3163
3164         //////////////////////////////////////////
3165         // Functions iterating getter/setters.
3166         // these functions return self on setter and
3167         // value on get.
3168         //////////////////////////////////////////
3169         var BOOLEAN_ATTR = {};
3170         forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3171           BOOLEAN_ATTR[lowercase(value)] = value;
3172         });
3173         var BOOLEAN_ELEMENTS = {};
3174         forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3175           BOOLEAN_ELEMENTS[value] = true;
3176         });
3177         var ALIASED_ATTR = {
3178           'ngMinlength': 'minlength',
3179           'ngMaxlength': 'maxlength',
3180           'ngMin': 'min',
3181           'ngMax': 'max',
3182           'ngPattern': 'pattern'
3183         };
3184
3185         function getBooleanAttrName(element, name) {
3186           // check dom last since we will most likely fail on name
3187           var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3188
3189           // booleanAttr is here twice to minimize DOM access
3190           return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3191         }
3192
3193         function getAliasedAttrName(name) {
3194           return ALIASED_ATTR[name];
3195         }
3196
3197         forEach({
3198           data: jqLiteData,
3199           removeData: jqLiteRemoveData,
3200           hasData: jqLiteHasData
3201         }, function(fn, name) {
3202           JQLite[name] = fn;
3203         });
3204
3205         forEach({
3206           data: jqLiteData,
3207           inheritedData: jqLiteInheritedData,
3208
3209           scope: function(element) {
3210             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3211             return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3212           },
3213
3214           isolateScope: function(element) {
3215             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3216             return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3217           },
3218
3219           controller: jqLiteController,
3220
3221           injector: function(element) {
3222             return jqLiteInheritedData(element, '$injector');
3223           },
3224
3225           removeAttr: function(element, name) {
3226             element.removeAttribute(name);
3227           },
3228
3229           hasClass: jqLiteHasClass,
3230
3231           css: function(element, name, value) {
3232             name = camelCase(name);
3233
3234             if (isDefined(value)) {
3235               element.style[name] = value;
3236             } else {
3237               return element.style[name];
3238             }
3239           },
3240
3241           attr: function(element, name, value) {
3242             var nodeType = element.nodeType;
3243             if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3244               return;
3245             }
3246             var lowercasedName = lowercase(name);
3247             if (BOOLEAN_ATTR[lowercasedName]) {
3248               if (isDefined(value)) {
3249                 if (!!value) {
3250                   element[name] = true;
3251                   element.setAttribute(name, lowercasedName);
3252                 } else {
3253                   element[name] = false;
3254                   element.removeAttribute(lowercasedName);
3255                 }
3256               } else {
3257                 return (element[name] ||
3258                          (element.attributes.getNamedItem(name) || noop).specified)
3259                        ? lowercasedName
3260                        : undefined;
3261               }
3262             } else if (isDefined(value)) {
3263               element.setAttribute(name, value);
3264             } else if (element.getAttribute) {
3265               // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3266               // some elements (e.g. Document) don't have get attribute, so return undefined
3267               var ret = element.getAttribute(name, 2);
3268               // normalize non-existing attributes to undefined (as jQuery)
3269               return ret === null ? undefined : ret;
3270             }
3271           },
3272
3273           prop: function(element, name, value) {
3274             if (isDefined(value)) {
3275               element[name] = value;
3276             } else {
3277               return element[name];
3278             }
3279           },
3280
3281           text: (function() {
3282             getText.$dv = '';
3283             return getText;
3284
3285             function getText(element, value) {
3286               if (isUndefined(value)) {
3287                 var nodeType = element.nodeType;
3288                 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3289               }
3290               element.textContent = value;
3291             }
3292           })(),
3293
3294           val: function(element, value) {
3295             if (isUndefined(value)) {
3296               if (element.multiple && nodeName_(element) === 'select') {
3297                 var result = [];
3298                 forEach(element.options, function(option) {
3299                   if (option.selected) {
3300                     result.push(option.value || option.text);
3301                   }
3302                 });
3303                 return result.length === 0 ? null : result;
3304               }
3305               return element.value;
3306             }
3307             element.value = value;
3308           },
3309
3310           html: function(element, value) {
3311             if (isUndefined(value)) {
3312               return element.innerHTML;
3313             }
3314             jqLiteDealoc(element, true);
3315             element.innerHTML = value;
3316           },
3317
3318           empty: jqLiteEmpty
3319         }, function(fn, name) {
3320           /**
3321            * Properties: writes return selection, reads return first value
3322            */
3323           JQLite.prototype[name] = function(arg1, arg2) {
3324             var i, key;
3325             var nodeCount = this.length;
3326
3327             // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3328             // in a way that survives minification.
3329             // jqLiteEmpty takes no arguments but is a setter.
3330             if (fn !== jqLiteEmpty &&
3331                 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3332               if (isObject(arg1)) {
3333
3334                 // we are a write, but the object properties are the key/values
3335                 for (i = 0; i < nodeCount; i++) {
3336                   if (fn === jqLiteData) {
3337                     // data() takes the whole object in jQuery
3338                     fn(this[i], arg1);
3339                   } else {
3340                     for (key in arg1) {
3341                       fn(this[i], key, arg1[key]);
3342                     }
3343                   }
3344                 }
3345                 // return self for chaining
3346                 return this;
3347               } else {
3348                 // we are a read, so read the first child.
3349                 // TODO: do we still need this?
3350                 var value = fn.$dv;
3351                 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3352                 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3353                 for (var j = 0; j < jj; j++) {
3354                   var nodeValue = fn(this[j], arg1, arg2);
3355                   value = value ? value + nodeValue : nodeValue;
3356                 }
3357                 return value;
3358               }
3359             } else {
3360               // we are a write, so apply to all children
3361               for (i = 0; i < nodeCount; i++) {
3362                 fn(this[i], arg1, arg2);
3363               }
3364               // return self for chaining
3365               return this;
3366             }
3367           };
3368         });
3369
3370         function createEventHandler(element, events) {
3371           var eventHandler = function(event, type) {
3372             // jQuery specific api
3373             event.isDefaultPrevented = function() {
3374               return event.defaultPrevented;
3375             };
3376
3377             var eventFns = events[type || event.type];
3378             var eventFnsLength = eventFns ? eventFns.length : 0;
3379
3380             if (!eventFnsLength) return;
3381
3382             if (isUndefined(event.immediatePropagationStopped)) {
3383               var originalStopImmediatePropagation = event.stopImmediatePropagation;
3384               event.stopImmediatePropagation = function() {
3385                 event.immediatePropagationStopped = true;
3386
3387                 if (event.stopPropagation) {
3388                   event.stopPropagation();
3389                 }
3390
3391                 if (originalStopImmediatePropagation) {
3392                   originalStopImmediatePropagation.call(event);
3393                 }
3394               };
3395             }
3396
3397             event.isImmediatePropagationStopped = function() {
3398               return event.immediatePropagationStopped === true;
3399             };
3400
3401             // Some events have special handlers that wrap the real handler
3402             var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3403
3404             // Copy event handlers in case event handlers array is modified during execution.
3405             if ((eventFnsLength > 1)) {
3406               eventFns = shallowCopy(eventFns);
3407             }
3408
3409             for (var i = 0; i < eventFnsLength; i++) {
3410               if (!event.isImmediatePropagationStopped()) {
3411                 handlerWrapper(element, event, eventFns[i]);
3412               }
3413             }
3414           };
3415
3416           // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3417           //       events on `element`
3418           eventHandler.elem = element;
3419           return eventHandler;
3420         }
3421
3422         function defaultHandlerWrapper(element, event, handler) {
3423           handler.call(element, event);
3424         }
3425
3426         function specialMouseHandlerWrapper(target, event, handler) {
3427           // Refer to jQuery's implementation of mouseenter & mouseleave
3428           // Read about mouseenter and mouseleave:
3429           // http://www.quirksmode.org/js/events_mouse.html#link8
3430           var related = event.relatedTarget;
3431           // For mousenter/leave call the handler if related is outside the target.
3432           // NB: No relatedTarget if the mouse left/entered the browser window
3433           if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3434             handler.call(target, event);
3435           }
3436         }
3437
3438         //////////////////////////////////////////
3439         // Functions iterating traversal.
3440         // These functions chain results into a single
3441         // selector.
3442         //////////////////////////////////////////
3443         forEach({
3444           removeData: jqLiteRemoveData,
3445
3446           on: function jqLiteOn(element, type, fn, unsupported) {
3447             if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3448
3449             // Do not add event handlers to non-elements because they will not be cleaned up.
3450             if (!jqLiteAcceptsData(element)) {
3451               return;
3452             }
3453
3454             var expandoStore = jqLiteExpandoStore(element, true);
3455             var events = expandoStore.events;
3456             var handle = expandoStore.handle;
3457
3458             if (!handle) {
3459               handle = expandoStore.handle = createEventHandler(element, events);
3460             }
3461
3462             // http://jsperf.com/string-indexof-vs-split
3463             var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3464             var i = types.length;
3465
3466             var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3467               var eventFns = events[type];
3468
3469               if (!eventFns) {
3470                 eventFns = events[type] = [];
3471                 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3472                 if (type !== '$destroy' && !noEventListener) {
3473                   addEventListenerFn(element, type, handle);
3474                 }
3475               }
3476
3477               eventFns.push(fn);
3478             };
3479
3480             while (i--) {
3481               type = types[i];
3482               if (MOUSE_EVENT_MAP[type]) {
3483                 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3484                 addHandler(type, undefined, true);
3485               } else {
3486                 addHandler(type);
3487               }
3488             }
3489           },
3490
3491           off: jqLiteOff,
3492
3493           one: function(element, type, fn) {
3494             element = jqLite(element);
3495
3496             //add the listener twice so that when it is called
3497             //you can remove the original function and still be
3498             //able to call element.off(ev, fn) normally
3499             element.on(type, function onFn() {
3500               element.off(type, fn);
3501               element.off(type, onFn);
3502             });
3503             element.on(type, fn);
3504           },
3505
3506           replaceWith: function(element, replaceNode) {
3507             var index, parent = element.parentNode;
3508             jqLiteDealoc(element);
3509             forEach(new JQLite(replaceNode), function(node) {
3510               if (index) {
3511                 parent.insertBefore(node, index.nextSibling);
3512               } else {
3513                 parent.replaceChild(node, element);
3514               }
3515               index = node;
3516             });
3517           },
3518
3519           children: function(element) {
3520             var children = [];
3521             forEach(element.childNodes, function(element) {
3522               if (element.nodeType === NODE_TYPE_ELEMENT) {
3523                 children.push(element);
3524               }
3525             });
3526             return children;
3527           },
3528
3529           contents: function(element) {
3530             return element.contentDocument || element.childNodes || [];
3531           },
3532
3533           append: function(element, node) {
3534             var nodeType = element.nodeType;
3535             if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3536
3537             node = new JQLite(node);
3538
3539             for (var i = 0, ii = node.length; i < ii; i++) {
3540               var child = node[i];
3541               element.appendChild(child);
3542             }
3543           },
3544
3545           prepend: function(element, node) {
3546             if (element.nodeType === NODE_TYPE_ELEMENT) {
3547               var index = element.firstChild;
3548               forEach(new JQLite(node), function(child) {
3549                 element.insertBefore(child, index);
3550               });
3551             }
3552           },
3553
3554           wrap: function(element, wrapNode) {
3555             wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3556             var parent = element.parentNode;
3557             if (parent) {
3558               parent.replaceChild(wrapNode, element);
3559             }
3560             wrapNode.appendChild(element);
3561           },
3562
3563           remove: jqLiteRemove,
3564
3565           detach: function(element) {
3566             jqLiteRemove(element, true);
3567           },
3568
3569           after: function(element, newElement) {
3570             var index = element, parent = element.parentNode;
3571             newElement = new JQLite(newElement);
3572
3573             for (var i = 0, ii = newElement.length; i < ii; i++) {
3574               var node = newElement[i];
3575               parent.insertBefore(node, index.nextSibling);
3576               index = node;
3577             }
3578           },
3579
3580           addClass: jqLiteAddClass,
3581           removeClass: jqLiteRemoveClass,
3582
3583           toggleClass: function(element, selector, condition) {
3584             if (selector) {
3585               forEach(selector.split(' '), function(className) {
3586                 var classCondition = condition;
3587                 if (isUndefined(classCondition)) {
3588                   classCondition = !jqLiteHasClass(element, className);
3589                 }
3590                 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3591               });
3592             }
3593           },
3594
3595           parent: function(element) {
3596             var parent = element.parentNode;
3597             return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3598           },
3599
3600           next: function(element) {
3601             return element.nextElementSibling;
3602           },
3603
3604           find: function(element, selector) {
3605             if (element.getElementsByTagName) {
3606               return element.getElementsByTagName(selector);
3607             } else {
3608               return [];
3609             }
3610           },
3611
3612           clone: jqLiteClone,
3613
3614           triggerHandler: function(element, event, extraParameters) {
3615
3616             var dummyEvent, eventFnsCopy, handlerArgs;
3617             var eventName = event.type || event;
3618             var expandoStore = jqLiteExpandoStore(element);
3619             var events = expandoStore && expandoStore.events;
3620             var eventFns = events && events[eventName];
3621
3622             if (eventFns) {
3623               // Create a dummy event to pass to the handlers
3624               dummyEvent = {
3625                 preventDefault: function() { this.defaultPrevented = true; },
3626                 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3627                 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3628                 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3629                 stopPropagation: noop,
3630                 type: eventName,
3631                 target: element
3632               };
3633
3634               // If a custom event was provided then extend our dummy event with it
3635               if (event.type) {
3636                 dummyEvent = extend(dummyEvent, event);
3637               }
3638
3639               // Copy event handlers in case event handlers array is modified during execution.
3640               eventFnsCopy = shallowCopy(eventFns);
3641               handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3642
3643               forEach(eventFnsCopy, function(fn) {
3644                 if (!dummyEvent.isImmediatePropagationStopped()) {
3645                   fn.apply(element, handlerArgs);
3646                 }
3647               });
3648             }
3649           }
3650         }, function(fn, name) {
3651           /**
3652            * chaining functions
3653            */
3654           JQLite.prototype[name] = function(arg1, arg2, arg3) {
3655             var value;
3656
3657             for (var i = 0, ii = this.length; i < ii; i++) {
3658               if (isUndefined(value)) {
3659                 value = fn(this[i], arg1, arg2, arg3);
3660                 if (isDefined(value)) {
3661                   // any function which returns a value needs to be wrapped
3662                   value = jqLite(value);
3663                 }
3664               } else {
3665                 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3666               }
3667             }
3668             return isDefined(value) ? value : this;
3669           };
3670
3671           // bind legacy bind/unbind to on/off
3672           JQLite.prototype.bind = JQLite.prototype.on;
3673           JQLite.prototype.unbind = JQLite.prototype.off;
3674         });
3675
3676
3677         // Provider for private $$jqLite service
3678         function $$jqLiteProvider() {
3679           this.$get = function $$jqLite() {
3680             return extend(JQLite, {
3681               hasClass: function(node, classes) {
3682                 if (node.attr) node = node[0];
3683                 return jqLiteHasClass(node, classes);
3684               },
3685               addClass: function(node, classes) {
3686                 if (node.attr) node = node[0];
3687                 return jqLiteAddClass(node, classes);
3688               },
3689               removeClass: function(node, classes) {
3690                 if (node.attr) node = node[0];
3691                 return jqLiteRemoveClass(node, classes);
3692               }
3693             });
3694           };
3695         }
3696
3697         /**
3698          * Computes a hash of an 'obj'.
3699          * Hash of a:
3700          *  string is string
3701          *  number is number as string
3702          *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3703          *         that is also assigned to the $$hashKey property of the object.
3704          *
3705          * @param obj
3706          * @returns {string} hash string such that the same input will have the same hash string.
3707          *         The resulting string key is in 'type:hashKey' format.
3708          */
3709         function hashKey(obj, nextUidFn) {
3710           var key = obj && obj.$$hashKey;
3711
3712           if (key) {
3713             if (typeof key === 'function') {
3714               key = obj.$$hashKey();
3715             }
3716             return key;
3717           }
3718
3719           var objType = typeof obj;
3720           if (objType == 'function' || (objType == 'object' && obj !== null)) {
3721             key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3722           } else {
3723             key = objType + ':' + obj;
3724           }
3725
3726           return key;
3727         }
3728
3729         /**
3730          * HashMap which can use objects as keys
3731          */
3732         function HashMap(array, isolatedUid) {
3733           if (isolatedUid) {
3734             var uid = 0;
3735             this.nextUid = function() {
3736               return ++uid;
3737             };
3738           }
3739           forEach(array, this.put, this);
3740         }
3741         HashMap.prototype = {
3742           /**
3743            * Store key value pair
3744            * @param key key to store can be any type
3745            * @param value value to store can be any type
3746            */
3747           put: function(key, value) {
3748             this[hashKey(key, this.nextUid)] = value;
3749           },
3750
3751           /**
3752            * @param key
3753            * @returns {Object} the value for the key
3754            */
3755           get: function(key) {
3756             return this[hashKey(key, this.nextUid)];
3757           },
3758
3759           /**
3760            * Remove the key/value pair
3761            * @param key
3762            */
3763           remove: function(key) {
3764             var value = this[key = hashKey(key, this.nextUid)];
3765             delete this[key];
3766             return value;
3767           }
3768         };
3769
3770         var $$HashMapProvider = [function() {
3771           this.$get = [function() {
3772             return HashMap;
3773           }];
3774         }];
3775
3776         /**
3777          * @ngdoc function
3778          * @module ng
3779          * @name angular.injector
3780          * @kind function
3781          *
3782          * @description
3783          * Creates an injector object that can be used for retrieving services as well as for
3784          * dependency injection (see {@link guide/di dependency injection}).
3785          *
3786          * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3787          *     {@link angular.module}. The `ng` module must be explicitly added.
3788          * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3789          *     disallows argument name annotation inference.
3790          * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3791          *
3792          * @example
3793          * Typical usage
3794          * ```js
3795          *   // create an injector
3796          *   var $injector = angular.injector(['ng']);
3797          *
3798          *   // use the injector to kick off your application
3799          *   // use the type inference to auto inject arguments, or use implicit injection
3800          *   $injector.invoke(function($rootScope, $compile, $document) {
3801          *     $compile($document)($rootScope);
3802          *     $rootScope.$digest();
3803          *   });
3804          * ```
3805          *
3806          * Sometimes you want to get access to the injector of a currently running Angular app
3807          * from outside Angular. Perhaps, you want to inject and compile some markup after the
3808          * application has been bootstrapped. You can do this using the extra `injector()` added
3809          * to JQuery/jqLite elements. See {@link angular.element}.
3810          *
3811          * *This is fairly rare but could be the case if a third party library is injecting the
3812          * markup.*
3813          *
3814          * In the following example a new block of HTML containing a `ng-controller`
3815          * directive is added to the end of the document body by JQuery. We then compile and link
3816          * it into the current AngularJS scope.
3817          *
3818          * ```js
3819          * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3820          * $(document.body).append($div);
3821          *
3822          * angular.element(document).injector().invoke(function($compile) {
3823          *   var scope = angular.element($div).scope();
3824          *   $compile($div)(scope);
3825          * });
3826          * ```
3827          */
3828
3829
3830         /**
3831          * @ngdoc module
3832          * @name auto
3833          * @description
3834          *
3835          * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3836          */
3837
3838         var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3839         var FN_ARG_SPLIT = /,/;
3840         var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3841         var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3842         var $injectorMinErr = minErr('$injector');
3843
3844         function anonFn(fn) {
3845           // For anonymous functions, showing at the very least the function signature can help in
3846           // debugging.
3847           var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3848               args = fnText.match(FN_ARGS);
3849           if (args) {
3850             return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3851           }
3852           return 'fn';
3853         }
3854
3855         function annotate(fn, strictDi, name) {
3856           var $inject,
3857               fnText,
3858               argDecl,
3859               last;
3860
3861           if (typeof fn === 'function') {
3862             if (!($inject = fn.$inject)) {
3863               $inject = [];
3864               if (fn.length) {
3865                 if (strictDi) {
3866                   if (!isString(name) || !name) {
3867                     name = fn.name || anonFn(fn);
3868                   }
3869                   throw $injectorMinErr('strictdi',
3870                     '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3871                 }
3872                 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3873                 argDecl = fnText.match(FN_ARGS);
3874                 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3875                   arg.replace(FN_ARG, function(all, underscore, name) {
3876                     $inject.push(name);
3877                   });
3878                 });
3879               }
3880               fn.$inject = $inject;
3881             }
3882           } else if (isArray(fn)) {
3883             last = fn.length - 1;
3884             assertArgFn(fn[last], 'fn');
3885             $inject = fn.slice(0, last);
3886           } else {
3887             assertArgFn(fn, 'fn', true);
3888           }
3889           return $inject;
3890         }
3891
3892         ///////////////////////////////////////
3893
3894         /**
3895          * @ngdoc service
3896          * @name $injector
3897          *
3898          * @description
3899          *
3900          * `$injector` is used to retrieve object instances as defined by
3901          * {@link auto.$provide provider}, instantiate types, invoke methods,
3902          * and load modules.
3903          *
3904          * The following always holds true:
3905          *
3906          * ```js
3907          *   var $injector = angular.injector();
3908          *   expect($injector.get('$injector')).toBe($injector);
3909          *   expect($injector.invoke(function($injector) {
3910          *     return $injector;
3911          *   })).toBe($injector);
3912          * ```
3913          *
3914          * # Injection Function Annotation
3915          *
3916          * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3917          * following are all valid ways of annotating function with injection arguments and are equivalent.
3918          *
3919          * ```js
3920          *   // inferred (only works if code not minified/obfuscated)
3921          *   $injector.invoke(function(serviceA){});
3922          *
3923          *   // annotated
3924          *   function explicit(serviceA) {};
3925          *   explicit.$inject = ['serviceA'];
3926          *   $injector.invoke(explicit);
3927          *
3928          *   // inline
3929          *   $injector.invoke(['serviceA', function(serviceA){}]);
3930          * ```
3931          *
3932          * ## Inference
3933          *
3934          * In JavaScript calling `toString()` on a function returns the function definition. The definition
3935          * can then be parsed and the function arguments can be extracted. This method of discovering
3936          * annotations is disallowed when the injector is in strict mode.
3937          * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3938          * argument names.
3939          *
3940          * ## `$inject` Annotation
3941          * By adding an `$inject` property onto a function the injection parameters can be specified.
3942          *
3943          * ## Inline
3944          * As an array of injection names, where the last item in the array is the function to call.
3945          */
3946
3947         /**
3948          * @ngdoc method
3949          * @name $injector#get
3950          *
3951          * @description
3952          * Return an instance of the service.
3953          *
3954          * @param {string} name The name of the instance to retrieve.
3955          * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3956          * @return {*} The instance.
3957          */
3958
3959         /**
3960          * @ngdoc method
3961          * @name $injector#invoke
3962          *
3963          * @description
3964          * Invoke the method and supply the method arguments from the `$injector`.
3965          *
3966          * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3967          *   injected according to the {@link guide/di $inject Annotation} rules.
3968          * @param {Object=} self The `this` for the invoked method.
3969          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3970          *                         object first, before the `$injector` is consulted.
3971          * @returns {*} the value returned by the invoked `fn` function.
3972          */
3973
3974         /**
3975          * @ngdoc method
3976          * @name $injector#has
3977          *
3978          * @description
3979          * Allows the user to query if the particular service exists.
3980          *
3981          * @param {string} name Name of the service to query.
3982          * @returns {boolean} `true` if injector has given service.
3983          */
3984
3985         /**
3986          * @ngdoc method
3987          * @name $injector#instantiate
3988          * @description
3989          * Create a new instance of JS type. The method takes a constructor function, invokes the new
3990          * operator, and supplies all of the arguments to the constructor function as specified by the
3991          * constructor annotation.
3992          *
3993          * @param {Function} Type Annotated constructor function.
3994          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3995          * object first, before the `$injector` is consulted.
3996          * @returns {Object} new instance of `Type`.
3997          */
3998
3999         /**
4000          * @ngdoc method
4001          * @name $injector#annotate
4002          *
4003          * @description
4004          * Returns an array of service names which the function is requesting for injection. This API is
4005          * used by the injector to determine which services need to be injected into the function when the
4006          * function is invoked. There are three ways in which the function can be annotated with the needed
4007          * dependencies.
4008          *
4009          * # Argument names
4010          *
4011          * The simplest form is to extract the dependencies from the arguments of the function. This is done
4012          * by converting the function into a string using `toString()` method and extracting the argument
4013          * names.
4014          * ```js
4015          *   // Given
4016          *   function MyController($scope, $route) {
4017          *     // ...
4018          *   }
4019          *
4020          *   // Then
4021          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4022          * ```
4023          *
4024          * You can disallow this method by using strict injection mode.
4025          *
4026          * This method does not work with code minification / obfuscation. For this reason the following
4027          * annotation strategies are supported.
4028          *
4029          * # The `$inject` property
4030          *
4031          * If a function has an `$inject` property and its value is an array of strings, then the strings
4032          * represent names of services to be injected into the function.
4033          * ```js
4034          *   // Given
4035          *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4036          *     // ...
4037          *   }
4038          *   // Define function dependencies
4039          *   MyController['$inject'] = ['$scope', '$route'];
4040          *
4041          *   // Then
4042          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4043          * ```
4044          *
4045          * # The array notation
4046          *
4047          * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4048          * is very inconvenient. In these situations using the array notation to specify the dependencies in
4049          * a way that survives minification is a better choice:
4050          *
4051          * ```js
4052          *   // We wish to write this (not minification / obfuscation safe)
4053          *   injector.invoke(function($compile, $rootScope) {
4054          *     // ...
4055          *   });
4056          *
4057          *   // We are forced to write break inlining
4058          *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4059          *     // ...
4060          *   };
4061          *   tmpFn.$inject = ['$compile', '$rootScope'];
4062          *   injector.invoke(tmpFn);
4063          *
4064          *   // To better support inline function the inline annotation is supported
4065          *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4066          *     // ...
4067          *   }]);
4068          *
4069          *   // Therefore
4070          *   expect(injector.annotate(
4071          *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4072          *    ).toEqual(['$compile', '$rootScope']);
4073          * ```
4074          *
4075          * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4076          * be retrieved as described above.
4077          *
4078          * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4079          *
4080          * @returns {Array.<string>} The names of the services which the function requires.
4081          */
4082
4083
4084
4085
4086         /**
4087          * @ngdoc service
4088          * @name $provide
4089          *
4090          * @description
4091          *
4092          * The {@link auto.$provide $provide} service has a number of methods for registering components
4093          * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4094          * {@link angular.Module}.
4095          *
4096          * An Angular **service** is a singleton object created by a **service factory**.  These **service
4097          * factories** are functions which, in turn, are created by a **service provider**.
4098          * The **service providers** are constructor functions. When instantiated they must contain a
4099          * property called `$get`, which holds the **service factory** function.
4100          *
4101          * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4102          * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4103          * function to get the instance of the **service**.
4104          *
4105          * Often services have no configuration options and there is no need to add methods to the service
4106          * provider.  The provider will be no more than a constructor function with a `$get` property. For
4107          * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4108          * services without specifying a provider.
4109          *
4110          * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4111          *     {@link auto.$injector $injector}
4112          * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4113          *     providers and services.
4114          * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4115          *     services, not providers.
4116          * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4117          *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4118          *     given factory function.
4119          * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4120          *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4121          *      a new object using the given constructor function.
4122          *
4123          * See the individual methods for more information and examples.
4124          */
4125
4126         /**
4127          * @ngdoc method
4128          * @name $provide#provider
4129          * @description
4130          *
4131          * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4132          * are constructor functions, whose instances are responsible for "providing" a factory for a
4133          * service.
4134          *
4135          * Service provider names start with the name of the service they provide followed by `Provider`.
4136          * For example, the {@link ng.$log $log} service has a provider called
4137          * {@link ng.$logProvider $logProvider}.
4138          *
4139          * Service provider objects can have additional methods which allow configuration of the provider
4140          * and its service. Importantly, you can configure what kind of service is created by the `$get`
4141          * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4142          * method {@link ng.$logProvider#debugEnabled debugEnabled}
4143          * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4144          * console or not.
4145          *
4146          * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4147                                 'Provider'` key.
4148          * @param {(Object|function())} provider If the provider is:
4149          *
4150          *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4151          *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4152          *   - `Constructor`: a new instance of the provider will be created using
4153          *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4154          *
4155          * @returns {Object} registered provider instance
4156
4157          * @example
4158          *
4159          * The following example shows how to create a simple event tracking service and register it using
4160          * {@link auto.$provide#provider $provide.provider()}.
4161          *
4162          * ```js
4163          *  // Define the eventTracker provider
4164          *  function EventTrackerProvider() {
4165          *    var trackingUrl = '/track';
4166          *
4167          *    // A provider method for configuring where the tracked events should been saved
4168          *    this.setTrackingUrl = function(url) {
4169          *      trackingUrl = url;
4170          *    };
4171          *
4172          *    // The service factory function
4173          *    this.$get = ['$http', function($http) {
4174          *      var trackedEvents = {};
4175          *      return {
4176          *        // Call this to track an event
4177          *        event: function(event) {
4178          *          var count = trackedEvents[event] || 0;
4179          *          count += 1;
4180          *          trackedEvents[event] = count;
4181          *          return count;
4182          *        },
4183          *        // Call this to save the tracked events to the trackingUrl
4184          *        save: function() {
4185          *          $http.post(trackingUrl, trackedEvents);
4186          *        }
4187          *      };
4188          *    }];
4189          *  }
4190          *
4191          *  describe('eventTracker', function() {
4192          *    var postSpy;
4193          *
4194          *    beforeEach(module(function($provide) {
4195          *      // Register the eventTracker provider
4196          *      $provide.provider('eventTracker', EventTrackerProvider);
4197          *    }));
4198          *
4199          *    beforeEach(module(function(eventTrackerProvider) {
4200          *      // Configure eventTracker provider
4201          *      eventTrackerProvider.setTrackingUrl('/custom-track');
4202          *    }));
4203          *
4204          *    it('tracks events', inject(function(eventTracker) {
4205          *      expect(eventTracker.event('login')).toEqual(1);
4206          *      expect(eventTracker.event('login')).toEqual(2);
4207          *    }));
4208          *
4209          *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4210          *      postSpy = spyOn($http, 'post');
4211          *      eventTracker.event('login');
4212          *      eventTracker.save();
4213          *      expect(postSpy).toHaveBeenCalled();
4214          *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4215          *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4216          *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4217          *    }));
4218          *  });
4219          * ```
4220          */
4221
4222         /**
4223          * @ngdoc method
4224          * @name $provide#factory
4225          * @description
4226          *
4227          * Register a **service factory**, which will be called to return the service instance.
4228          * This is short for registering a service where its provider consists of only a `$get` property,
4229          * which is the given service factory function.
4230          * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4231          * configure your service in a provider.
4232          *
4233          * @param {string} name The name of the instance.
4234          * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4235          *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4236          * @returns {Object} registered provider instance
4237          *
4238          * @example
4239          * Here is an example of registering a service
4240          * ```js
4241          *   $provide.factory('ping', ['$http', function($http) {
4242          *     return function ping() {
4243          *       return $http.send('/ping');
4244          *     };
4245          *   }]);
4246          * ```
4247          * You would then inject and use this service like this:
4248          * ```js
4249          *   someModule.controller('Ctrl', ['ping', function(ping) {
4250          *     ping();
4251          *   }]);
4252          * ```
4253          */
4254
4255
4256         /**
4257          * @ngdoc method
4258          * @name $provide#service
4259          * @description
4260          *
4261          * Register a **service constructor**, which will be invoked with `new` to create the service
4262          * instance.
4263          * This is short for registering a service where its provider's `$get` property is the service
4264          * constructor function that will be used to instantiate the service instance.
4265          *
4266          * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4267          * as a type/class.
4268          *
4269          * @param {string} name The name of the instance.
4270          * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4271          *     that will be instantiated.
4272          * @returns {Object} registered provider instance
4273          *
4274          * @example
4275          * Here is an example of registering a service using
4276          * {@link auto.$provide#service $provide.service(class)}.
4277          * ```js
4278          *   var Ping = function($http) {
4279          *     this.$http = $http;
4280          *   };
4281          *
4282          *   Ping.$inject = ['$http'];
4283          *
4284          *   Ping.prototype.send = function() {
4285          *     return this.$http.get('/ping');
4286          *   };
4287          *   $provide.service('ping', Ping);
4288          * ```
4289          * You would then inject and use this service like this:
4290          * ```js
4291          *   someModule.controller('Ctrl', ['ping', function(ping) {
4292          *     ping.send();
4293          *   }]);
4294          * ```
4295          */
4296
4297
4298         /**
4299          * @ngdoc method
4300          * @name $provide#value
4301          * @description
4302          *
4303          * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4304          * number, an array, an object or a function.  This is short for registering a service where its
4305          * provider's `$get` property is a factory function that takes no arguments and returns the **value
4306          * service**.
4307          *
4308          * Value services are similar to constant services, except that they cannot be injected into a
4309          * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4310          * an Angular
4311          * {@link auto.$provide#decorator decorator}.
4312          *
4313          * @param {string} name The name of the instance.
4314          * @param {*} value The value.
4315          * @returns {Object} registered provider instance
4316          *
4317          * @example
4318          * Here are some examples of creating value services.
4319          * ```js
4320          *   $provide.value('ADMIN_USER', 'admin');
4321          *
4322          *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4323          *
4324          *   $provide.value('halfOf', function(value) {
4325          *     return value / 2;
4326          *   });
4327          * ```
4328          */
4329
4330
4331         /**
4332          * @ngdoc method
4333          * @name $provide#constant
4334          * @description
4335          *
4336          * Register a **constant service**, such as a string, a number, an array, an object or a function,
4337          * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4338          * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4339          * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4340          *
4341          * @param {string} name The name of the constant.
4342          * @param {*} value The constant value.
4343          * @returns {Object} registered instance
4344          *
4345          * @example
4346          * Here a some examples of creating constants:
4347          * ```js
4348          *   $provide.constant('SHARD_HEIGHT', 306);
4349          *
4350          *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4351          *
4352          *   $provide.constant('double', function(value) {
4353          *     return value * 2;
4354          *   });
4355          * ```
4356          */
4357
4358
4359         /**
4360          * @ngdoc method
4361          * @name $provide#decorator
4362          * @description
4363          *
4364          * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4365          * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4366          * service. The object returned by the decorator may be the original service, or a new service
4367          * object which replaces or wraps and delegates to the original service.
4368          *
4369          * @param {string} name The name of the service to decorate.
4370          * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4371          *    instantiated and should return the decorated service instance. The function is called using
4372          *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4373          *    Local injection arguments:
4374          *
4375          *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4376          *      decorated or delegated to.
4377          *
4378          * @example
4379          * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4380          * calls to {@link ng.$log#error $log.warn()}.
4381          * ```js
4382          *   $provide.decorator('$log', ['$delegate', function($delegate) {
4383          *     $delegate.warn = $delegate.error;
4384          *     return $delegate;
4385          *   }]);
4386          * ```
4387          */
4388
4389
4390         function createInjector(modulesToLoad, strictDi) {
4391           strictDi = (strictDi === true);
4392           var INSTANTIATING = {},
4393               providerSuffix = 'Provider',
4394               path = [],
4395               loadedModules = new HashMap([], true),
4396               providerCache = {
4397                 $provide: {
4398                     provider: supportObject(provider),
4399                     factory: supportObject(factory),
4400                     service: supportObject(service),
4401                     value: supportObject(value),
4402                     constant: supportObject(constant),
4403                     decorator: decorator
4404                   }
4405               },
4406               providerInjector = (providerCache.$injector =
4407                   createInternalInjector(providerCache, function(serviceName, caller) {
4408                     if (angular.isString(caller)) {
4409                       path.push(caller);
4410                     }
4411                     throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4412                   })),
4413               instanceCache = {},
4414               instanceInjector = (instanceCache.$injector =
4415                   createInternalInjector(instanceCache, function(serviceName, caller) {
4416                     var provider = providerInjector.get(serviceName + providerSuffix, caller);
4417                     return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4418                   }));
4419
4420
4421           forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4422
4423           return instanceInjector;
4424
4425           ////////////////////////////////////
4426           // $provider
4427           ////////////////////////////////////
4428
4429           function supportObject(delegate) {
4430             return function(key, value) {
4431               if (isObject(key)) {
4432                 forEach(key, reverseParams(delegate));
4433               } else {
4434                 return delegate(key, value);
4435               }
4436             };
4437           }
4438
4439           function provider(name, provider_) {
4440             assertNotHasOwnProperty(name, 'service');
4441             if (isFunction(provider_) || isArray(provider_)) {
4442               provider_ = providerInjector.instantiate(provider_);
4443             }
4444             if (!provider_.$get) {
4445               throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4446             }
4447             return providerCache[name + providerSuffix] = provider_;
4448           }
4449
4450           function enforceReturnValue(name, factory) {
4451             return function enforcedReturnValue() {
4452               var result = instanceInjector.invoke(factory, this);
4453               if (isUndefined(result)) {
4454                 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4455               }
4456               return result;
4457             };
4458           }
4459
4460           function factory(name, factoryFn, enforce) {
4461             return provider(name, {
4462               $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4463             });
4464           }
4465
4466           function service(name, constructor) {
4467             return factory(name, ['$injector', function($injector) {
4468               return $injector.instantiate(constructor);
4469             }]);
4470           }
4471
4472           function value(name, val) { return factory(name, valueFn(val), false); }
4473
4474           function constant(name, value) {
4475             assertNotHasOwnProperty(name, 'constant');
4476             providerCache[name] = value;
4477             instanceCache[name] = value;
4478           }
4479
4480           function decorator(serviceName, decorFn) {
4481             var origProvider = providerInjector.get(serviceName + providerSuffix),
4482                 orig$get = origProvider.$get;
4483
4484             origProvider.$get = function() {
4485               var origInstance = instanceInjector.invoke(orig$get, origProvider);
4486               return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4487             };
4488           }
4489
4490           ////////////////////////////////////
4491           // Module Loading
4492           ////////////////////////////////////
4493           function loadModules(modulesToLoad) {
4494             assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4495             var runBlocks = [], moduleFn;
4496             forEach(modulesToLoad, function(module) {
4497               if (loadedModules.get(module)) return;
4498               loadedModules.put(module, true);
4499
4500               function runInvokeQueue(queue) {
4501                 var i, ii;
4502                 for (i = 0, ii = queue.length; i < ii; i++) {
4503                   var invokeArgs = queue[i],
4504                       provider = providerInjector.get(invokeArgs[0]);
4505
4506                   provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4507                 }
4508               }
4509
4510               try {
4511                 if (isString(module)) {
4512                   moduleFn = angularModule(module);
4513                   runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4514                   runInvokeQueue(moduleFn._invokeQueue);
4515                   runInvokeQueue(moduleFn._configBlocks);
4516                 } else if (isFunction(module)) {
4517                     runBlocks.push(providerInjector.invoke(module));
4518                 } else if (isArray(module)) {
4519                     runBlocks.push(providerInjector.invoke(module));
4520                 } else {
4521                   assertArgFn(module, 'module');
4522                 }
4523               } catch (e) {
4524                 if (isArray(module)) {
4525                   module = module[module.length - 1];
4526                 }
4527                 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4528                   // Safari & FF's stack traces don't contain error.message content
4529                   // unlike those of Chrome and IE
4530                   // So if stack doesn't contain message, we create a new string that contains both.
4531                   // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4532                   /* jshint -W022 */
4533                   e = e.message + '\n' + e.stack;
4534                 }
4535                 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4536                           module, e.stack || e.message || e);
4537               }
4538             });
4539             return runBlocks;
4540           }
4541
4542           ////////////////////////////////////
4543           // internal Injector
4544           ////////////////////////////////////
4545
4546           function createInternalInjector(cache, factory) {
4547
4548             function getService(serviceName, caller) {
4549               if (cache.hasOwnProperty(serviceName)) {
4550                 if (cache[serviceName] === INSTANTIATING) {
4551                   throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4552                             serviceName + ' <- ' + path.join(' <- '));
4553                 }
4554                 return cache[serviceName];
4555               } else {
4556                 try {
4557                   path.unshift(serviceName);
4558                   cache[serviceName] = INSTANTIATING;
4559                   return cache[serviceName] = factory(serviceName, caller);
4560                 } catch (err) {
4561                   if (cache[serviceName] === INSTANTIATING) {
4562                     delete cache[serviceName];
4563                   }
4564                   throw err;
4565                 } finally {
4566                   path.shift();
4567                 }
4568               }
4569             }
4570
4571             function invoke(fn, self, locals, serviceName) {
4572               if (typeof locals === 'string') {
4573                 serviceName = locals;
4574                 locals = null;
4575               }
4576
4577               var args = [],
4578                   $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4579                   length, i,
4580                   key;
4581
4582               for (i = 0, length = $inject.length; i < length; i++) {
4583                 key = $inject[i];
4584                 if (typeof key !== 'string') {
4585                   throw $injectorMinErr('itkn',
4586                           'Incorrect injection token! Expected service name as string, got {0}', key);
4587                 }
4588                 args.push(
4589                   locals && locals.hasOwnProperty(key)
4590                   ? locals[key]
4591                   : getService(key, serviceName)
4592                 );
4593               }
4594               if (isArray(fn)) {
4595                 fn = fn[length];
4596               }
4597
4598               // http://jsperf.com/angularjs-invoke-apply-vs-switch
4599               // #5388
4600               return fn.apply(self, args);
4601             }
4602
4603             function instantiate(Type, locals, serviceName) {
4604               // Check if Type is annotated and use just the given function at n-1 as parameter
4605               // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4606               // Object creation: http://jsperf.com/create-constructor/2
4607               var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4608               var returnedValue = invoke(Type, instance, locals, serviceName);
4609
4610               return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4611             }
4612
4613             return {
4614               invoke: invoke,
4615               instantiate: instantiate,
4616               get: getService,
4617               annotate: createInjector.$$annotate,
4618               has: function(name) {
4619                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4620               }
4621             };
4622           }
4623         }
4624
4625         createInjector.$$annotate = annotate;
4626
4627         /**
4628          * @ngdoc provider
4629          * @name $anchorScrollProvider
4630          *
4631          * @description
4632          * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4633          * {@link ng.$location#hash $location.hash()} changes.
4634          */
4635         function $AnchorScrollProvider() {
4636
4637           var autoScrollingEnabled = true;
4638
4639           /**
4640            * @ngdoc method
4641            * @name $anchorScrollProvider#disableAutoScrolling
4642            *
4643            * @description
4644            * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4645            * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4646            * Use this method to disable automatic scrolling.
4647            *
4648            * If automatic scrolling is disabled, one must explicitly call
4649            * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4650            * current hash.
4651            */
4652           this.disableAutoScrolling = function() {
4653             autoScrollingEnabled = false;
4654           };
4655
4656           /**
4657            * @ngdoc service
4658            * @name $anchorScroll
4659            * @kind function
4660            * @requires $window
4661            * @requires $location
4662            * @requires $rootScope
4663            *
4664            * @description
4665            * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4666            * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4667            * in the
4668            * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4669            *
4670            * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4671            * match any anchor whenever it changes. This can be disabled by calling
4672            * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4673            *
4674            * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4675            * vertical scroll-offset (either fixed or dynamic).
4676            *
4677            * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4678            *                       {@link ng.$location#hash $location.hash()} will be used.
4679            *
4680            * @property {(number|function|jqLite)} yOffset
4681            * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4682            * positioned elements at the top of the page, such as navbars, headers etc.
4683            *
4684            * `yOffset` can be specified in various ways:
4685            * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4686            * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4687            *   a number representing the offset (in pixels).<br /><br />
4688            * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4689            *   the top of the page to the element's bottom will be used as offset.<br />
4690            *   **Note**: The element will be taken into account only as long as its `position` is set to
4691            *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4692            *   their height and/or positioning according to the viewport's size.
4693            *
4694            * <br />
4695            * <div class="alert alert-warning">
4696            * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4697            * not some child element.
4698            * </div>
4699            *
4700            * @example
4701              <example module="anchorScrollExample">
4702                <file name="index.html">
4703                  <div id="scrollArea" ng-controller="ScrollController">
4704                    <a ng-click="gotoBottom()">Go to bottom</a>
4705                    <a id="bottom"></a> You're at the bottom!
4706                  </div>
4707                </file>
4708                <file name="script.js">
4709                  angular.module('anchorScrollExample', [])
4710                    .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4711                      function ($scope, $location, $anchorScroll) {
4712                        $scope.gotoBottom = function() {
4713                          // set the location.hash to the id of
4714                          // the element you wish to scroll to.
4715                          $location.hash('bottom');
4716
4717                          // call $anchorScroll()
4718                          $anchorScroll();
4719                        };
4720                      }]);
4721                </file>
4722                <file name="style.css">
4723                  #scrollArea {
4724                    height: 280px;
4725                    overflow: auto;
4726                  }
4727
4728                  #bottom {
4729                    display: block;
4730                    margin-top: 2000px;
4731                  }
4732                </file>
4733              </example>
4734            *
4735            * <hr />
4736            * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4737            * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4738            *
4739            * @example
4740              <example module="anchorScrollOffsetExample">
4741                <file name="index.html">
4742                  <div class="fixed-header" ng-controller="headerCtrl">
4743                    <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4744                      Go to anchor {{x}}
4745                    </a>
4746                  </div>
4747                  <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4748                    Anchor {{x}} of 5
4749                  </div>
4750                </file>
4751                <file name="script.js">
4752                  angular.module('anchorScrollOffsetExample', [])
4753                    .run(['$anchorScroll', function($anchorScroll) {
4754                      $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4755                    }])
4756                    .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4757                      function ($anchorScroll, $location, $scope) {
4758                        $scope.gotoAnchor = function(x) {
4759                          var newHash = 'anchor' + x;
4760                          if ($location.hash() !== newHash) {
4761                            // set the $location.hash to `newHash` and
4762                            // $anchorScroll will automatically scroll to it
4763                            $location.hash('anchor' + x);
4764                          } else {
4765                            // call $anchorScroll() explicitly,
4766                            // since $location.hash hasn't changed
4767                            $anchorScroll();
4768                          }
4769                        };
4770                      }
4771                    ]);
4772                </file>
4773                <file name="style.css">
4774                  body {
4775                    padding-top: 50px;
4776                  }
4777
4778                  .anchor {
4779                    border: 2px dashed DarkOrchid;
4780                    padding: 10px 10px 200px 10px;
4781                  }
4782
4783                  .fixed-header {
4784                    background-color: rgba(0, 0, 0, 0.2);
4785                    height: 50px;
4786                    position: fixed;
4787                    top: 0; left: 0; right: 0;
4788                  }
4789
4790                  .fixed-header > a {
4791                    display: inline-block;
4792                    margin: 5px 15px;
4793                  }
4794                </file>
4795              </example>
4796            */
4797           this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4798             var document = $window.document;
4799
4800             // Helper function to get first anchor from a NodeList
4801             // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4802             //  and working in all supported browsers.)
4803             function getFirstAnchor(list) {
4804               var result = null;
4805               Array.prototype.some.call(list, function(element) {
4806                 if (nodeName_(element) === 'a') {
4807                   result = element;
4808                   return true;
4809                 }
4810               });
4811               return result;
4812             }
4813
4814             function getYOffset() {
4815
4816               var offset = scroll.yOffset;
4817
4818               if (isFunction(offset)) {
4819                 offset = offset();
4820               } else if (isElement(offset)) {
4821                 var elem = offset[0];
4822                 var style = $window.getComputedStyle(elem);
4823                 if (style.position !== 'fixed') {
4824                   offset = 0;
4825                 } else {
4826                   offset = elem.getBoundingClientRect().bottom;
4827                 }
4828               } else if (!isNumber(offset)) {
4829                 offset = 0;
4830               }
4831
4832               return offset;
4833             }
4834
4835             function scrollTo(elem) {
4836               if (elem) {
4837                 elem.scrollIntoView();
4838
4839                 var offset = getYOffset();
4840
4841                 if (offset) {
4842                   // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4843                   // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4844                   // top of the viewport.
4845                   //
4846                   // IF the number of pixels from the top of `elem` to the end of the page's content is less
4847                   // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4848                   // way down the page.
4849                   //
4850                   // This is often the case for elements near the bottom of the page.
4851                   //
4852                   // In such cases we do not need to scroll the whole `offset` up, just the difference between
4853                   // the top of the element and the offset, which is enough to align the top of `elem` at the
4854                   // desired position.
4855                   var elemTop = elem.getBoundingClientRect().top;
4856                   $window.scrollBy(0, elemTop - offset);
4857                 }
4858               } else {
4859                 $window.scrollTo(0, 0);
4860               }
4861             }
4862
4863             function scroll(hash) {
4864               hash = isString(hash) ? hash : $location.hash();
4865               var elm;
4866
4867               // empty hash, scroll to the top of the page
4868               if (!hash) scrollTo(null);
4869
4870               // element with given id
4871               else if ((elm = document.getElementById(hash))) scrollTo(elm);
4872
4873               // first anchor with given name :-D
4874               else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4875
4876               // no element and hash == 'top', scroll to the top of the page
4877               else if (hash === 'top') scrollTo(null);
4878             }
4879
4880             // does not scroll when user clicks on anchor link that is currently on
4881             // (no url change, no $location.hash() change), browser native does scroll
4882             if (autoScrollingEnabled) {
4883               $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4884                 function autoScrollWatchAction(newVal, oldVal) {
4885                   // skip the initial scroll if $location.hash is empty
4886                   if (newVal === oldVal && newVal === '') return;
4887
4888                   jqLiteDocumentLoaded(function() {
4889                     $rootScope.$evalAsync(scroll);
4890                   });
4891                 });
4892             }
4893
4894             return scroll;
4895           }];
4896         }
4897
4898         var $animateMinErr = minErr('$animate');
4899         var ELEMENT_NODE = 1;
4900         var NG_ANIMATE_CLASSNAME = 'ng-animate';
4901
4902         function mergeClasses(a,b) {
4903           if (!a && !b) return '';
4904           if (!a) return b;
4905           if (!b) return a;
4906           if (isArray(a)) a = a.join(' ');
4907           if (isArray(b)) b = b.join(' ');
4908           return a + ' ' + b;
4909         }
4910
4911         function extractElementNode(element) {
4912           for (var i = 0; i < element.length; i++) {
4913             var elm = element[i];
4914             if (elm.nodeType === ELEMENT_NODE) {
4915               return elm;
4916             }
4917           }
4918         }
4919
4920         function splitClasses(classes) {
4921           if (isString(classes)) {
4922             classes = classes.split(' ');
4923           }
4924
4925           // Use createMap() to prevent class assumptions involving property names in
4926           // Object.prototype
4927           var obj = createMap();
4928           forEach(classes, function(klass) {
4929             // sometimes the split leaves empty string values
4930             // incase extra spaces were applied to the options
4931             if (klass.length) {
4932               obj[klass] = true;
4933             }
4934           });
4935           return obj;
4936         }
4937
4938         // if any other type of options value besides an Object value is
4939         // passed into the $animate.method() animation then this helper code
4940         // will be run which will ignore it. While this patch is not the
4941         // greatest solution to this, a lot of existing plugins depend on
4942         // $animate to either call the callback (< 1.2) or return a promise
4943         // that can be changed. This helper function ensures that the options
4944         // are wiped clean incase a callback function is provided.
4945         function prepareAnimateOptions(options) {
4946           return isObject(options)
4947               ? options
4948               : {};
4949         }
4950
4951         var $$CoreAnimateRunnerProvider = function() {
4952           this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4953             function AnimateRunner() {}
4954             AnimateRunner.all = noop;
4955             AnimateRunner.chain = noop;
4956             AnimateRunner.prototype = {
4957               end: noop,
4958               cancel: noop,
4959               resume: noop,
4960               pause: noop,
4961               complete: noop,
4962               then: function(pass, fail) {
4963                 return $q(function(resolve) {
4964                   $$rAF(function() {
4965                     resolve();
4966                   });
4967                 }).then(pass, fail);
4968               }
4969             };
4970             return AnimateRunner;
4971           }];
4972         };
4973
4974         // this is prefixed with Core since it conflicts with
4975         // the animateQueueProvider defined in ngAnimate/animateQueue.js
4976         var $$CoreAnimateQueueProvider = function() {
4977           var postDigestQueue = new HashMap();
4978           var postDigestElements = [];
4979
4980           this.$get = ['$$AnimateRunner', '$rootScope',
4981                function($$AnimateRunner,   $rootScope) {
4982             return {
4983               enabled: noop,
4984               on: noop,
4985               off: noop,
4986               pin: noop,
4987
4988               push: function(element, event, options, domOperation) {
4989                 domOperation        && domOperation();
4990
4991                 options = options || {};
4992                 options.from        && element.css(options.from);
4993                 options.to          && element.css(options.to);
4994
4995                 if (options.addClass || options.removeClass) {
4996                   addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4997                 }
4998
4999                 return new $$AnimateRunner(); // jshint ignore:line
5000               }
5001             };
5002
5003
5004             function updateData(data, classes, value) {
5005               var changed = false;
5006               if (classes) {
5007                 classes = isString(classes) ? classes.split(' ') :
5008                           isArray(classes) ? classes : [];
5009                 forEach(classes, function(className) {
5010                   if (className) {
5011                     changed = true;
5012                     data[className] = value;
5013                   }
5014                 });
5015               }
5016               return changed;
5017             }
5018
5019             function handleCSSClassChanges() {
5020               forEach(postDigestElements, function(element) {
5021                 var data = postDigestQueue.get(element);
5022                 if (data) {
5023                   var existing = splitClasses(element.attr('class'));
5024                   var toAdd = '';
5025                   var toRemove = '';
5026                   forEach(data, function(status, className) {
5027                     var hasClass = !!existing[className];
5028                     if (status !== hasClass) {
5029                       if (status) {
5030                         toAdd += (toAdd.length ? ' ' : '') + className;
5031                       } else {
5032                         toRemove += (toRemove.length ? ' ' : '') + className;
5033                       }
5034                     }
5035                   });
5036
5037                   forEach(element, function(elm) {
5038                     toAdd    && jqLiteAddClass(elm, toAdd);
5039                     toRemove && jqLiteRemoveClass(elm, toRemove);
5040                   });
5041                   postDigestQueue.remove(element);
5042                 }
5043               });
5044               postDigestElements.length = 0;
5045             }
5046
5047
5048             function addRemoveClassesPostDigest(element, add, remove) {
5049               var data = postDigestQueue.get(element) || {};
5050
5051               var classesAdded = updateData(data, add, true);
5052               var classesRemoved = updateData(data, remove, false);
5053
5054               if (classesAdded || classesRemoved) {
5055
5056                 postDigestQueue.put(element, data);
5057                 postDigestElements.push(element);
5058
5059                 if (postDigestElements.length === 1) {
5060                   $rootScope.$$postDigest(handleCSSClassChanges);
5061                 }
5062               }
5063             }
5064           }];
5065         };
5066
5067         /**
5068          * @ngdoc provider
5069          * @name $animateProvider
5070          *
5071          * @description
5072          * Default implementation of $animate that doesn't perform any animations, instead just
5073          * synchronously performs DOM updates and resolves the returned runner promise.
5074          *
5075          * In order to enable animations the `ngAnimate` module has to be loaded.
5076          *
5077          * To see the functional implementation check out `src/ngAnimate/animate.js`.
5078          */
5079         var $AnimateProvider = ['$provide', function($provide) {
5080           var provider = this;
5081
5082           this.$$registeredAnimations = Object.create(null);
5083
5084            /**
5085            * @ngdoc method
5086            * @name $animateProvider#register
5087            *
5088            * @description
5089            * Registers a new injectable animation factory function. The factory function produces the
5090            * animation object which contains callback functions for each event that is expected to be
5091            * animated.
5092            *
5093            *   * `eventFn`: `function(element, ... , doneFunction, options)`
5094            *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5095            *   on the type of animation additional arguments will be injected into the animation function. The
5096            *   list below explains the function signatures for the different animation methods:
5097            *
5098            *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5099            *   - addClass: function(element, addedClasses, doneFunction, options)
5100            *   - removeClass: function(element, removedClasses, doneFunction, options)
5101            *   - enter, leave, move: function(element, doneFunction, options)
5102            *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5103            *
5104            *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5105            *
5106            * ```js
5107            *   return {
5108            *     //enter, leave, move signature
5109            *     eventFn : function(element, done, options) {
5110            *       //code to run the animation
5111            *       //once complete, then run done()
5112            *       return function endFunction(wasCancelled) {
5113            *         //code to cancel the animation
5114            *       }
5115            *     }
5116            *   }
5117            * ```
5118            *
5119            * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5120            * @param {Function} factory The factory function that will be executed to return the animation
5121            *                           object.
5122            */
5123           this.register = function(name, factory) {
5124             if (name && name.charAt(0) !== '.') {
5125               throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5126             }
5127
5128             var key = name + '-animation';
5129             provider.$$registeredAnimations[name.substr(1)] = key;
5130             $provide.factory(key, factory);
5131           };
5132
5133           /**
5134            * @ngdoc method
5135            * @name $animateProvider#classNameFilter
5136            *
5137            * @description
5138            * Sets and/or returns the CSS class regular expression that is checked when performing
5139            * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5140            * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5141            * When setting the `classNameFilter` value, animations will only be performed on elements
5142            * that successfully match the filter expression. This in turn can boost performance
5143            * for low-powered devices as well as applications containing a lot of structural operations.
5144            * @param {RegExp=} expression The className expression which will be checked against all animations
5145            * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5146            */
5147           this.classNameFilter = function(expression) {
5148             if (arguments.length === 1) {
5149               this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5150               if (this.$$classNameFilter) {
5151                 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5152                 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5153                   throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5154
5155                 }
5156               }
5157             }
5158             return this.$$classNameFilter;
5159           };
5160
5161           this.$get = ['$$animateQueue', function($$animateQueue) {
5162             function domInsert(element, parentElement, afterElement) {
5163               // if for some reason the previous element was removed
5164               // from the dom sometime before this code runs then let's
5165               // just stick to using the parent element as the anchor
5166               if (afterElement) {
5167                 var afterNode = extractElementNode(afterElement);
5168                 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5169                   afterElement = null;
5170                 }
5171               }
5172               afterElement ? afterElement.after(element) : parentElement.prepend(element);
5173             }
5174
5175             /**
5176              * @ngdoc service
5177              * @name $animate
5178              * @description The $animate service exposes a series of DOM utility methods that provide support
5179              * for animation hooks. The default behavior is the application of DOM operations, however,
5180              * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5181              * to ensure that animation runs with the triggered DOM operation.
5182              *
5183              * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5184              * included and only when it is active then the animation hooks that `$animate` triggers will be
5185              * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5186              * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5187              * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5188              *
5189              * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5190              *
5191              * To learn more about enabling animation support, click here to visit the
5192              * {@link ngAnimate ngAnimate module page}.
5193              */
5194             return {
5195               // we don't call it directly since non-existant arguments may
5196               // be interpreted as null within the sub enabled function
5197
5198               /**
5199                *
5200                * @ngdoc method
5201                * @name $animate#on
5202                * @kind function
5203                * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5204                *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5205                *    is fired with the following params:
5206                *
5207                * ```js
5208                * $animate.on('enter', container,
5209                *    function callback(element, phase) {
5210                *      // cool we detected an enter animation within the container
5211                *    }
5212                * );
5213                * ```
5214                *
5215                * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5216                * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5217                *     as well as among its children
5218                * @param {Function} callback the callback function that will be fired when the listener is triggered
5219                *
5220                * The arguments present in the callback function are:
5221                * * `element` - The captured DOM element that the animation was fired on.
5222                * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5223                */
5224               on: $$animateQueue.on,
5225
5226               /**
5227                *
5228                * @ngdoc method
5229                * @name $animate#off
5230                * @kind function
5231                * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5232                * can be used in three different ways depending on the arguments:
5233                *
5234                * ```js
5235                * // remove all the animation event listeners listening for `enter`
5236                * $animate.off('enter');
5237                *
5238                * // remove all the animation event listeners listening for `enter` on the given element and its children
5239                * $animate.off('enter', container);
5240                *
5241                * // remove the event listener function provided by `listenerFn` that is set
5242                * // to listen for `enter` on the given `element` as well as its children
5243                * $animate.off('enter', container, callback);
5244                * ```
5245                *
5246                * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5247                * @param {DOMElement=} container the container element the event listener was placed on
5248                * @param {Function=} callback the callback function that was registered as the listener
5249                */
5250               off: $$animateQueue.off,
5251
5252               /**
5253                * @ngdoc method
5254                * @name $animate#pin
5255                * @kind function
5256                * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5257                *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5258                *    element despite being outside the realm of the application or within another application. Say for example if the application
5259                *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5260                *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5261                *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5262                *
5263                *    Note that this feature is only active when the `ngAnimate` module is used.
5264                *
5265                * @param {DOMElement} element the external element that will be pinned
5266                * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5267                */
5268               pin: $$animateQueue.pin,
5269
5270               /**
5271                *
5272                * @ngdoc method
5273                * @name $animate#enabled
5274                * @kind function
5275                * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5276                * function can be called in four ways:
5277                *
5278                * ```js
5279                * // returns true or false
5280                * $animate.enabled();
5281                *
5282                * // changes the enabled state for all animations
5283                * $animate.enabled(false);
5284                * $animate.enabled(true);
5285                *
5286                * // returns true or false if animations are enabled for an element
5287                * $animate.enabled(element);
5288                *
5289                * // changes the enabled state for an element and its children
5290                * $animate.enabled(element, true);
5291                * $animate.enabled(element, false);
5292                * ```
5293                *
5294                * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5295                * @param {boolean=} enabled whether or not the animations will be enabled for the element
5296                *
5297                * @return {boolean} whether or not animations are enabled
5298                */
5299               enabled: $$animateQueue.enabled,
5300
5301               /**
5302                * @ngdoc method
5303                * @name $animate#cancel
5304                * @kind function
5305                * @description Cancels the provided animation.
5306                *
5307                * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5308                */
5309               cancel: function(runner) {
5310                 runner.end && runner.end();
5311               },
5312
5313               /**
5314                *
5315                * @ngdoc method
5316                * @name $animate#enter
5317                * @kind function
5318                * @description Inserts the element into the DOM either after the `after` element (if provided) or
5319                *   as the first child within the `parent` element and then triggers an animation.
5320                *   A promise is returned that will be resolved during the next digest once the animation
5321                *   has completed.
5322                *
5323                * @param {DOMElement} element the element which will be inserted into the DOM
5324                * @param {DOMElement} parent the parent element which will append the element as
5325                *   a child (so long as the after element is not present)
5326                * @param {DOMElement=} after the sibling element after which the element will be appended
5327                * @param {object=} options an optional collection of options/styles that will be applied to the element
5328                *
5329                * @return {Promise} the animation callback promise
5330                */
5331               enter: function(element, parent, after, options) {
5332                 parent = parent && jqLite(parent);
5333                 after = after && jqLite(after);
5334                 parent = parent || after.parent();
5335                 domInsert(element, parent, after);
5336                 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5337               },
5338
5339               /**
5340                *
5341                * @ngdoc method
5342                * @name $animate#move
5343                * @kind function
5344                * @description Inserts (moves) the element into its new position in the DOM either after
5345                *   the `after` element (if provided) or as the first child within the `parent` element
5346                *   and then triggers an animation. A promise is returned that will be resolved
5347                *   during the next digest once the animation has completed.
5348                *
5349                * @param {DOMElement} element the element which will be moved into the new DOM position
5350                * @param {DOMElement} parent the parent element which will append the element as
5351                *   a child (so long as the after element is not present)
5352                * @param {DOMElement=} after the sibling element after which the element will be appended
5353                * @param {object=} options an optional collection of options/styles that will be applied to the element
5354                *
5355                * @return {Promise} the animation callback promise
5356                */
5357               move: function(element, parent, after, options) {
5358                 parent = parent && jqLite(parent);
5359                 after = after && jqLite(after);
5360                 parent = parent || after.parent();
5361                 domInsert(element, parent, after);
5362                 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5363               },
5364
5365               /**
5366                * @ngdoc method
5367                * @name $animate#leave
5368                * @kind function
5369                * @description Triggers an animation and then removes the element from the DOM.
5370                * When the function is called a promise is returned that will be resolved during the next
5371                * digest once the animation has completed.
5372                *
5373                * @param {DOMElement} element the element which will be removed from the DOM
5374                * @param {object=} options an optional collection of options/styles that will be applied to the element
5375                *
5376                * @return {Promise} the animation callback promise
5377                */
5378               leave: function(element, options) {
5379                 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5380                   element.remove();
5381                 });
5382               },
5383
5384               /**
5385                * @ngdoc method
5386                * @name $animate#addClass
5387                * @kind function
5388                *
5389                * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5390                *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5391                *   animation if element already contains the CSS class or if the class is removed at a later step.
5392                *   Note that class-based animations are treated differently compared to structural animations
5393                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5394                *   depending if CSS or JavaScript animations are used.
5395                *
5396                * @param {DOMElement} element the element which the CSS classes will be applied to
5397                * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5398                * @param {object=} options an optional collection of options/styles that will be applied to the element
5399                *
5400                * @return {Promise} the animation callback promise
5401                */
5402               addClass: function(element, className, options) {
5403                 options = prepareAnimateOptions(options);
5404                 options.addClass = mergeClasses(options.addclass, className);
5405                 return $$animateQueue.push(element, 'addClass', options);
5406               },
5407
5408               /**
5409                * @ngdoc method
5410                * @name $animate#removeClass
5411                * @kind function
5412                *
5413                * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5414                *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5415                *   animation if element does not contain the CSS class or if the class is added at a later step.
5416                *   Note that class-based animations are treated differently compared to structural animations
5417                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5418                *   depending if CSS or JavaScript animations are used.
5419                *
5420                * @param {DOMElement} element the element which the CSS classes will be applied to
5421                * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5422                * @param {object=} options an optional collection of options/styles that will be applied to the element
5423                *
5424                * @return {Promise} the animation callback promise
5425                */
5426               removeClass: function(element, className, options) {
5427                 options = prepareAnimateOptions(options);
5428                 options.removeClass = mergeClasses(options.removeClass, className);
5429                 return $$animateQueue.push(element, 'removeClass', options);
5430               },
5431
5432               /**
5433                * @ngdoc method
5434                * @name $animate#setClass
5435                * @kind function
5436                *
5437                * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5438                *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5439                *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5440                *    passed. Note that class-based animations are treated differently compared to structural animations
5441                *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5442                *    depending if CSS or JavaScript animations are used.
5443                *
5444                * @param {DOMElement} element the element which the CSS classes will be applied to
5445                * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5446                * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5447                * @param {object=} options an optional collection of options/styles that will be applied to the element
5448                *
5449                * @return {Promise} the animation callback promise
5450                */
5451               setClass: function(element, add, remove, options) {
5452                 options = prepareAnimateOptions(options);
5453                 options.addClass = mergeClasses(options.addClass, add);
5454                 options.removeClass = mergeClasses(options.removeClass, remove);
5455                 return $$animateQueue.push(element, 'setClass', options);
5456               },
5457
5458               /**
5459                * @ngdoc method
5460                * @name $animate#animate
5461                * @kind function
5462                *
5463                * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5464                * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5465                * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5466                * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5467                * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5468                *
5469                * @param {DOMElement} element the element which the CSS styles will be applied to
5470                * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5471                * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5472                * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5473                *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5474                *    (Note that if no animation is detected then this value will not be appplied to the element.)
5475                * @param {object=} options an optional collection of options/styles that will be applied to the element
5476                *
5477                * @return {Promise} the animation callback promise
5478                */
5479               animate: function(element, from, to, className, options) {
5480                 options = prepareAnimateOptions(options);
5481                 options.from = options.from ? extend(options.from, from) : from;
5482                 options.to   = options.to   ? extend(options.to, to)     : to;
5483
5484                 className = className || 'ng-inline-animate';
5485                 options.tempClasses = mergeClasses(options.tempClasses, className);
5486                 return $$animateQueue.push(element, 'animate', options);
5487               }
5488             };
5489           }];
5490         }];
5491
5492         /**
5493          * @ngdoc service
5494          * @name $animateCss
5495          * @kind object
5496          *
5497          * @description
5498          * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5499          * then the `$animateCss` service will actually perform animations.
5500          *
5501          * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5502          */
5503         var $CoreAnimateCssProvider = function() {
5504           this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5505
5506             var RAFPromise = function() {};
5507             RAFPromise.prototype = {
5508               done: function(cancel) {
5509                 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5510               },
5511               end: function() {
5512                 this.done();
5513               },
5514               cancel: function() {
5515                 this.done(true);
5516               },
5517               getPromise: function() {
5518                 if (!this.defer) {
5519                   this.defer = $q.defer();
5520                 }
5521                 return this.defer.promise;
5522               },
5523               then: function(f1,f2) {
5524                 return this.getPromise().then(f1,f2);
5525               },
5526               'catch': function(f1) {
5527                 return this.getPromise()['catch'](f1);
5528               },
5529               'finally': function(f1) {
5530                 return this.getPromise()['finally'](f1);
5531               }
5532             };
5533
5534             return function(element, options) {
5535               // there is no point in applying the styles since
5536               // there is no animation that goes on at all in
5537               // this version of $animateCss.
5538               if (options.cleanupStyles) {
5539                 options.from = options.to = null;
5540               }
5541
5542               if (options.from) {
5543                 element.css(options.from);
5544                 options.from = null;
5545               }
5546
5547               var closed, runner = new RAFPromise();
5548               return {
5549                 start: run,
5550                 end: run
5551               };
5552
5553               function run() {
5554                 $$rAF(function() {
5555                   close();
5556                   if (!closed) {
5557                     runner.done();
5558                   }
5559                   closed = true;
5560                 });
5561                 return runner;
5562               }
5563
5564               function close() {
5565                 if (options.addClass) {
5566                   element.addClass(options.addClass);
5567                   options.addClass = null;
5568                 }
5569                 if (options.removeClass) {
5570                   element.removeClass(options.removeClass);
5571                   options.removeClass = null;
5572                 }
5573                 if (options.to) {
5574                   element.css(options.to);
5575                   options.to = null;
5576                 }
5577               }
5578             };
5579           }];
5580         };
5581
5582         /* global stripHash: true */
5583
5584         /**
5585          * ! This is a private undocumented service !
5586          *
5587          * @name $browser
5588          * @requires $log
5589          * @description
5590          * This object has two goals:
5591          *
5592          * - hide all the global state in the browser caused by the window object
5593          * - abstract away all the browser specific features and inconsistencies
5594          *
5595          * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5596          * service, which can be used for convenient testing of the application without the interaction with
5597          * the real browser apis.
5598          */
5599         /**
5600          * @param {object} window The global window object.
5601          * @param {object} document jQuery wrapped document.
5602          * @param {object} $log window.console or an object with the same interface.
5603          * @param {object} $sniffer $sniffer service
5604          */
5605         function Browser(window, document, $log, $sniffer) {
5606           var self = this,
5607               rawDocument = document[0],
5608               location = window.location,
5609               history = window.history,
5610               setTimeout = window.setTimeout,
5611               clearTimeout = window.clearTimeout,
5612               pendingDeferIds = {};
5613
5614           self.isMock = false;
5615
5616           var outstandingRequestCount = 0;
5617           var outstandingRequestCallbacks = [];
5618
5619           // TODO(vojta): remove this temporary api
5620           self.$$completeOutstandingRequest = completeOutstandingRequest;
5621           self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5622
5623           /**
5624            * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5625            * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5626            */
5627           function completeOutstandingRequest(fn) {
5628             try {
5629               fn.apply(null, sliceArgs(arguments, 1));
5630             } finally {
5631               outstandingRequestCount--;
5632               if (outstandingRequestCount === 0) {
5633                 while (outstandingRequestCallbacks.length) {
5634                   try {
5635                     outstandingRequestCallbacks.pop()();
5636                   } catch (e) {
5637                     $log.error(e);
5638                   }
5639                 }
5640               }
5641             }
5642           }
5643
5644           function getHash(url) {
5645             var index = url.indexOf('#');
5646             return index === -1 ? '' : url.substr(index);
5647           }
5648
5649           /**
5650            * @private
5651            * Note: this method is used only by scenario runner
5652            * TODO(vojta): prefix this method with $$ ?
5653            * @param {function()} callback Function that will be called when no outstanding request
5654            */
5655           self.notifyWhenNoOutstandingRequests = function(callback) {
5656             if (outstandingRequestCount === 0) {
5657               callback();
5658             } else {
5659               outstandingRequestCallbacks.push(callback);
5660             }
5661           };
5662
5663           //////////////////////////////////////////////////////////////
5664           // URL API
5665           //////////////////////////////////////////////////////////////
5666
5667           var cachedState, lastHistoryState,
5668               lastBrowserUrl = location.href,
5669               baseElement = document.find('base'),
5670               pendingLocation = null;
5671
5672           cacheState();
5673           lastHistoryState = cachedState;
5674
5675           /**
5676            * @name $browser#url
5677            *
5678            * @description
5679            * GETTER:
5680            * Without any argument, this method just returns current value of location.href.
5681            *
5682            * SETTER:
5683            * With at least one argument, this method sets url to new value.
5684            * If html5 history api supported, pushState/replaceState is used, otherwise
5685            * location.href/location.replace is used.
5686            * Returns its own instance to allow chaining
5687            *
5688            * NOTE: this api is intended for use only by the $location service. Please use the
5689            * {@link ng.$location $location service} to change url.
5690            *
5691            * @param {string} url New url (when used as setter)
5692            * @param {boolean=} replace Should new url replace current history record?
5693            * @param {object=} state object to use with pushState/replaceState
5694            */
5695           self.url = function(url, replace, state) {
5696             // In modern browsers `history.state` is `null` by default; treating it separately
5697             // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5698             // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5699             if (isUndefined(state)) {
5700               state = null;
5701             }
5702
5703             // Android Browser BFCache causes location, history reference to become stale.
5704             if (location !== window.location) location = window.location;
5705             if (history !== window.history) history = window.history;
5706
5707             // setter
5708             if (url) {
5709               var sameState = lastHistoryState === state;
5710
5711               // Don't change anything if previous and current URLs and states match. This also prevents
5712               // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5713               // See https://github.com/angular/angular.js/commit/ffb2701
5714               if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5715                 return self;
5716               }
5717               var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5718               lastBrowserUrl = url;
5719               lastHistoryState = state;
5720               // Don't use history API if only the hash changed
5721               // due to a bug in IE10/IE11 which leads
5722               // to not firing a `hashchange` nor `popstate` event
5723               // in some cases (see #9143).
5724               if ($sniffer.history && (!sameBase || !sameState)) {
5725                 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5726                 cacheState();
5727                 // Do the assignment again so that those two variables are referentially identical.
5728                 lastHistoryState = cachedState;
5729               } else {
5730                 if (!sameBase || pendingLocation) {
5731                   pendingLocation = url;
5732                 }
5733                 if (replace) {
5734                   location.replace(url);
5735                 } else if (!sameBase) {
5736                   location.href = url;
5737                 } else {
5738                   location.hash = getHash(url);
5739                 }
5740                 if (location.href !== url) {
5741                   pendingLocation = url;
5742                 }
5743               }
5744               return self;
5745             // getter
5746             } else {
5747               // - pendingLocation is needed as browsers don't allow to read out
5748               //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5749               //   https://openradar.appspot.com/22186109).
5750               // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5751               return pendingLocation || location.href.replace(/%27/g,"'");
5752             }
5753           };
5754
5755           /**
5756            * @name $browser#state
5757            *
5758            * @description
5759            * This method is a getter.
5760            *
5761            * Return history.state or null if history.state is undefined.
5762            *
5763            * @returns {object} state
5764            */
5765           self.state = function() {
5766             return cachedState;
5767           };
5768
5769           var urlChangeListeners = [],
5770               urlChangeInit = false;
5771
5772           function cacheStateAndFireUrlChange() {
5773             pendingLocation = null;
5774             cacheState();
5775             fireUrlChange();
5776           }
5777
5778           function getCurrentState() {
5779             try {
5780               return history.state;
5781             } catch (e) {
5782               // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5783             }
5784           }
5785
5786           // This variable should be used *only* inside the cacheState function.
5787           var lastCachedState = null;
5788           function cacheState() {
5789             // This should be the only place in $browser where `history.state` is read.
5790             cachedState = getCurrentState();
5791             cachedState = isUndefined(cachedState) ? null : cachedState;
5792
5793             // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5794             if (equals(cachedState, lastCachedState)) {
5795               cachedState = lastCachedState;
5796             }
5797             lastCachedState = cachedState;
5798           }
5799
5800           function fireUrlChange() {
5801             if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5802               return;
5803             }
5804
5805             lastBrowserUrl = self.url();
5806             lastHistoryState = cachedState;
5807             forEach(urlChangeListeners, function(listener) {
5808               listener(self.url(), cachedState);
5809             });
5810           }
5811
5812           /**
5813            * @name $browser#onUrlChange
5814            *
5815            * @description
5816            * Register callback function that will be called, when url changes.
5817            *
5818            * It's only called when the url is changed from outside of angular:
5819            * - user types different url into address bar
5820            * - user clicks on history (forward/back) button
5821            * - user clicks on a link
5822            *
5823            * It's not called when url is changed by $browser.url() method
5824            *
5825            * The listener gets called with new url as parameter.
5826            *
5827            * NOTE: this api is intended for use only by the $location service. Please use the
5828            * {@link ng.$location $location service} to monitor url changes in angular apps.
5829            *
5830            * @param {function(string)} listener Listener function to be called when url changes.
5831            * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5832            */
5833           self.onUrlChange = function(callback) {
5834             // TODO(vojta): refactor to use node's syntax for events
5835             if (!urlChangeInit) {
5836               // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5837               // don't fire popstate when user change the address bar and don't fire hashchange when url
5838               // changed by push/replaceState
5839
5840               // html5 history api - popstate event
5841               if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5842               // hashchange event
5843               jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5844
5845               urlChangeInit = true;
5846             }
5847
5848             urlChangeListeners.push(callback);
5849             return callback;
5850           };
5851
5852           /**
5853            * @private
5854            * Remove popstate and hashchange handler from window.
5855            *
5856            * NOTE: this api is intended for use only by $rootScope.
5857            */
5858           self.$$applicationDestroyed = function() {
5859             jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5860           };
5861
5862           /**
5863            * Checks whether the url has changed outside of Angular.
5864            * Needs to be exported to be able to check for changes that have been done in sync,
5865            * as hashchange/popstate events fire in async.
5866            */
5867           self.$$checkUrlChange = fireUrlChange;
5868
5869           //////////////////////////////////////////////////////////////
5870           // Misc API
5871           //////////////////////////////////////////////////////////////
5872
5873           /**
5874            * @name $browser#baseHref
5875            *
5876            * @description
5877            * Returns current <base href>
5878            * (always relative - without domain)
5879            *
5880            * @returns {string} The current base href
5881            */
5882           self.baseHref = function() {
5883             var href = baseElement.attr('href');
5884             return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5885           };
5886
5887           /**
5888            * @name $browser#defer
5889            * @param {function()} fn A function, who's execution should be deferred.
5890            * @param {number=} [delay=0] of milliseconds to defer the function execution.
5891            * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5892            *
5893            * @description
5894            * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5895            *
5896            * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5897            * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5898            * via `$browser.defer.flush()`.
5899            *
5900            */
5901           self.defer = function(fn, delay) {
5902             var timeoutId;
5903             outstandingRequestCount++;
5904             timeoutId = setTimeout(function() {
5905               delete pendingDeferIds[timeoutId];
5906               completeOutstandingRequest(fn);
5907             }, delay || 0);
5908             pendingDeferIds[timeoutId] = true;
5909             return timeoutId;
5910           };
5911
5912
5913           /**
5914            * @name $browser#defer.cancel
5915            *
5916            * @description
5917            * Cancels a deferred task identified with `deferId`.
5918            *
5919            * @param {*} deferId Token returned by the `$browser.defer` function.
5920            * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5921            *                    canceled.
5922            */
5923           self.defer.cancel = function(deferId) {
5924             if (pendingDeferIds[deferId]) {
5925               delete pendingDeferIds[deferId];
5926               clearTimeout(deferId);
5927               completeOutstandingRequest(noop);
5928               return true;
5929             }
5930             return false;
5931           };
5932
5933         }
5934
5935         function $BrowserProvider() {
5936           this.$get = ['$window', '$log', '$sniffer', '$document',
5937               function($window, $log, $sniffer, $document) {
5938                 return new Browser($window, $document, $log, $sniffer);
5939               }];
5940         }
5941
5942         /**
5943          * @ngdoc service
5944          * @name $cacheFactory
5945          *
5946          * @description
5947          * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5948          * them.
5949          *
5950          * ```js
5951          *
5952          *  var cache = $cacheFactory('cacheId');
5953          *  expect($cacheFactory.get('cacheId')).toBe(cache);
5954          *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5955          *
5956          *  cache.put("key", "value");
5957          *  cache.put("another key", "another value");
5958          *
5959          *  // We've specified no options on creation
5960          *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5961          *
5962          * ```
5963          *
5964          *
5965          * @param {string} cacheId Name or id of the newly created cache.
5966          * @param {object=} options Options object that specifies the cache behavior. Properties:
5967          *
5968          *   - `{number=}` `capacity` — turns the cache into LRU cache.
5969          *
5970          * @returns {object} Newly created cache object with the following set of methods:
5971          *
5972          * - `{object}` `info()` — Returns id, size, and options of cache.
5973          * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5974          *   it.
5975          * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5976          * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5977          * - `{void}` `removeAll()` — Removes all cached values.
5978          * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5979          *
5980          * @example
5981            <example module="cacheExampleApp">
5982              <file name="index.html">
5983                <div ng-controller="CacheController">
5984                  <input ng-model="newCacheKey" placeholder="Key">
5985                  <input ng-model="newCacheValue" placeholder="Value">
5986                  <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5987
5988                  <p ng-if="keys.length">Cached Values</p>
5989                  <div ng-repeat="key in keys">
5990                    <span ng-bind="key"></span>
5991                    <span>: </span>
5992                    <b ng-bind="cache.get(key)"></b>
5993                  </div>
5994
5995                  <p>Cache Info</p>
5996                  <div ng-repeat="(key, value) in cache.info()">
5997                    <span ng-bind="key"></span>
5998                    <span>: </span>
5999                    <b ng-bind="value"></b>
6000                  </div>
6001                </div>
6002              </file>
6003              <file name="script.js">
6004                angular.module('cacheExampleApp', []).
6005                  controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6006                    $scope.keys = [];
6007                    $scope.cache = $cacheFactory('cacheId');
6008                    $scope.put = function(key, value) {
6009                      if (angular.isUndefined($scope.cache.get(key))) {
6010                        $scope.keys.push(key);
6011                      }
6012                      $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6013                    };
6014                  }]);
6015              </file>
6016              <file name="style.css">
6017                p {
6018                  margin: 10px 0 3px;
6019                }
6020              </file>
6021            </example>
6022          */
6023         function $CacheFactoryProvider() {
6024
6025           this.$get = function() {
6026             var caches = {};
6027
6028             function cacheFactory(cacheId, options) {
6029               if (cacheId in caches) {
6030                 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6031               }
6032
6033               var size = 0,
6034                   stats = extend({}, options, {id: cacheId}),
6035                   data = createMap(),
6036                   capacity = (options && options.capacity) || Number.MAX_VALUE,
6037                   lruHash = createMap(),
6038                   freshEnd = null,
6039                   staleEnd = null;
6040
6041               /**
6042                * @ngdoc type
6043                * @name $cacheFactory.Cache
6044                *
6045                * @description
6046                * A cache object used to store and retrieve data, primarily used by
6047                * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6048                * templates and other data.
6049                *
6050                * ```js
6051                *  angular.module('superCache')
6052                *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6053                *      return $cacheFactory('super-cache');
6054                *    }]);
6055                * ```
6056                *
6057                * Example test:
6058                *
6059                * ```js
6060                *  it('should behave like a cache', inject(function(superCache) {
6061                *    superCache.put('key', 'value');
6062                *    superCache.put('another key', 'another value');
6063                *
6064                *    expect(superCache.info()).toEqual({
6065                *      id: 'super-cache',
6066                *      size: 2
6067                *    });
6068                *
6069                *    superCache.remove('another key');
6070                *    expect(superCache.get('another key')).toBeUndefined();
6071                *
6072                *    superCache.removeAll();
6073                *    expect(superCache.info()).toEqual({
6074                *      id: 'super-cache',
6075                *      size: 0
6076                *    });
6077                *  }));
6078                * ```
6079                */
6080               return caches[cacheId] = {
6081
6082                 /**
6083                  * @ngdoc method
6084                  * @name $cacheFactory.Cache#put
6085                  * @kind function
6086                  *
6087                  * @description
6088                  * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6089                  * retrieved later, and incrementing the size of the cache if the key was not already
6090                  * present in the cache. If behaving like an LRU cache, it will also remove stale
6091                  * entries from the set.
6092                  *
6093                  * It will not insert undefined values into the cache.
6094                  *
6095                  * @param {string} key the key under which the cached data is stored.
6096                  * @param {*} value the value to store alongside the key. If it is undefined, the key
6097                  *    will not be stored.
6098                  * @returns {*} the value stored.
6099                  */
6100                 put: function(key, value) {
6101                   if (isUndefined(value)) return;
6102                   if (capacity < Number.MAX_VALUE) {
6103                     var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6104
6105                     refresh(lruEntry);
6106                   }
6107
6108                   if (!(key in data)) size++;
6109                   data[key] = value;
6110
6111                   if (size > capacity) {
6112                     this.remove(staleEnd.key);
6113                   }
6114
6115                   return value;
6116                 },
6117
6118                 /**
6119                  * @ngdoc method
6120                  * @name $cacheFactory.Cache#get
6121                  * @kind function
6122                  *
6123                  * @description
6124                  * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6125                  *
6126                  * @param {string} key the key of the data to be retrieved
6127                  * @returns {*} the value stored.
6128                  */
6129                 get: function(key) {
6130                   if (capacity < Number.MAX_VALUE) {
6131                     var lruEntry = lruHash[key];
6132
6133                     if (!lruEntry) return;
6134
6135                     refresh(lruEntry);
6136                   }
6137
6138                   return data[key];
6139                 },
6140
6141
6142                 /**
6143                  * @ngdoc method
6144                  * @name $cacheFactory.Cache#remove
6145                  * @kind function
6146                  *
6147                  * @description
6148                  * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6149                  *
6150                  * @param {string} key the key of the entry to be removed
6151                  */
6152                 remove: function(key) {
6153                   if (capacity < Number.MAX_VALUE) {
6154                     var lruEntry = lruHash[key];
6155
6156                     if (!lruEntry) return;
6157
6158                     if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6159                     if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6160                     link(lruEntry.n,lruEntry.p);
6161
6162                     delete lruHash[key];
6163                   }
6164
6165                   if (!(key in data)) return;
6166
6167                   delete data[key];
6168                   size--;
6169                 },
6170
6171
6172                 /**
6173                  * @ngdoc method
6174                  * @name $cacheFactory.Cache#removeAll
6175                  * @kind function
6176                  *
6177                  * @description
6178                  * Clears the cache object of any entries.
6179                  */
6180                 removeAll: function() {
6181                   data = createMap();
6182                   size = 0;
6183                   lruHash = createMap();
6184                   freshEnd = staleEnd = null;
6185                 },
6186
6187
6188                 /**
6189                  * @ngdoc method
6190                  * @name $cacheFactory.Cache#destroy
6191                  * @kind function
6192                  *
6193                  * @description
6194                  * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6195                  * removing it from the {@link $cacheFactory $cacheFactory} set.
6196                  */
6197                 destroy: function() {
6198                   data = null;
6199                   stats = null;
6200                   lruHash = null;
6201                   delete caches[cacheId];
6202                 },
6203
6204
6205                 /**
6206                  * @ngdoc method
6207                  * @name $cacheFactory.Cache#info
6208                  * @kind function
6209                  *
6210                  * @description
6211                  * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6212                  *
6213                  * @returns {object} an object with the following properties:
6214                  *   <ul>
6215                  *     <li>**id**: the id of the cache instance</li>
6216                  *     <li>**size**: the number of entries kept in the cache instance</li>
6217                  *     <li>**...**: any additional properties from the options object when creating the
6218                  *       cache.</li>
6219                  *   </ul>
6220                  */
6221                 info: function() {
6222                   return extend({}, stats, {size: size});
6223                 }
6224               };
6225
6226
6227               /**
6228                * makes the `entry` the freshEnd of the LRU linked list
6229                */
6230               function refresh(entry) {
6231                 if (entry != freshEnd) {
6232                   if (!staleEnd) {
6233                     staleEnd = entry;
6234                   } else if (staleEnd == entry) {
6235                     staleEnd = entry.n;
6236                   }
6237
6238                   link(entry.n, entry.p);
6239                   link(entry, freshEnd);
6240                   freshEnd = entry;
6241                   freshEnd.n = null;
6242                 }
6243               }
6244
6245
6246               /**
6247                * bidirectionally links two entries of the LRU linked list
6248                */
6249               function link(nextEntry, prevEntry) {
6250                 if (nextEntry != prevEntry) {
6251                   if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6252                   if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6253                 }
6254               }
6255             }
6256
6257
6258           /**
6259            * @ngdoc method
6260            * @name $cacheFactory#info
6261            *
6262            * @description
6263            * Get information about all the caches that have been created
6264            *
6265            * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6266            */
6267             cacheFactory.info = function() {
6268               var info = {};
6269               forEach(caches, function(cache, cacheId) {
6270                 info[cacheId] = cache.info();
6271               });
6272               return info;
6273             };
6274
6275
6276           /**
6277            * @ngdoc method
6278            * @name $cacheFactory#get
6279            *
6280            * @description
6281            * Get access to a cache object by the `cacheId` used when it was created.
6282            *
6283            * @param {string} cacheId Name or id of a cache to access.
6284            * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6285            */
6286             cacheFactory.get = function(cacheId) {
6287               return caches[cacheId];
6288             };
6289
6290
6291             return cacheFactory;
6292           };
6293         }
6294
6295         /**
6296          * @ngdoc service
6297          * @name $templateCache
6298          *
6299          * @description
6300          * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6301          * can load templates directly into the cache in a `script` tag, or by consuming the
6302          * `$templateCache` service directly.
6303          *
6304          * Adding via the `script` tag:
6305          *
6306          * ```html
6307          *   <script type="text/ng-template" id="templateId.html">
6308          *     <p>This is the content of the template</p>
6309          *   </script>
6310          * ```
6311          *
6312          * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6313          * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6314          * element with ng-app attribute), otherwise the template will be ignored.
6315          *
6316          * Adding via the `$templateCache` service:
6317          *
6318          * ```js
6319          * var myApp = angular.module('myApp', []);
6320          * myApp.run(function($templateCache) {
6321          *   $templateCache.put('templateId.html', 'This is the content of the template');
6322          * });
6323          * ```
6324          *
6325          * To retrieve the template later, simply use it in your HTML:
6326          * ```html
6327          * <div ng-include=" 'templateId.html' "></div>
6328          * ```
6329          *
6330          * or get it via Javascript:
6331          * ```js
6332          * $templateCache.get('templateId.html')
6333          * ```
6334          *
6335          * See {@link ng.$cacheFactory $cacheFactory}.
6336          *
6337          */
6338         function $TemplateCacheProvider() {
6339           this.$get = ['$cacheFactory', function($cacheFactory) {
6340             return $cacheFactory('templates');
6341           }];
6342         }
6343
6344         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6345          *     Any commits to this file should be reviewed with security in mind.  *
6346          *   Changes to this file can potentially create security vulnerabilities. *
6347          *          An approval from 2 Core members with history of modifying      *
6348          *                         this file is required.                          *
6349          *                                                                         *
6350          *  Does the change somehow allow for arbitrary javascript to be executed? *
6351          *    Or allows for someone to change the prototype of built-in objects?   *
6352          *     Or gives undesired access to variables likes document or window?    *
6353          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6354
6355         /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6356          *
6357          * DOM-related variables:
6358          *
6359          * - "node" - DOM Node
6360          * - "element" - DOM Element or Node
6361          * - "$node" or "$element" - jqLite-wrapped node or element
6362          *
6363          *
6364          * Compiler related stuff:
6365          *
6366          * - "linkFn" - linking fn of a single directive
6367          * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6368          * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6369          * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6370          */
6371
6372
6373         /**
6374          * @ngdoc service
6375          * @name $compile
6376          * @kind function
6377          *
6378          * @description
6379          * Compiles an HTML string or DOM into a template and produces a template function, which
6380          * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6381          *
6382          * The compilation is a process of walking the DOM tree and matching DOM elements to
6383          * {@link ng.$compileProvider#directive directives}.
6384          *
6385          * <div class="alert alert-warning">
6386          * **Note:** This document is an in-depth reference of all directive options.
6387          * For a gentle introduction to directives with examples of common use cases,
6388          * see the {@link guide/directive directive guide}.
6389          * </div>
6390          *
6391          * ## Comprehensive Directive API
6392          *
6393          * There are many different options for a directive.
6394          *
6395          * The difference resides in the return value of the factory function.
6396          * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6397          * or just the `postLink` function (all other properties will have the default values).
6398          *
6399          * <div class="alert alert-success">
6400          * **Best Practice:** It's recommended to use the "directive definition object" form.
6401          * </div>
6402          *
6403          * Here's an example directive declared with a Directive Definition Object:
6404          *
6405          * ```js
6406          *   var myModule = angular.module(...);
6407          *
6408          *   myModule.directive('directiveName', function factory(injectables) {
6409          *     var directiveDefinitionObject = {
6410          *       priority: 0,
6411          *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6412          *       // or
6413          *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6414          *       transclude: false,
6415          *       restrict: 'A',
6416          *       templateNamespace: 'html',
6417          *       scope: false,
6418          *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6419          *       controllerAs: 'stringIdentifier',
6420          *       bindToController: false,
6421          *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6422          *       compile: function compile(tElement, tAttrs, transclude) {
6423          *         return {
6424          *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6425          *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6426          *         }
6427          *         // or
6428          *         // return function postLink( ... ) { ... }
6429          *       },
6430          *       // or
6431          *       // link: {
6432          *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6433          *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6434          *       // }
6435          *       // or
6436          *       // link: function postLink( ... ) { ... }
6437          *     };
6438          *     return directiveDefinitionObject;
6439          *   });
6440          * ```
6441          *
6442          * <div class="alert alert-warning">
6443          * **Note:** Any unspecified options will use the default value. You can see the default values below.
6444          * </div>
6445          *
6446          * Therefore the above can be simplified as:
6447          *
6448          * ```js
6449          *   var myModule = angular.module(...);
6450          *
6451          *   myModule.directive('directiveName', function factory(injectables) {
6452          *     var directiveDefinitionObject = {
6453          *       link: function postLink(scope, iElement, iAttrs) { ... }
6454          *     };
6455          *     return directiveDefinitionObject;
6456          *     // or
6457          *     // return function postLink(scope, iElement, iAttrs) { ... }
6458          *   });
6459          * ```
6460          *
6461          *
6462          *
6463          * ### Directive Definition Object
6464          *
6465          * The directive definition object provides instructions to the {@link ng.$compile
6466          * compiler}. The attributes are:
6467          *
6468          * #### `multiElement`
6469          * When this property is set to true, the HTML compiler will collect DOM nodes between
6470          * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6471          * together as the directive elements. It is recommended that this feature be used on directives
6472          * which are not strictly behavioural (such as {@link ngClick}), and which
6473          * do not manipulate or replace child nodes (such as {@link ngInclude}).
6474          *
6475          * #### `priority`
6476          * When there are multiple directives defined on a single DOM element, sometimes it
6477          * is necessary to specify the order in which the directives are applied. The `priority` is used
6478          * to sort the directives before their `compile` functions get called. Priority is defined as a
6479          * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6480          * are also run in priority order, but post-link functions are run in reverse order. The order
6481          * of directives with the same priority is undefined. The default priority is `0`.
6482          *
6483          * #### `terminal`
6484          * If set to true then the current `priority` will be the last set of directives
6485          * which will execute (any directives at the current priority will still execute
6486          * as the order of execution on same `priority` is undefined). Note that expressions
6487          * and other directives used in the directive's template will also be excluded from execution.
6488          *
6489          * #### `scope`
6490          * The scope property can be `true`, an object or a falsy value:
6491          *
6492          * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6493          *
6494          * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6495          * the directive's element. If multiple directives on the same element request a new scope,
6496          * only one new scope is created. The new scope rule does not apply for the root of the template
6497          * since the root of the template always gets a new scope.
6498          *
6499          * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6500          * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6501          * scope. This is useful when creating reusable components, which should not accidentally read or modify
6502          * data in the parent scope.
6503          *
6504          * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6505          * directive's element. These local properties are useful for aliasing values for templates. The keys in
6506          * the object hash map to the name of the property on the isolate scope; the values define how the property
6507          * is bound to the parent scope, via matching attributes on the directive's element:
6508          *
6509          * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6510          *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6511          *   attribute name is assumed to be the same as the local name.
6512          *   Given `<widget my-attr="hello {{name}}">` and widget definition
6513          *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6514          *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6515          *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6516          *   component scope).
6517          *
6518          * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6519          *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6520          *   name is specified then the attribute name is assumed to be the same as the local name.
6521          *   Given `<widget my-attr="parentModel">` and widget definition of
6522          *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6523          *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6524          *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6525          *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6526          *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6527          *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6528          *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6529          *
6530          * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6531          *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6532          *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6533          *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6534          *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6535          *   pass data from the isolated scope via an expression to the parent scope, this can be
6536          *   done by passing a map of local variable names and values into the expression wrapper fn.
6537          *   For example, if the expression is `increment(amount)` then we can specify the amount value
6538          *   by calling the `localFn` as `localFn({amount: 22})`.
6539          *
6540          * In general it's possible to apply more than one directive to one element, but there might be limitations
6541          * depending on the type of scope required by the directives. The following points will help explain these limitations.
6542          * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6543          *
6544          * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6545          * * **child scope** + **no scope** =>  Both directives will share one single child scope
6546          * * **child scope** + **child scope** =>  Both directives will share one single child scope
6547          * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6548          * its parent's scope
6549          * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6550          * be applied to the same element.
6551          * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6552          * cannot be applied to the same element.
6553          *
6554          *
6555          * #### `bindToController`
6556          * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6557          * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6558          * is instantiated, the initial values of the isolate scope bindings are already available.
6559          *
6560          * #### `controller`
6561          * Controller constructor function. The controller is instantiated before the
6562          * pre-linking phase and can be accessed by other directives (see
6563          * `require` attribute). This allows the directives to communicate with each other and augment
6564          * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6565          *
6566          * * `$scope` - Current scope associated with the element
6567          * * `$element` - Current element
6568          * * `$attrs` - Current attributes object for the element
6569          * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6570          *   `function([scope], cloneLinkingFn, futureParentElement)`.
6571          *    * `scope`: optional argument to override the scope.
6572          *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6573          *    * `futureParentElement`:
6574          *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6575          *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6576          *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6577          *          and when the `cloneLinkinFn` is passed,
6578          *          as those elements need to created and cloned in a special way when they are defined outside their
6579          *          usual containers (e.g. like `<svg>`).
6580          *        * See also the `directive.templateNamespace` property.
6581          *
6582          *
6583          * #### `require`
6584          * Require another directive and inject its controller as the fourth argument to the linking function. The
6585          * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6586          * injected argument will be an array in corresponding order. If no such directive can be
6587          * found, or if the directive does not have a controller, then an error is raised (unless no link function
6588          * is specified, in which case error checking is skipped). The name can be prefixed with:
6589          *
6590          * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6591          * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6592          * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6593          * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6594          * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6595          *   `null` to the `link` fn if not found.
6596          * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6597          *   `null` to the `link` fn if not found.
6598          *
6599          *
6600          * #### `controllerAs`
6601          * Identifier name for a reference to the controller in the directive's scope.
6602          * This allows the controller to be referenced from the directive template. This is especially
6603          * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6604          * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6605          * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6606          *
6607          *
6608          * #### `restrict`
6609          * String of subset of `EACM` which restricts the directive to a specific directive
6610          * declaration style. If omitted, the defaults (elements and attributes) are used.
6611          *
6612          * * `E` - Element name (default): `<my-directive></my-directive>`
6613          * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6614          * * `C` - Class: `<div class="my-directive: exp;"></div>`
6615          * * `M` - Comment: `<!-- directive: my-directive exp -->`
6616          *
6617          *
6618          * #### `templateNamespace`
6619          * String representing the document type used by the markup in the template.
6620          * AngularJS needs this information as those elements need to be created and cloned
6621          * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6622          *
6623          * * `html` - All root nodes in the template are HTML. Root nodes may also be
6624          *   top-level elements such as `<svg>` or `<math>`.
6625          * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6626          * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6627          *
6628          * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6629          *
6630          * #### `template`
6631          * HTML markup that may:
6632          * * Replace the contents of the directive's element (default).
6633          * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6634          * * Wrap the contents of the directive's element (if `transclude` is true).
6635          *
6636          * Value may be:
6637          *
6638          * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6639          * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6640          *   function api below) and returns a string value.
6641          *
6642          *
6643          * #### `templateUrl`
6644          * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6645          *
6646          * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6647          * for later when the template has been resolved.  In the meantime it will continue to compile and link
6648          * sibling and parent elements as though this element had not contained any directives.
6649          *
6650          * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6651          * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6652          * case when only one deeply nested directive has `templateUrl`.
6653          *
6654          * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6655          *
6656          * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6657          * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6658          * a string value representing the url.  In either case, the template URL is passed through {@link
6659          * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6660          *
6661          *
6662          * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6663          * specify what the template should replace. Defaults to `false`.
6664          *
6665          * * `true` - the template will replace the directive's element.
6666          * * `false` - the template will replace the contents of the directive's element.
6667          *
6668          * The replacement process migrates all of the attributes / classes from the old element to the new
6669          * one. See the {@link guide/directive#template-expanding-directive
6670          * Directives Guide} for an example.
6671          *
6672          * There are very few scenarios where element replacement is required for the application function,
6673          * the main one being reusable custom components that are used within SVG contexts
6674          * (because SVG doesn't work with custom elements in the DOM tree).
6675          *
6676          * #### `transclude`
6677          * Extract the contents of the element where the directive appears and make it available to the directive.
6678          * The contents are compiled and provided to the directive as a **transclusion function**. See the
6679          * {@link $compile#transclusion Transclusion} section below.
6680          *
6681          * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6682          * directive's element or the entire element:
6683          *
6684          * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6685          * * `'element'` - transclude the whole of the directive's element including any directives on this
6686          *   element that defined at a lower priority than this directive. When used, the `template`
6687          *   property is ignored.
6688          *
6689          *
6690          * #### `compile`
6691          *
6692          * ```js
6693          *   function compile(tElement, tAttrs, transclude) { ... }
6694          * ```
6695          *
6696          * The compile function deals with transforming the template DOM. Since most directives do not do
6697          * template transformation, it is not used often. The compile function takes the following arguments:
6698          *
6699          *   * `tElement` - template element - The element where the directive has been declared. It is
6700          *     safe to do template transformation on the element and child elements only.
6701          *
6702          *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6703          *     between all directive compile functions.
6704          *
6705          *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6706          *
6707          * <div class="alert alert-warning">
6708          * **Note:** The template instance and the link instance may be different objects if the template has
6709          * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6710          * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6711          * should be done in a linking function rather than in a compile function.
6712          * </div>
6713
6714          * <div class="alert alert-warning">
6715          * **Note:** The compile function cannot handle directives that recursively use themselves in their
6716          * own templates or compile functions. Compiling these directives results in an infinite loop and a
6717          * stack overflow errors.
6718          *
6719          * This can be avoided by manually using $compile in the postLink function to imperatively compile
6720          * a directive's template instead of relying on automatic template compilation via `template` or
6721          * `templateUrl` declaration or manual compilation inside the compile function.
6722          * </div>
6723          *
6724          * <div class="alert alert-danger">
6725          * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6726          *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6727          *   to the link function instead.
6728          * </div>
6729
6730          * A compile function can have a return value which can be either a function or an object.
6731          *
6732          * * returning a (post-link) function - is equivalent to registering the linking function via the
6733          *   `link` property of the config object when the compile function is empty.
6734          *
6735          * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6736          *   control when a linking function should be called during the linking phase. See info about
6737          *   pre-linking and post-linking functions below.
6738          *
6739          *
6740          * #### `link`
6741          * This property is used only if the `compile` property is not defined.
6742          *
6743          * ```js
6744          *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6745          * ```
6746          *
6747          * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6748          * executed after the template has been cloned. This is where most of the directive logic will be
6749          * put.
6750          *
6751          *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6752          *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6753          *
6754          *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6755          *     manipulate the children of the element only in `postLink` function since the children have
6756          *     already been linked.
6757          *
6758          *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6759          *     between all directive linking functions.
6760          *
6761          *   * `controller` - the directive's required controller instance(s) - Instances are shared
6762          *     among all directives, which allows the directives to use the controllers as a communication
6763          *     channel. The exact value depends on the directive's `require` property:
6764          *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6765          *       * `string`: the controller instance
6766          *       * `array`: array of controller instances
6767          *
6768          *     If a required controller cannot be found, and it is optional, the instance is `null`,
6769          *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6770          *
6771          *     Note that you can also require the directive's own controller - it will be made available like
6772          *     any other controller.
6773          *
6774          *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6775          *     This is the same as the `$transclude`
6776          *     parameter of directive controllers, see there for details.
6777          *     `function([scope], cloneLinkingFn, futureParentElement)`.
6778          *
6779          * #### Pre-linking function
6780          *
6781          * Executed before the child elements are linked. Not safe to do DOM transformation since the
6782          * compiler linking function will fail to locate the correct elements for linking.
6783          *
6784          * #### Post-linking function
6785          *
6786          * Executed after the child elements are linked.
6787          *
6788          * Note that child elements that contain `templateUrl` directives will not have been compiled
6789          * and linked since they are waiting for their template to load asynchronously and their own
6790          * compilation and linking has been suspended until that occurs.
6791          *
6792          * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6793          * for their async templates to be resolved.
6794          *
6795          *
6796          * ### Transclusion
6797          *
6798          * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6799          * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6800          * scope from where they were taken.
6801          *
6802          * Transclusion is used (often with {@link ngTransclude}) to insert the
6803          * original contents of a directive's element into a specified place in the template of the directive.
6804          * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6805          * content has access to the properties on the scope from which it was taken, even if the directive
6806          * has isolated scope.
6807          * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6808          *
6809          * This makes it possible for the widget to have private state for its template, while the transcluded
6810          * content has access to its originating scope.
6811          *
6812          * <div class="alert alert-warning">
6813          * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6814          * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6815          * Testing Transclusion Directives}.
6816          * </div>
6817          *
6818          * #### Transclusion Functions
6819          *
6820          * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6821          * function** to the directive's `link` function and `controller`. This transclusion function is a special
6822          * **linking function** that will return the compiled contents linked to a new transclusion scope.
6823          *
6824          * <div class="alert alert-info">
6825          * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6826          * ngTransclude will deal with it for us.
6827          * </div>
6828          *
6829          * If you want to manually control the insertion and removal of the transcluded content in your directive
6830          * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6831          * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6832          *
6833          * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6834          * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6835          * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6836          *
6837          * <div class="alert alert-info">
6838          * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6839          * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6840          * </div>
6841          *
6842          * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6843          * attach function**:
6844          *
6845          * ```js
6846          * var transcludedContent, transclusionScope;
6847          *
6848          * $transclude(function(clone, scope) {
6849          *   element.append(clone);
6850          *   transcludedContent = clone;
6851          *   transclusionScope = scope;
6852          * });
6853          * ```
6854          *
6855          * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6856          * associated transclusion scope:
6857          *
6858          * ```js
6859          * transcludedContent.remove();
6860          * transclusionScope.$destroy();
6861          * ```
6862          *
6863          * <div class="alert alert-info">
6864          * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6865          * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6866          * then you are also responsible for calling `$destroy` on the transclusion scope.
6867          * </div>
6868          *
6869          * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6870          * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6871          * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6872          *
6873          *
6874          * #### Transclusion Scopes
6875          *
6876          * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6877          * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6878          * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6879          * was taken.
6880          *
6881          * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6882          * like this:
6883          *
6884          * ```html
6885          * <div ng-app>
6886          *   <div isolate>
6887          *     <div transclusion>
6888          *     </div>
6889          *   </div>
6890          * </div>
6891          * ```
6892          *
6893          * The `$parent` scope hierarchy will look like this:
6894          *
6895          * ```
6896          * - $rootScope
6897          *   - isolate
6898          *     - transclusion
6899          * ```
6900          *
6901          * but the scopes will inherit prototypically from different scopes to their `$parent`.
6902          *
6903          * ```
6904          * - $rootScope
6905          *   - transclusion
6906          * - isolate
6907          * ```
6908          *
6909          *
6910          * ### Attributes
6911          *
6912          * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6913          * `link()` or `compile()` functions. It has a variety of uses.
6914          *
6915          * accessing *Normalized attribute names:*
6916          * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6917          * the attributes object allows for normalized access to
6918          *   the attributes.
6919          *
6920          * * *Directive inter-communication:* All directives share the same instance of the attributes
6921          *   object which allows the directives to use the attributes object as inter directive
6922          *   communication.
6923          *
6924          * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6925          *   allowing other directives to read the interpolated value.
6926          *
6927          * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6928          *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6929          *   the only way to easily get the actual value because during the linking phase the interpolation
6930          *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6931          *
6932          * ```js
6933          * function linkingFn(scope, elm, attrs, ctrl) {
6934          *   // get the attribute value
6935          *   console.log(attrs.ngModel);
6936          *
6937          *   // change the attribute
6938          *   attrs.$set('ngModel', 'new value');
6939          *
6940          *   // observe changes to interpolated attribute
6941          *   attrs.$observe('ngModel', function(value) {
6942          *     console.log('ngModel has changed value to ' + value);
6943          *   });
6944          * }
6945          * ```
6946          *
6947          * ## Example
6948          *
6949          * <div class="alert alert-warning">
6950          * **Note**: Typically directives are registered with `module.directive`. The example below is
6951          * to illustrate how `$compile` works.
6952          * </div>
6953          *
6954          <example module="compileExample">
6955            <file name="index.html">
6956             <script>
6957               angular.module('compileExample', [], function($compileProvider) {
6958                 // configure new 'compile' directive by passing a directive
6959                 // factory function. The factory function injects the '$compile'
6960                 $compileProvider.directive('compile', function($compile) {
6961                   // directive factory creates a link function
6962                   return function(scope, element, attrs) {
6963                     scope.$watch(
6964                       function(scope) {
6965                          // watch the 'compile' expression for changes
6966                         return scope.$eval(attrs.compile);
6967                       },
6968                       function(value) {
6969                         // when the 'compile' expression changes
6970                         // assign it into the current DOM
6971                         element.html(value);
6972
6973                         // compile the new DOM and link it to the current
6974                         // scope.
6975                         // NOTE: we only compile .childNodes so that
6976                         // we don't get into infinite loop compiling ourselves
6977                         $compile(element.contents())(scope);
6978                       }
6979                     );
6980                   };
6981                 });
6982               })
6983               .controller('GreeterController', ['$scope', function($scope) {
6984                 $scope.name = 'Angular';
6985                 $scope.html = 'Hello {{name}}';
6986               }]);
6987             </script>
6988             <div ng-controller="GreeterController">
6989               <input ng-model="name"> <br/>
6990               <textarea ng-model="html"></textarea> <br/>
6991               <div compile="html"></div>
6992             </div>
6993            </file>
6994            <file name="protractor.js" type="protractor">
6995              it('should auto compile', function() {
6996                var textarea = $('textarea');
6997                var output = $('div[compile]');
6998                // The initial state reads 'Hello Angular'.
6999                expect(output.getText()).toBe('Hello Angular');
7000                textarea.clear();
7001                textarea.sendKeys('{{name}}!');
7002                expect(output.getText()).toBe('Angular!');
7003              });
7004            </file>
7005          </example>
7006
7007          *
7008          *
7009          * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7010          * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7011          *
7012          * <div class="alert alert-danger">
7013          * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7014          *   e.g. will not use the right outer scope. Please pass the transclude function as a
7015          *   `parentBoundTranscludeFn` to the link function instead.
7016          * </div>
7017          *
7018          * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7019          *                 root element(s), not their children)
7020          * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7021          * (a DOM element/tree) to a scope. Where:
7022          *
7023          *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7024          *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7025          *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7026          *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7027          *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7028          *
7029          *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7030          *      * `scope` - is the current scope with which the linking function is working with.
7031          *
7032          *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7033          *  keys may be used to control linking behavior:
7034          *
7035          *      * `parentBoundTranscludeFn` - the transclude function made available to
7036          *        directives; if given, it will be passed through to the link functions of
7037          *        directives found in `element` during compilation.
7038          *      * `transcludeControllers` - an object hash with keys that map controller names
7039          *        to controller instances; if given, it will make the controllers
7040          *        available to directives.
7041          *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7042          *        the cloned elements; only needed for transcludes that are allowed to contain non html
7043          *        elements (e.g. SVG elements). See also the directive.controller property.
7044          *
7045          * Calling the linking function returns the element of the template. It is either the original
7046          * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7047          *
7048          * After linking the view is not updated until after a call to $digest which typically is done by
7049          * Angular automatically.
7050          *
7051          * If you need access to the bound view, there are two ways to do it:
7052          *
7053          * - If you are not asking the linking function to clone the template, create the DOM element(s)
7054          *   before you send them to the compiler and keep this reference around.
7055          *   ```js
7056          *     var element = $compile('<p>{{total}}</p>')(scope);
7057          *   ```
7058          *
7059          * - if on the other hand, you need the element to be cloned, the view reference from the original
7060          *   example would not point to the clone, but rather to the original template that was cloned. In
7061          *   this case, you can access the clone via the cloneAttachFn:
7062          *   ```js
7063          *     var templateElement = angular.element('<p>{{total}}</p>'),
7064          *         scope = ....;
7065          *
7066          *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7067          *       //attach the clone to DOM document at the right place
7068          *     });
7069          *
7070          *     //now we have reference to the cloned DOM via `clonedElement`
7071          *   ```
7072          *
7073          *
7074          * For information on how the compiler works, see the
7075          * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7076          */
7077
7078         var $compileMinErr = minErr('$compile');
7079
7080         /**
7081          * @ngdoc provider
7082          * @name $compileProvider
7083          *
7084          * @description
7085          */
7086         $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7087         function $CompileProvider($provide, $$sanitizeUriProvider) {
7088           var hasDirectives = {},
7089               Suffix = 'Directive',
7090               COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7091               CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7092               ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7093               REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7094
7095           // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7096           // The assumption is that future DOM event attribute names will begin with
7097           // 'on' and be composed of only English letters.
7098           var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7099
7100           function parseIsolateBindings(scope, directiveName, isController) {
7101             var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7102
7103             var bindings = {};
7104
7105             forEach(scope, function(definition, scopeName) {
7106               var match = definition.match(LOCAL_REGEXP);
7107
7108               if (!match) {
7109                 throw $compileMinErr('iscp',
7110                     "Invalid {3} for directive '{0}'." +
7111                     " Definition: {... {1}: '{2}' ...}",
7112                     directiveName, scopeName, definition,
7113                     (isController ? "controller bindings definition" :
7114                     "isolate scope definition"));
7115               }
7116
7117               bindings[scopeName] = {
7118                 mode: match[1][0],
7119                 collection: match[2] === '*',
7120                 optional: match[3] === '?',
7121                 attrName: match[4] || scopeName
7122               };
7123             });
7124
7125             return bindings;
7126           }
7127
7128           function parseDirectiveBindings(directive, directiveName) {
7129             var bindings = {
7130               isolateScope: null,
7131               bindToController: null
7132             };
7133             if (isObject(directive.scope)) {
7134               if (directive.bindToController === true) {
7135                 bindings.bindToController = parseIsolateBindings(directive.scope,
7136                                                                  directiveName, true);
7137                 bindings.isolateScope = {};
7138               } else {
7139                 bindings.isolateScope = parseIsolateBindings(directive.scope,
7140                                                              directiveName, false);
7141               }
7142             }
7143             if (isObject(directive.bindToController)) {
7144               bindings.bindToController =
7145                   parseIsolateBindings(directive.bindToController, directiveName, true);
7146             }
7147             if (isObject(bindings.bindToController)) {
7148               var controller = directive.controller;
7149               var controllerAs = directive.controllerAs;
7150               if (!controller) {
7151                 // There is no controller, there may or may not be a controllerAs property
7152                 throw $compileMinErr('noctrl',
7153                       "Cannot bind to controller without directive '{0}'s controller.",
7154                       directiveName);
7155               } else if (!identifierForController(controller, controllerAs)) {
7156                 // There is a controller, but no identifier or controllerAs property
7157                 throw $compileMinErr('noident',
7158                       "Cannot bind to controller without identifier for directive '{0}'.",
7159                       directiveName);
7160               }
7161             }
7162             return bindings;
7163           }
7164
7165           function assertValidDirectiveName(name) {
7166             var letter = name.charAt(0);
7167             if (!letter || letter !== lowercase(letter)) {
7168               throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7169             }
7170             if (name !== name.trim()) {
7171               throw $compileMinErr('baddir',
7172                     "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7173                     name);
7174             }
7175           }
7176
7177           /**
7178            * @ngdoc method
7179            * @name $compileProvider#directive
7180            * @kind function
7181            *
7182            * @description
7183            * Register a new directive with the compiler.
7184            *
7185            * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7186            *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7187            *    names and the values are the factories.
7188            * @param {Function|Array} directiveFactory An injectable directive factory function. See
7189            *    {@link guide/directive} for more info.
7190            * @returns {ng.$compileProvider} Self for chaining.
7191            */
7192            this.directive = function registerDirective(name, directiveFactory) {
7193             assertNotHasOwnProperty(name, 'directive');
7194             if (isString(name)) {
7195               assertValidDirectiveName(name);
7196               assertArg(directiveFactory, 'directiveFactory');
7197               if (!hasDirectives.hasOwnProperty(name)) {
7198                 hasDirectives[name] = [];
7199                 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7200                   function($injector, $exceptionHandler) {
7201                     var directives = [];
7202                     forEach(hasDirectives[name], function(directiveFactory, index) {
7203                       try {
7204                         var directive = $injector.invoke(directiveFactory);
7205                         if (isFunction(directive)) {
7206                           directive = { compile: valueFn(directive) };
7207                         } else if (!directive.compile && directive.link) {
7208                           directive.compile = valueFn(directive.link);
7209                         }
7210                         directive.priority = directive.priority || 0;
7211                         directive.index = index;
7212                         directive.name = directive.name || name;
7213                         directive.require = directive.require || (directive.controller && directive.name);
7214                         directive.restrict = directive.restrict || 'EA';
7215                         var bindings = directive.$$bindings =
7216                             parseDirectiveBindings(directive, directive.name);
7217                         if (isObject(bindings.isolateScope)) {
7218                           directive.$$isolateBindings = bindings.isolateScope;
7219                         }
7220                         directive.$$moduleName = directiveFactory.$$moduleName;
7221                         directives.push(directive);
7222                       } catch (e) {
7223                         $exceptionHandler(e);
7224                       }
7225                     });
7226                     return directives;
7227                   }]);
7228               }
7229               hasDirectives[name].push(directiveFactory);
7230             } else {
7231               forEach(name, reverseParams(registerDirective));
7232             }
7233             return this;
7234           };
7235
7236
7237           /**
7238            * @ngdoc method
7239            * @name $compileProvider#aHrefSanitizationWhitelist
7240            * @kind function
7241            *
7242            * @description
7243            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7244            * urls during a[href] sanitization.
7245            *
7246            * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7247            *
7248            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7249            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7250            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7251            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7252            *
7253            * @param {RegExp=} regexp New regexp to whitelist urls with.
7254            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7255            *    chaining otherwise.
7256            */
7257           this.aHrefSanitizationWhitelist = function(regexp) {
7258             if (isDefined(regexp)) {
7259               $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7260               return this;
7261             } else {
7262               return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7263             }
7264           };
7265
7266
7267           /**
7268            * @ngdoc method
7269            * @name $compileProvider#imgSrcSanitizationWhitelist
7270            * @kind function
7271            *
7272            * @description
7273            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7274            * urls during img[src] sanitization.
7275            *
7276            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7277            *
7278            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7279            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7280            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7281            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7282            *
7283            * @param {RegExp=} regexp New regexp to whitelist urls with.
7284            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7285            *    chaining otherwise.
7286            */
7287           this.imgSrcSanitizationWhitelist = function(regexp) {
7288             if (isDefined(regexp)) {
7289               $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7290               return this;
7291             } else {
7292               return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7293             }
7294           };
7295
7296           /**
7297            * @ngdoc method
7298            * @name  $compileProvider#debugInfoEnabled
7299            *
7300            * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7301            * current debugInfoEnabled state
7302            * @returns {*} current value if used as getter or itself (chaining) if used as setter
7303            *
7304            * @kind function
7305            *
7306            * @description
7307            * Call this method to enable/disable various debug runtime information in the compiler such as adding
7308            * binding information and a reference to the current scope on to DOM elements.
7309            * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7310            * * `ng-binding` CSS class
7311            * * `$binding` data property containing an array of the binding expressions
7312            *
7313            * You may want to disable this in production for a significant performance boost. See
7314            * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7315            *
7316            * The default value is true.
7317            */
7318           var debugInfoEnabled = true;
7319           this.debugInfoEnabled = function(enabled) {
7320             if (isDefined(enabled)) {
7321               debugInfoEnabled = enabled;
7322               return this;
7323             }
7324             return debugInfoEnabled;
7325           };
7326
7327           this.$get = [
7328                     '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7329                     '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7330             function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7331                      $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7332
7333             var Attributes = function(element, attributesToCopy) {
7334               if (attributesToCopy) {
7335                 var keys = Object.keys(attributesToCopy);
7336                 var i, l, key;
7337
7338                 for (i = 0, l = keys.length; i < l; i++) {
7339                   key = keys[i];
7340                   this[key] = attributesToCopy[key];
7341                 }
7342               } else {
7343                 this.$attr = {};
7344               }
7345
7346               this.$$element = element;
7347             };
7348
7349             Attributes.prototype = {
7350               /**
7351                * @ngdoc method
7352                * @name $compile.directive.Attributes#$normalize
7353                * @kind function
7354                *
7355                * @description
7356                * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7357                * `data-`) to its normalized, camelCase form.
7358                *
7359                * Also there is special case for Moz prefix starting with upper case letter.
7360                *
7361                * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7362                *
7363                * @param {string} name Name to normalize
7364                */
7365               $normalize: directiveNormalize,
7366
7367
7368               /**
7369                * @ngdoc method
7370                * @name $compile.directive.Attributes#$addClass
7371                * @kind function
7372                *
7373                * @description
7374                * Adds the CSS class value specified by the classVal parameter to the element. If animations
7375                * are enabled then an animation will be triggered for the class addition.
7376                *
7377                * @param {string} classVal The className value that will be added to the element
7378                */
7379               $addClass: function(classVal) {
7380                 if (classVal && classVal.length > 0) {
7381                   $animate.addClass(this.$$element, classVal);
7382                 }
7383               },
7384
7385               /**
7386                * @ngdoc method
7387                * @name $compile.directive.Attributes#$removeClass
7388                * @kind function
7389                *
7390                * @description
7391                * Removes the CSS class value specified by the classVal parameter from the element. If
7392                * animations are enabled then an animation will be triggered for the class removal.
7393                *
7394                * @param {string} classVal The className value that will be removed from the element
7395                */
7396               $removeClass: function(classVal) {
7397                 if (classVal && classVal.length > 0) {
7398                   $animate.removeClass(this.$$element, classVal);
7399                 }
7400               },
7401
7402               /**
7403                * @ngdoc method
7404                * @name $compile.directive.Attributes#$updateClass
7405                * @kind function
7406                *
7407                * @description
7408                * Adds and removes the appropriate CSS class values to the element based on the difference
7409                * between the new and old CSS class values (specified as newClasses and oldClasses).
7410                *
7411                * @param {string} newClasses The current CSS className value
7412                * @param {string} oldClasses The former CSS className value
7413                */
7414               $updateClass: function(newClasses, oldClasses) {
7415                 var toAdd = tokenDifference(newClasses, oldClasses);
7416                 if (toAdd && toAdd.length) {
7417                   $animate.addClass(this.$$element, toAdd);
7418                 }
7419
7420                 var toRemove = tokenDifference(oldClasses, newClasses);
7421                 if (toRemove && toRemove.length) {
7422                   $animate.removeClass(this.$$element, toRemove);
7423                 }
7424               },
7425
7426               /**
7427                * Set a normalized attribute on the element in a way such that all directives
7428                * can share the attribute. This function properly handles boolean attributes.
7429                * @param {string} key Normalized key. (ie ngAttribute)
7430                * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7431                * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7432                *     Defaults to true.
7433                * @param {string=} attrName Optional none normalized name. Defaults to key.
7434                */
7435               $set: function(key, value, writeAttr, attrName) {
7436                 // TODO: decide whether or not to throw an error if "class"
7437                 //is set through this function since it may cause $updateClass to
7438                 //become unstable.
7439
7440                 var node = this.$$element[0],
7441                     booleanKey = getBooleanAttrName(node, key),
7442                     aliasedKey = getAliasedAttrName(key),
7443                     observer = key,
7444                     nodeName;
7445
7446                 if (booleanKey) {
7447                   this.$$element.prop(key, value);
7448                   attrName = booleanKey;
7449                 } else if (aliasedKey) {
7450                   this[aliasedKey] = value;
7451                   observer = aliasedKey;
7452                 }
7453
7454                 this[key] = value;
7455
7456                 // translate normalized key to actual key
7457                 if (attrName) {
7458                   this.$attr[key] = attrName;
7459                 } else {
7460                   attrName = this.$attr[key];
7461                   if (!attrName) {
7462                     this.$attr[key] = attrName = snake_case(key, '-');
7463                   }
7464                 }
7465
7466                 nodeName = nodeName_(this.$$element);
7467
7468                 if ((nodeName === 'a' && key === 'href') ||
7469                     (nodeName === 'img' && key === 'src')) {
7470                   // sanitize a[href] and img[src] values
7471                   this[key] = value = $$sanitizeUri(value, key === 'src');
7472                 } else if (nodeName === 'img' && key === 'srcset') {
7473                   // sanitize img[srcset] values
7474                   var result = "";
7475
7476                   // first check if there are spaces because it's not the same pattern
7477                   var trimmedSrcset = trim(value);
7478                   //                (   999x   ,|   999w   ,|   ,|,   )
7479                   var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7480                   var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7481
7482                   // split srcset into tuple of uri and descriptor except for the last item
7483                   var rawUris = trimmedSrcset.split(pattern);
7484
7485                   // for each tuples
7486                   var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7487                   for (var i = 0; i < nbrUrisWith2parts; i++) {
7488                     var innerIdx = i * 2;
7489                     // sanitize the uri
7490                     result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7491                     // add the descriptor
7492                     result += (" " + trim(rawUris[innerIdx + 1]));
7493                   }
7494
7495                   // split the last item into uri and descriptor
7496                   var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7497
7498                   // sanitize the last uri
7499                   result += $$sanitizeUri(trim(lastTuple[0]), true);
7500
7501                   // and add the last descriptor if any
7502                   if (lastTuple.length === 2) {
7503                     result += (" " + trim(lastTuple[1]));
7504                   }
7505                   this[key] = value = result;
7506                 }
7507
7508                 if (writeAttr !== false) {
7509                   if (value === null || isUndefined(value)) {
7510                     this.$$element.removeAttr(attrName);
7511                   } else {
7512                     this.$$element.attr(attrName, value);
7513                   }
7514                 }
7515
7516                 // fire observers
7517                 var $$observers = this.$$observers;
7518                 $$observers && forEach($$observers[observer], function(fn) {
7519                   try {
7520                     fn(value);
7521                   } catch (e) {
7522                     $exceptionHandler(e);
7523                   }
7524                 });
7525               },
7526
7527
7528               /**
7529                * @ngdoc method
7530                * @name $compile.directive.Attributes#$observe
7531                * @kind function
7532                *
7533                * @description
7534                * Observes an interpolated attribute.
7535                *
7536                * The observer function will be invoked once during the next `$digest` following
7537                * compilation. The observer is then invoked whenever the interpolated value
7538                * changes.
7539                *
7540                * @param {string} key Normalized key. (ie ngAttribute) .
7541                * @param {function(interpolatedValue)} fn Function that will be called whenever
7542                         the interpolated value of the attribute changes.
7543                *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7544                * @returns {function()} Returns a deregistration function for this observer.
7545                */
7546               $observe: function(key, fn) {
7547                 var attrs = this,
7548                     $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7549                     listeners = ($$observers[key] || ($$observers[key] = []));
7550
7551                 listeners.push(fn);
7552                 $rootScope.$evalAsync(function() {
7553                   if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7554                     // no one registered attribute interpolation function, so lets call it manually
7555                     fn(attrs[key]);
7556                   }
7557                 });
7558
7559                 return function() {
7560                   arrayRemove(listeners, fn);
7561                 };
7562               }
7563             };
7564
7565
7566             function safeAddClass($element, className) {
7567               try {
7568                 $element.addClass(className);
7569               } catch (e) {
7570                 // ignore, since it means that we are trying to set class on
7571                 // SVG element, where class name is read-only.
7572               }
7573             }
7574
7575
7576             var startSymbol = $interpolate.startSymbol(),
7577                 endSymbol = $interpolate.endSymbol(),
7578                 denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7579                     ? identity
7580                     : function denormalizeTemplate(template) {
7581                       return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7582                 },
7583                 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7584             var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7585
7586             compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7587               var bindings = $element.data('$binding') || [];
7588
7589               if (isArray(binding)) {
7590                 bindings = bindings.concat(binding);
7591               } else {
7592                 bindings.push(binding);
7593               }
7594
7595               $element.data('$binding', bindings);
7596             } : noop;
7597
7598             compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7599               safeAddClass($element, 'ng-binding');
7600             } : noop;
7601
7602             compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7603               var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7604               $element.data(dataName, scope);
7605             } : noop;
7606
7607             compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7608               safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7609             } : noop;
7610
7611             return compile;
7612
7613             //================================
7614
7615             function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7616                                 previousCompileContext) {
7617               if (!($compileNodes instanceof jqLite)) {
7618                 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7619                 // modify it.
7620                 $compileNodes = jqLite($compileNodes);
7621               }
7622               // We can not compile top level text elements since text nodes can be merged and we will
7623               // not be able to attach scope data to them, so we will wrap them in <span>
7624               forEach($compileNodes, function(node, index) {
7625                 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7626                   $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7627                 }
7628               });
7629               var compositeLinkFn =
7630                       compileNodes($compileNodes, transcludeFn, $compileNodes,
7631                                    maxPriority, ignoreDirective, previousCompileContext);
7632               compile.$$addScopeClass($compileNodes);
7633               var namespace = null;
7634               return function publicLinkFn(scope, cloneConnectFn, options) {
7635                 assertArg(scope, 'scope');
7636
7637                 if (previousCompileContext && previousCompileContext.needsNewScope) {
7638                   // A parent directive did a replace and a directive on this element asked
7639                   // for transclusion, which caused us to lose a layer of element on which
7640                   // we could hold the new transclusion scope, so we will create it manually
7641                   // here.
7642                   scope = scope.$parent.$new();
7643                 }
7644
7645                 options = options || {};
7646                 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7647                   transcludeControllers = options.transcludeControllers,
7648                   futureParentElement = options.futureParentElement;
7649
7650                 // When `parentBoundTranscludeFn` is passed, it is a
7651                 // `controllersBoundTransclude` function (it was previously passed
7652                 // as `transclude` to directive.link) so we must unwrap it to get
7653                 // its `boundTranscludeFn`
7654                 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7655                   parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7656                 }
7657
7658                 if (!namespace) {
7659                   namespace = detectNamespaceForChildElements(futureParentElement);
7660                 }
7661                 var $linkNode;
7662                 if (namespace !== 'html') {
7663                   // When using a directive with replace:true and templateUrl the $compileNodes
7664                   // (or a child element inside of them)
7665                   // might change, so we need to recreate the namespace adapted compileNodes
7666                   // for call to the link function.
7667                   // Note: This will already clone the nodes...
7668                   $linkNode = jqLite(
7669                     wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7670                   );
7671                 } else if (cloneConnectFn) {
7672                   // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7673                   // and sometimes changes the structure of the DOM.
7674                   $linkNode = JQLitePrototype.clone.call($compileNodes);
7675                 } else {
7676                   $linkNode = $compileNodes;
7677                 }
7678
7679                 if (transcludeControllers) {
7680                   for (var controllerName in transcludeControllers) {
7681                     $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7682                   }
7683                 }
7684
7685                 compile.$$addScopeInfo($linkNode, scope);
7686
7687                 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7688                 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7689                 return $linkNode;
7690               };
7691             }
7692
7693             function detectNamespaceForChildElements(parentElement) {
7694               // TODO: Make this detect MathML as well...
7695               var node = parentElement && parentElement[0];
7696               if (!node) {
7697                 return 'html';
7698               } else {
7699                 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7700               }
7701             }
7702
7703             /**
7704              * Compile function matches each node in nodeList against the directives. Once all directives
7705              * for a particular node are collected their compile functions are executed. The compile
7706              * functions return values - the linking functions - are combined into a composite linking
7707              * function, which is the a linking function for the node.
7708              *
7709              * @param {NodeList} nodeList an array of nodes or NodeList to compile
7710              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7711              *        scope argument is auto-generated to the new child of the transcluded parent scope.
7712              * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7713              *        the rootElement must be set the jqLite collection of the compile root. This is
7714              *        needed so that the jqLite collection items can be replaced with widgets.
7715              * @param {number=} maxPriority Max directive priority.
7716              * @returns {Function} A composite linking function of all of the matched directives or null.
7717              */
7718             function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7719                                     previousCompileContext) {
7720               var linkFns = [],
7721                   attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7722
7723               for (var i = 0; i < nodeList.length; i++) {
7724                 attrs = new Attributes();
7725
7726                 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7727                 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7728                                                 ignoreDirective);
7729
7730                 nodeLinkFn = (directives.length)
7731                     ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7732                                               null, [], [], previousCompileContext)
7733                     : null;
7734
7735                 if (nodeLinkFn && nodeLinkFn.scope) {
7736                   compile.$$addScopeClass(attrs.$$element);
7737                 }
7738
7739                 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7740                               !(childNodes = nodeList[i].childNodes) ||
7741                               !childNodes.length)
7742                     ? null
7743                     : compileNodes(childNodes,
7744                          nodeLinkFn ? (
7745                           (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7746                              && nodeLinkFn.transclude) : transcludeFn);
7747
7748                 if (nodeLinkFn || childLinkFn) {
7749                   linkFns.push(i, nodeLinkFn, childLinkFn);
7750                   linkFnFound = true;
7751                   nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7752                 }
7753
7754                 //use the previous context only for the first element in the virtual group
7755                 previousCompileContext = null;
7756               }
7757
7758               // return a linking function if we have found anything, null otherwise
7759               return linkFnFound ? compositeLinkFn : null;
7760
7761               function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7762                 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7763                 var stableNodeList;
7764
7765
7766                 if (nodeLinkFnFound) {
7767                   // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7768                   // offsets don't get screwed up
7769                   var nodeListLength = nodeList.length;
7770                   stableNodeList = new Array(nodeListLength);
7771
7772                   // create a sparse array by only copying the elements which have a linkFn
7773                   for (i = 0; i < linkFns.length; i+=3) {
7774                     idx = linkFns[i];
7775                     stableNodeList[idx] = nodeList[idx];
7776                   }
7777                 } else {
7778                   stableNodeList = nodeList;
7779                 }
7780
7781                 for (i = 0, ii = linkFns.length; i < ii;) {
7782                   node = stableNodeList[linkFns[i++]];
7783                   nodeLinkFn = linkFns[i++];
7784                   childLinkFn = linkFns[i++];
7785
7786                   if (nodeLinkFn) {
7787                     if (nodeLinkFn.scope) {
7788                       childScope = scope.$new();
7789                       compile.$$addScopeInfo(jqLite(node), childScope);
7790                     } else {
7791                       childScope = scope;
7792                     }
7793
7794                     if (nodeLinkFn.transcludeOnThisElement) {
7795                       childBoundTranscludeFn = createBoundTranscludeFn(
7796                           scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7797
7798                     } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7799                       childBoundTranscludeFn = parentBoundTranscludeFn;
7800
7801                     } else if (!parentBoundTranscludeFn && transcludeFn) {
7802                       childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7803
7804                     } else {
7805                       childBoundTranscludeFn = null;
7806                     }
7807
7808                     nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7809
7810                   } else if (childLinkFn) {
7811                     childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7812                   }
7813                 }
7814               }
7815             }
7816
7817             function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7818
7819               var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7820
7821                 if (!transcludedScope) {
7822                   transcludedScope = scope.$new(false, containingScope);
7823                   transcludedScope.$$transcluded = true;
7824                 }
7825
7826                 return transcludeFn(transcludedScope, cloneFn, {
7827                   parentBoundTranscludeFn: previousBoundTranscludeFn,
7828                   transcludeControllers: controllers,
7829                   futureParentElement: futureParentElement
7830                 });
7831               };
7832
7833               return boundTranscludeFn;
7834             }
7835
7836             /**
7837              * Looks for directives on the given node and adds them to the directive collection which is
7838              * sorted.
7839              *
7840              * @param node Node to search.
7841              * @param directives An array to which the directives are added to. This array is sorted before
7842              *        the function returns.
7843              * @param attrs The shared attrs object which is used to populate the normalized attributes.
7844              * @param {number=} maxPriority Max directive priority.
7845              */
7846             function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7847               var nodeType = node.nodeType,
7848                   attrsMap = attrs.$attr,
7849                   match,
7850                   className;
7851
7852               switch (nodeType) {
7853                 case NODE_TYPE_ELEMENT: /* Element */
7854                   // use the node name: <directive>
7855                   addDirective(directives,
7856                       directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7857
7858                   // iterate over the attributes
7859                   for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7860                            j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7861                     var attrStartName = false;
7862                     var attrEndName = false;
7863
7864                     attr = nAttrs[j];
7865                     name = attr.name;
7866                     value = trim(attr.value);
7867
7868                     // support ngAttr attribute binding
7869                     ngAttrName = directiveNormalize(name);
7870                     if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7871                       name = name.replace(PREFIX_REGEXP, '')
7872                         .substr(8).replace(/_(.)/g, function(match, letter) {
7873                           return letter.toUpperCase();
7874                         });
7875                     }
7876
7877                     var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7878                     if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7879                       attrStartName = name;
7880                       attrEndName = name.substr(0, name.length - 5) + 'end';
7881                       name = name.substr(0, name.length - 6);
7882                     }
7883
7884                     nName = directiveNormalize(name.toLowerCase());
7885                     attrsMap[nName] = name;
7886                     if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7887                         attrs[nName] = value;
7888                         if (getBooleanAttrName(node, nName)) {
7889                           attrs[nName] = true; // presence means true
7890                         }
7891                     }
7892                     addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7893                     addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7894                                   attrEndName);
7895                   }
7896
7897                   // use class as directive
7898                   className = node.className;
7899                   if (isObject(className)) {
7900                       // Maybe SVGAnimatedString
7901                       className = className.animVal;
7902                   }
7903                   if (isString(className) && className !== '') {
7904                     while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7905                       nName = directiveNormalize(match[2]);
7906                       if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7907                         attrs[nName] = trim(match[3]);
7908                       }
7909                       className = className.substr(match.index + match[0].length);
7910                     }
7911                   }
7912                   break;
7913                 case NODE_TYPE_TEXT: /* Text Node */
7914                   if (msie === 11) {
7915                     // Workaround for #11781
7916                     while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7917                       node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7918                       node.parentNode.removeChild(node.nextSibling);
7919                     }
7920                   }
7921                   addTextInterpolateDirective(directives, node.nodeValue);
7922                   break;
7923                 case NODE_TYPE_COMMENT: /* Comment */
7924                   try {
7925                     match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7926                     if (match) {
7927                       nName = directiveNormalize(match[1]);
7928                       if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7929                         attrs[nName] = trim(match[2]);
7930                       }
7931                     }
7932                   } catch (e) {
7933                     // turns out that under some circumstances IE9 throws errors when one attempts to read
7934                     // comment's node value.
7935                     // Just ignore it and continue. (Can't seem to reproduce in test case.)
7936                   }
7937                   break;
7938               }
7939
7940               directives.sort(byPriority);
7941               return directives;
7942             }
7943
7944             /**
7945              * Given a node with an directive-start it collects all of the siblings until it finds
7946              * directive-end.
7947              * @param node
7948              * @param attrStart
7949              * @param attrEnd
7950              * @returns {*}
7951              */
7952             function groupScan(node, attrStart, attrEnd) {
7953               var nodes = [];
7954               var depth = 0;
7955               if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7956                 do {
7957                   if (!node) {
7958                     throw $compileMinErr('uterdir',
7959                               "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7960                               attrStart, attrEnd);
7961                   }
7962                   if (node.nodeType == NODE_TYPE_ELEMENT) {
7963                     if (node.hasAttribute(attrStart)) depth++;
7964                     if (node.hasAttribute(attrEnd)) depth--;
7965                   }
7966                   nodes.push(node);
7967                   node = node.nextSibling;
7968                 } while (depth > 0);
7969               } else {
7970                 nodes.push(node);
7971               }
7972
7973               return jqLite(nodes);
7974             }
7975
7976             /**
7977              * Wrapper for linking function which converts normal linking function into a grouped
7978              * linking function.
7979              * @param linkFn
7980              * @param attrStart
7981              * @param attrEnd
7982              * @returns {Function}
7983              */
7984             function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7985               return function(scope, element, attrs, controllers, transcludeFn) {
7986                 element = groupScan(element[0], attrStart, attrEnd);
7987                 return linkFn(scope, element, attrs, controllers, transcludeFn);
7988               };
7989             }
7990
7991             /**
7992              * Once the directives have been collected, their compile functions are executed. This method
7993              * is responsible for inlining directive templates as well as terminating the application
7994              * of the directives if the terminal directive has been reached.
7995              *
7996              * @param {Array} directives Array of collected directives to execute their compile function.
7997              *        this needs to be pre-sorted by priority order.
7998              * @param {Node} compileNode The raw DOM node to apply the compile functions to
7999              * @param {Object} templateAttrs The shared attribute function
8000              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8001              *                                                  scope argument is auto-generated to the new
8002              *                                                  child of the transcluded parent scope.
8003              * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8004              *                              argument has the root jqLite array so that we can replace nodes
8005              *                              on it.
8006              * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8007              *                                           compiling the transclusion.
8008              * @param {Array.<Function>} preLinkFns
8009              * @param {Array.<Function>} postLinkFns
8010              * @param {Object} previousCompileContext Context used for previous compilation of the current
8011              *                                        node
8012              * @returns {Function} linkFn
8013              */
8014             function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8015                                            jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8016                                            previousCompileContext) {
8017               previousCompileContext = previousCompileContext || {};
8018
8019               var terminalPriority = -Number.MAX_VALUE,
8020                   newScopeDirective = previousCompileContext.newScopeDirective,
8021                   controllerDirectives = previousCompileContext.controllerDirectives,
8022                   newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8023                   templateDirective = previousCompileContext.templateDirective,
8024                   nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8025                   hasTranscludeDirective = false,
8026                   hasTemplate = false,
8027                   hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8028                   $compileNode = templateAttrs.$$element = jqLite(compileNode),
8029                   directive,
8030                   directiveName,
8031                   $template,
8032                   replaceDirective = originalReplaceDirective,
8033                   childTranscludeFn = transcludeFn,
8034                   linkFn,
8035                   directiveValue;
8036
8037               // executes all directives on the current element
8038               for (var i = 0, ii = directives.length; i < ii; i++) {
8039                 directive = directives[i];
8040                 var attrStart = directive.$$start;
8041                 var attrEnd = directive.$$end;
8042
8043                 // collect multiblock sections
8044                 if (attrStart) {
8045                   $compileNode = groupScan(compileNode, attrStart, attrEnd);
8046                 }
8047                 $template = undefined;
8048
8049                 if (terminalPriority > directive.priority) {
8050                   break; // prevent further processing of directives
8051                 }
8052
8053                 if (directiveValue = directive.scope) {
8054
8055                   // skip the check for directives with async templates, we'll check the derived sync
8056                   // directive when the template arrives
8057                   if (!directive.templateUrl) {
8058                     if (isObject(directiveValue)) {
8059                       // This directive is trying to add an isolated scope.
8060                       // Check that there is no scope of any kind already
8061                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8062                                         directive, $compileNode);
8063                       newIsolateScopeDirective = directive;
8064                     } else {
8065                       // This directive is trying to add a child scope.
8066                       // Check that there is no isolated scope already
8067                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8068                                         $compileNode);
8069                     }
8070                   }
8071
8072                   newScopeDirective = newScopeDirective || directive;
8073                 }
8074
8075                 directiveName = directive.name;
8076
8077                 if (!directive.templateUrl && directive.controller) {
8078                   directiveValue = directive.controller;
8079                   controllerDirectives = controllerDirectives || createMap();
8080                   assertNoDuplicate("'" + directiveName + "' controller",
8081                       controllerDirectives[directiveName], directive, $compileNode);
8082                   controllerDirectives[directiveName] = directive;
8083                 }
8084
8085                 if (directiveValue = directive.transclude) {
8086                   hasTranscludeDirective = true;
8087
8088                   // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8089                   // This option should only be used by directives that know how to safely handle element transclusion,
8090                   // where the transcluded nodes are added or replaced after linking.
8091                   if (!directive.$$tlb) {
8092                     assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8093                     nonTlbTranscludeDirective = directive;
8094                   }
8095
8096                   if (directiveValue == 'element') {
8097                     hasElementTranscludeDirective = true;
8098                     terminalPriority = directive.priority;
8099                     $template = $compileNode;
8100                     $compileNode = templateAttrs.$$element =
8101                         jqLite(document.createComment(' ' + directiveName + ': ' +
8102                                                       templateAttrs[directiveName] + ' '));
8103                     compileNode = $compileNode[0];
8104                     replaceWith(jqCollection, sliceArgs($template), compileNode);
8105
8106                     childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8107                                                 replaceDirective && replaceDirective.name, {
8108                                                   // Don't pass in:
8109                                                   // - controllerDirectives - otherwise we'll create duplicates controllers
8110                                                   // - newIsolateScopeDirective or templateDirective - combining templates with
8111                                                   //   element transclusion doesn't make sense.
8112                                                   //
8113                                                   // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8114                                                   // on the same element more than once.
8115                                                   nonTlbTranscludeDirective: nonTlbTranscludeDirective
8116                                                 });
8117                   } else {
8118                     $template = jqLite(jqLiteClone(compileNode)).contents();
8119                     $compileNode.empty(); // clear contents
8120                     childTranscludeFn = compile($template, transcludeFn, undefined,
8121                         undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8122                   }
8123                 }
8124
8125                 if (directive.template) {
8126                   hasTemplate = true;
8127                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8128                   templateDirective = directive;
8129
8130                   directiveValue = (isFunction(directive.template))
8131                       ? directive.template($compileNode, templateAttrs)
8132                       : directive.template;
8133
8134                   directiveValue = denormalizeTemplate(directiveValue);
8135
8136                   if (directive.replace) {
8137                     replaceDirective = directive;
8138                     if (jqLiteIsTextNode(directiveValue)) {
8139                       $template = [];
8140                     } else {
8141                       $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8142                     }
8143                     compileNode = $template[0];
8144
8145                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8146                       throw $compileMinErr('tplrt',
8147                           "Template for directive '{0}' must have exactly one root element. {1}",
8148                           directiveName, '');
8149                     }
8150
8151                     replaceWith(jqCollection, $compileNode, compileNode);
8152
8153                     var newTemplateAttrs = {$attr: {}};
8154
8155                     // combine directives from the original node and from the template:
8156                     // - take the array of directives for this element
8157                     // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8158                     // - collect directives from the template and sort them by priority
8159                     // - combine directives as: processed + template + unprocessed
8160                     var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8161                     var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8162
8163                     if (newIsolateScopeDirective || newScopeDirective) {
8164                       // The original directive caused the current element to be replaced but this element
8165                       // also needs to have a new scope, so we need to tell the template directives
8166                       // that they would need to get their scope from further up, if they require transclusion
8167                       markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8168                     }
8169                     directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8170                     mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8171
8172                     ii = directives.length;
8173                   } else {
8174                     $compileNode.html(directiveValue);
8175                   }
8176                 }
8177
8178                 if (directive.templateUrl) {
8179                   hasTemplate = true;
8180                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8181                   templateDirective = directive;
8182
8183                   if (directive.replace) {
8184                     replaceDirective = directive;
8185                   }
8186
8187                   nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8188                       templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8189                         controllerDirectives: controllerDirectives,
8190                         newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8191                         newIsolateScopeDirective: newIsolateScopeDirective,
8192                         templateDirective: templateDirective,
8193                         nonTlbTranscludeDirective: nonTlbTranscludeDirective
8194                       });
8195                   ii = directives.length;
8196                 } else if (directive.compile) {
8197                   try {
8198                     linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8199                     if (isFunction(linkFn)) {
8200                       addLinkFns(null, linkFn, attrStart, attrEnd);
8201                     } else if (linkFn) {
8202                       addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8203                     }
8204                   } catch (e) {
8205                     $exceptionHandler(e, startingTag($compileNode));
8206                   }
8207                 }
8208
8209                 if (directive.terminal) {
8210                   nodeLinkFn.terminal = true;
8211                   terminalPriority = Math.max(terminalPriority, directive.priority);
8212                 }
8213
8214               }
8215
8216               nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8217               nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8218               nodeLinkFn.templateOnThisElement = hasTemplate;
8219               nodeLinkFn.transclude = childTranscludeFn;
8220
8221               previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8222
8223               // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8224               return nodeLinkFn;
8225
8226               ////////////////////
8227
8228               function addLinkFns(pre, post, attrStart, attrEnd) {
8229                 if (pre) {
8230                   if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8231                   pre.require = directive.require;
8232                   pre.directiveName = directiveName;
8233                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8234                     pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8235                   }
8236                   preLinkFns.push(pre);
8237                 }
8238                 if (post) {
8239                   if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8240                   post.require = directive.require;
8241                   post.directiveName = directiveName;
8242                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8243                     post = cloneAndAnnotateFn(post, {isolateScope: true});
8244                   }
8245                   postLinkFns.push(post);
8246                 }
8247               }
8248
8249
8250               function getControllers(directiveName, require, $element, elementControllers) {
8251                 var value;
8252
8253                 if (isString(require)) {
8254                   var match = require.match(REQUIRE_PREFIX_REGEXP);
8255                   var name = require.substring(match[0].length);
8256                   var inheritType = match[1] || match[3];
8257                   var optional = match[2] === '?';
8258
8259                   //If only parents then start at the parent element
8260                   if (inheritType === '^^') {
8261                     $element = $element.parent();
8262                   //Otherwise attempt getting the controller from elementControllers in case
8263                   //the element is transcluded (and has no data) and to avoid .data if possible
8264                   } else {
8265                     value = elementControllers && elementControllers[name];
8266                     value = value && value.instance;
8267                   }
8268
8269                   if (!value) {
8270                     var dataName = '$' + name + 'Controller';
8271                     value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8272                   }
8273
8274                   if (!value && !optional) {
8275                     throw $compileMinErr('ctreq',
8276                         "Controller '{0}', required by directive '{1}', can't be found!",
8277                         name, directiveName);
8278                   }
8279                 } else if (isArray(require)) {
8280                   value = [];
8281                   for (var i = 0, ii = require.length; i < ii; i++) {
8282                     value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8283                   }
8284                 }
8285
8286                 return value || null;
8287               }
8288
8289               function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8290                 var elementControllers = createMap();
8291                 for (var controllerKey in controllerDirectives) {
8292                   var directive = controllerDirectives[controllerKey];
8293                   var locals = {
8294                     $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8295                     $element: $element,
8296                     $attrs: attrs,
8297                     $transclude: transcludeFn
8298                   };
8299
8300                   var controller = directive.controller;
8301                   if (controller == '@') {
8302                     controller = attrs[directive.name];
8303                   }
8304
8305                   var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8306
8307                   // For directives with element transclusion the element is a comment,
8308                   // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8309                   // clean up (http://bugs.jquery.com/ticket/8335).
8310                   // Instead, we save the controllers for the element in a local hash and attach to .data
8311                   // later, once we have the actual element.
8312                   elementControllers[directive.name] = controllerInstance;
8313                   if (!hasElementTranscludeDirective) {
8314                     $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8315                   }
8316                 }
8317                 return elementControllers;
8318               }
8319
8320               function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8321                 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8322                     attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8323
8324                 if (compileNode === linkNode) {
8325                   attrs = templateAttrs;
8326                   $element = templateAttrs.$$element;
8327                 } else {
8328                   $element = jqLite(linkNode);
8329                   attrs = new Attributes($element, templateAttrs);
8330                 }
8331
8332                 controllerScope = scope;
8333                 if (newIsolateScopeDirective) {
8334                   isolateScope = scope.$new(true);
8335                 } else if (newScopeDirective) {
8336                   controllerScope = scope.$parent;
8337                 }
8338
8339                 if (boundTranscludeFn) {
8340                   // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8341                   // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8342                   transcludeFn = controllersBoundTransclude;
8343                   transcludeFn.$$boundTransclude = boundTranscludeFn;
8344                 }
8345
8346                 if (controllerDirectives) {
8347                   elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8348                 }
8349
8350                 if (newIsolateScopeDirective) {
8351                   // Initialize isolate scope bindings for new isolate scope directive.
8352                   compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8353                       templateDirective === newIsolateScopeDirective.$$originalDirective)));
8354                   compile.$$addScopeClass($element, true);
8355                   isolateScope.$$isolateBindings =
8356                       newIsolateScopeDirective.$$isolateBindings;
8357                   removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8358                                                 isolateScope.$$isolateBindings,
8359                                                 newIsolateScopeDirective);
8360                   if (removeScopeBindingWatches) {
8361                     isolateScope.$on('$destroy', removeScopeBindingWatches);
8362                   }
8363                 }
8364
8365                 // Initialize bindToController bindings
8366                 for (var name in elementControllers) {
8367                   var controllerDirective = controllerDirectives[name];
8368                   var controller = elementControllers[name];
8369                   var bindings = controllerDirective.$$bindings.bindToController;
8370
8371                   if (controller.identifier && bindings) {
8372                     removeControllerBindingWatches =
8373                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8374                   }
8375
8376                   var controllerResult = controller();
8377                   if (controllerResult !== controller.instance) {
8378                     // If the controller constructor has a return value, overwrite the instance
8379                     // from setupControllers
8380                     controller.instance = controllerResult;
8381                     $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8382                     removeControllerBindingWatches && removeControllerBindingWatches();
8383                     removeControllerBindingWatches =
8384                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8385                   }
8386                 }
8387
8388                 // PRELINKING
8389                 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8390                   linkFn = preLinkFns[i];
8391                   invokeLinkFn(linkFn,
8392                       linkFn.isolateScope ? isolateScope : scope,
8393                       $element,
8394                       attrs,
8395                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8396                       transcludeFn
8397                   );
8398                 }
8399
8400                 // RECURSION
8401                 // We only pass the isolate scope, if the isolate directive has a template,
8402                 // otherwise the child elements do not belong to the isolate directive.
8403                 var scopeToChild = scope;
8404                 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8405                   scopeToChild = isolateScope;
8406                 }
8407                 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8408
8409                 // POSTLINKING
8410                 for (i = postLinkFns.length - 1; i >= 0; i--) {
8411                   linkFn = postLinkFns[i];
8412                   invokeLinkFn(linkFn,
8413                       linkFn.isolateScope ? isolateScope : scope,
8414                       $element,
8415                       attrs,
8416                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8417                       transcludeFn
8418                   );
8419                 }
8420
8421                 // This is the function that is injected as `$transclude`.
8422                 // Note: all arguments are optional!
8423                 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8424                   var transcludeControllers;
8425
8426                   // No scope passed in:
8427                   if (!isScope(scope)) {
8428                     futureParentElement = cloneAttachFn;
8429                     cloneAttachFn = scope;
8430                     scope = undefined;
8431                   }
8432
8433                   if (hasElementTranscludeDirective) {
8434                     transcludeControllers = elementControllers;
8435                   }
8436                   if (!futureParentElement) {
8437                     futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8438                   }
8439                   return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8440                 }
8441               }
8442             }
8443
8444             // Depending upon the context in which a directive finds itself it might need to have a new isolated
8445             // or child scope created. For instance:
8446             // * if the directive has been pulled into a template because another directive with a higher priority
8447             // asked for element transclusion
8448             // * if the directive itself asks for transclusion but it is at the root of a template and the original
8449             // element was replaced. See https://github.com/angular/angular.js/issues/12936
8450             function markDirectiveScope(directives, isolateScope, newScope) {
8451               for (var j = 0, jj = directives.length; j < jj; j++) {
8452                 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8453               }
8454             }
8455
8456             /**
8457              * looks up the directive and decorates it with exception handling and proper parameters. We
8458              * call this the boundDirective.
8459              *
8460              * @param {string} name name of the directive to look up.
8461              * @param {string} location The directive must be found in specific format.
8462              *   String containing any of theses characters:
8463              *
8464              *   * `E`: element name
8465              *   * `A': attribute
8466              *   * `C`: class
8467              *   * `M`: comment
8468              * @returns {boolean} true if directive was added.
8469              */
8470             function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8471                                   endAttrName) {
8472               if (name === ignoreDirective) return null;
8473               var match = null;
8474               if (hasDirectives.hasOwnProperty(name)) {
8475                 for (var directive, directives = $injector.get(name + Suffix),
8476                     i = 0, ii = directives.length; i < ii; i++) {
8477                   try {
8478                     directive = directives[i];
8479                     if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8480                          directive.restrict.indexOf(location) != -1) {
8481                       if (startAttrName) {
8482                         directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8483                       }
8484                       tDirectives.push(directive);
8485                       match = directive;
8486                     }
8487                   } catch (e) { $exceptionHandler(e); }
8488                 }
8489               }
8490               return match;
8491             }
8492
8493
8494             /**
8495              * looks up the directive and returns true if it is a multi-element directive,
8496              * and therefore requires DOM nodes between -start and -end markers to be grouped
8497              * together.
8498              *
8499              * @param {string} name name of the directive to look up.
8500              * @returns true if directive was registered as multi-element.
8501              */
8502             function directiveIsMultiElement(name) {
8503               if (hasDirectives.hasOwnProperty(name)) {
8504                 for (var directive, directives = $injector.get(name + Suffix),
8505                     i = 0, ii = directives.length; i < ii; i++) {
8506                   directive = directives[i];
8507                   if (directive.multiElement) {
8508                     return true;
8509                   }
8510                 }
8511               }
8512               return false;
8513             }
8514
8515             /**
8516              * When the element is replaced with HTML template then the new attributes
8517              * on the template need to be merged with the existing attributes in the DOM.
8518              * The desired effect is to have both of the attributes present.
8519              *
8520              * @param {object} dst destination attributes (original DOM)
8521              * @param {object} src source attributes (from the directive template)
8522              */
8523             function mergeTemplateAttributes(dst, src) {
8524               var srcAttr = src.$attr,
8525                   dstAttr = dst.$attr,
8526                   $element = dst.$$element;
8527
8528               // reapply the old attributes to the new element
8529               forEach(dst, function(value, key) {
8530                 if (key.charAt(0) != '$') {
8531                   if (src[key] && src[key] !== value) {
8532                     value += (key === 'style' ? ';' : ' ') + src[key];
8533                   }
8534                   dst.$set(key, value, true, srcAttr[key]);
8535                 }
8536               });
8537
8538               // copy the new attributes on the old attrs object
8539               forEach(src, function(value, key) {
8540                 if (key == 'class') {
8541                   safeAddClass($element, value);
8542                   dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8543                 } else if (key == 'style') {
8544                   $element.attr('style', $element.attr('style') + ';' + value);
8545                   dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8546                   // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8547                   // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8548                   // have an attribute like "has-own-property" or "data-has-own-property", etc.
8549                 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8550                   dst[key] = value;
8551                   dstAttr[key] = srcAttr[key];
8552                 }
8553               });
8554             }
8555
8556
8557             function compileTemplateUrl(directives, $compileNode, tAttrs,
8558                 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8559               var linkQueue = [],
8560                   afterTemplateNodeLinkFn,
8561                   afterTemplateChildLinkFn,
8562                   beforeTemplateCompileNode = $compileNode[0],
8563                   origAsyncDirective = directives.shift(),
8564                   derivedSyncDirective = inherit(origAsyncDirective, {
8565                     templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8566                   }),
8567                   templateUrl = (isFunction(origAsyncDirective.templateUrl))
8568                       ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8569                       : origAsyncDirective.templateUrl,
8570                   templateNamespace = origAsyncDirective.templateNamespace;
8571
8572               $compileNode.empty();
8573
8574               $templateRequest(templateUrl)
8575                 .then(function(content) {
8576                   var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8577
8578                   content = denormalizeTemplate(content);
8579
8580                   if (origAsyncDirective.replace) {
8581                     if (jqLiteIsTextNode(content)) {
8582                       $template = [];
8583                     } else {
8584                       $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8585                     }
8586                     compileNode = $template[0];
8587
8588                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8589                       throw $compileMinErr('tplrt',
8590                           "Template for directive '{0}' must have exactly one root element. {1}",
8591                           origAsyncDirective.name, templateUrl);
8592                     }
8593
8594                     tempTemplateAttrs = {$attr: {}};
8595                     replaceWith($rootElement, $compileNode, compileNode);
8596                     var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8597
8598                     if (isObject(origAsyncDirective.scope)) {
8599                       // the original directive that caused the template to be loaded async required
8600                       // an isolate scope
8601                       markDirectiveScope(templateDirectives, true);
8602                     }
8603                     directives = templateDirectives.concat(directives);
8604                     mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8605                   } else {
8606                     compileNode = beforeTemplateCompileNode;
8607                     $compileNode.html(content);
8608                   }
8609
8610                   directives.unshift(derivedSyncDirective);
8611
8612                   afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8613                       childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8614                       previousCompileContext);
8615                   forEach($rootElement, function(node, i) {
8616                     if (node == compileNode) {
8617                       $rootElement[i] = $compileNode[0];
8618                     }
8619                   });
8620                   afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8621
8622                   while (linkQueue.length) {
8623                     var scope = linkQueue.shift(),
8624                         beforeTemplateLinkNode = linkQueue.shift(),
8625                         linkRootElement = linkQueue.shift(),
8626                         boundTranscludeFn = linkQueue.shift(),
8627                         linkNode = $compileNode[0];
8628
8629                     if (scope.$$destroyed) continue;
8630
8631                     if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8632                       var oldClasses = beforeTemplateLinkNode.className;
8633
8634                       if (!(previousCompileContext.hasElementTranscludeDirective &&
8635                           origAsyncDirective.replace)) {
8636                         // it was cloned therefore we have to clone as well.
8637                         linkNode = jqLiteClone(compileNode);
8638                       }
8639                       replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8640
8641                       // Copy in CSS classes from original node
8642                       safeAddClass(jqLite(linkNode), oldClasses);
8643                     }
8644                     if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8645                       childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8646                     } else {
8647                       childBoundTranscludeFn = boundTranscludeFn;
8648                     }
8649                     afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8650                       childBoundTranscludeFn);
8651                   }
8652                   linkQueue = null;
8653                 });
8654
8655               return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8656                 var childBoundTranscludeFn = boundTranscludeFn;
8657                 if (scope.$$destroyed) return;
8658                 if (linkQueue) {
8659                   linkQueue.push(scope,
8660                                  node,
8661                                  rootElement,
8662                                  childBoundTranscludeFn);
8663                 } else {
8664                   if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8665                     childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8666                   }
8667                   afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8668                 }
8669               };
8670             }
8671
8672
8673             /**
8674              * Sorting function for bound directives.
8675              */
8676             function byPriority(a, b) {
8677               var diff = b.priority - a.priority;
8678               if (diff !== 0) return diff;
8679               if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8680               return a.index - b.index;
8681             }
8682
8683             function assertNoDuplicate(what, previousDirective, directive, element) {
8684
8685               function wrapModuleNameIfDefined(moduleName) {
8686                 return moduleName ?
8687                   (' (module: ' + moduleName + ')') :
8688                   '';
8689               }
8690
8691               if (previousDirective) {
8692                 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8693                     previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8694                     directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8695               }
8696             }
8697
8698
8699             function addTextInterpolateDirective(directives, text) {
8700               var interpolateFn = $interpolate(text, true);
8701               if (interpolateFn) {
8702                 directives.push({
8703                   priority: 0,
8704                   compile: function textInterpolateCompileFn(templateNode) {
8705                     var templateNodeParent = templateNode.parent(),
8706                         hasCompileParent = !!templateNodeParent.length;
8707
8708                     // When transcluding a template that has bindings in the root
8709                     // we don't have a parent and thus need to add the class during linking fn.
8710                     if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8711
8712                     return function textInterpolateLinkFn(scope, node) {
8713                       var parent = node.parent();
8714                       if (!hasCompileParent) compile.$$addBindingClass(parent);
8715                       compile.$$addBindingInfo(parent, interpolateFn.expressions);
8716                       scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8717                         node[0].nodeValue = value;
8718                       });
8719                     };
8720                   }
8721                 });
8722               }
8723             }
8724
8725
8726             function wrapTemplate(type, template) {
8727               type = lowercase(type || 'html');
8728               switch (type) {
8729               case 'svg':
8730               case 'math':
8731                 var wrapper = document.createElement('div');
8732                 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8733                 return wrapper.childNodes[0].childNodes;
8734               default:
8735                 return template;
8736               }
8737             }
8738
8739
8740             function getTrustedContext(node, attrNormalizedName) {
8741               if (attrNormalizedName == "srcdoc") {
8742                 return $sce.HTML;
8743               }
8744               var tag = nodeName_(node);
8745               // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8746               if (attrNormalizedName == "xlinkHref" ||
8747                   (tag == "form" && attrNormalizedName == "action") ||
8748                   (tag != "img" && (attrNormalizedName == "src" ||
8749                                     attrNormalizedName == "ngSrc"))) {
8750                 return $sce.RESOURCE_URL;
8751               }
8752             }
8753
8754
8755             function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8756               var trustedContext = getTrustedContext(node, name);
8757               allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8758
8759               var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8760
8761               // no interpolation found -> ignore
8762               if (!interpolateFn) return;
8763
8764
8765               if (name === "multiple" && nodeName_(node) === "select") {
8766                 throw $compileMinErr("selmulti",
8767                     "Binding to the 'multiple' attribute is not supported. Element: {0}",
8768                     startingTag(node));
8769               }
8770
8771               directives.push({
8772                 priority: 100,
8773                 compile: function() {
8774                     return {
8775                       pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8776                         var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8777
8778                         if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8779                           throw $compileMinErr('nodomevents',
8780                               "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8781                                   "ng- versions (such as ng-click instead of onclick) instead.");
8782                         }
8783
8784                         // If the attribute has changed since last $interpolate()ed
8785                         var newValue = attr[name];
8786                         if (newValue !== value) {
8787                           // we need to interpolate again since the attribute value has been updated
8788                           // (e.g. by another directive's compile function)
8789                           // ensure unset/empty values make interpolateFn falsy
8790                           interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8791                           value = newValue;
8792                         }
8793
8794                         // if attribute was updated so that there is no interpolation going on we don't want to
8795                         // register any observers
8796                         if (!interpolateFn) return;
8797
8798                         // initialize attr object so that it's ready in case we need the value for isolate
8799                         // scope initialization, otherwise the value would not be available from isolate
8800                         // directive's linking fn during linking phase
8801                         attr[name] = interpolateFn(scope);
8802
8803                         ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8804                         (attr.$$observers && attr.$$observers[name].$$scope || scope).
8805                           $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8806                             //special case for class attribute addition + removal
8807                             //so that class changes can tap into the animation
8808                             //hooks provided by the $animate service. Be sure to
8809                             //skip animations when the first digest occurs (when
8810                             //both the new and the old values are the same) since
8811                             //the CSS classes are the non-interpolated values
8812                             if (name === 'class' && newValue != oldValue) {
8813                               attr.$updateClass(newValue, oldValue);
8814                             } else {
8815                               attr.$set(name, newValue);
8816                             }
8817                           });
8818                       }
8819                     };
8820                   }
8821               });
8822             }
8823
8824
8825             /**
8826              * This is a special jqLite.replaceWith, which can replace items which
8827              * have no parents, provided that the containing jqLite collection is provided.
8828              *
8829              * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8830              *                               in the root of the tree.
8831              * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8832              *                                  the shell, but replace its DOM node reference.
8833              * @param {Node} newNode The new DOM node.
8834              */
8835             function replaceWith($rootElement, elementsToRemove, newNode) {
8836               var firstElementToRemove = elementsToRemove[0],
8837                   removeCount = elementsToRemove.length,
8838                   parent = firstElementToRemove.parentNode,
8839                   i, ii;
8840
8841               if ($rootElement) {
8842                 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8843                   if ($rootElement[i] == firstElementToRemove) {
8844                     $rootElement[i++] = newNode;
8845                     for (var j = i, j2 = j + removeCount - 1,
8846                              jj = $rootElement.length;
8847                          j < jj; j++, j2++) {
8848                       if (j2 < jj) {
8849                         $rootElement[j] = $rootElement[j2];
8850                       } else {
8851                         delete $rootElement[j];
8852                       }
8853                     }
8854                     $rootElement.length -= removeCount - 1;
8855
8856                     // If the replaced element is also the jQuery .context then replace it
8857                     // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8858                     // http://api.jquery.com/context/
8859                     if ($rootElement.context === firstElementToRemove) {
8860                       $rootElement.context = newNode;
8861                     }
8862                     break;
8863                   }
8864                 }
8865               }
8866
8867               if (parent) {
8868                 parent.replaceChild(newNode, firstElementToRemove);
8869               }
8870
8871               // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8872               var fragment = document.createDocumentFragment();
8873               fragment.appendChild(firstElementToRemove);
8874
8875               if (jqLite.hasData(firstElementToRemove)) {
8876                 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8877                 // data here because there's no public interface in jQuery to do that and copying over
8878                 // event listeners (which is the main use of private data) wouldn't work anyway.
8879                 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8880
8881                 // Remove data of the replaced element. We cannot just call .remove()
8882                 // on the element it since that would deallocate scope that is needed
8883                 // for the new node. Instead, remove the data "manually".
8884                 if (!jQuery) {
8885                   delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8886                 } else {
8887                   // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8888                   // the replaced element. The cleanData version monkey-patched by Angular would cause
8889                   // the scope to be trashed and we do need the very same scope to work with the new
8890                   // element. However, we cannot just cache the non-patched version and use it here as
8891                   // that would break if another library patches the method after Angular does (one
8892                   // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8893                   // skipped this one time.
8894                   skipDestroyOnNextJQueryCleanData = true;
8895                   jQuery.cleanData([firstElementToRemove]);
8896                 }
8897               }
8898
8899               for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8900                 var element = elementsToRemove[k];
8901                 jqLite(element).remove(); // must do this way to clean up expando
8902                 fragment.appendChild(element);
8903                 delete elementsToRemove[k];
8904               }
8905
8906               elementsToRemove[0] = newNode;
8907               elementsToRemove.length = 1;
8908             }
8909
8910
8911             function cloneAndAnnotateFn(fn, annotation) {
8912               return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8913             }
8914
8915
8916             function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8917               try {
8918                 linkFn(scope, $element, attrs, controllers, transcludeFn);
8919               } catch (e) {
8920                 $exceptionHandler(e, startingTag($element));
8921               }
8922             }
8923
8924
8925             // Set up $watches for isolate scope and controller bindings. This process
8926             // only occurs for isolate scopes and new scopes with controllerAs.
8927             function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8928               var removeWatchCollection = [];
8929               forEach(bindings, function(definition, scopeName) {
8930                 var attrName = definition.attrName,
8931                 optional = definition.optional,
8932                 mode = definition.mode, // @, =, or &
8933                 lastValue,
8934                 parentGet, parentSet, compare;
8935
8936                 switch (mode) {
8937
8938                   case '@':
8939                     if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8940                       destination[scopeName] = attrs[attrName] = void 0;
8941                     }
8942                     attrs.$observe(attrName, function(value) {
8943                       if (isString(value)) {
8944                         destination[scopeName] = value;
8945                       }
8946                     });
8947                     attrs.$$observers[attrName].$$scope = scope;
8948                     if (isString(attrs[attrName])) {
8949                       // If the attribute has been provided then we trigger an interpolation to ensure
8950                       // the value is there for use in the link fn
8951                       destination[scopeName] = $interpolate(attrs[attrName])(scope);
8952                     }
8953                     break;
8954
8955                   case '=':
8956                     if (!hasOwnProperty.call(attrs, attrName)) {
8957                       if (optional) break;
8958                       attrs[attrName] = void 0;
8959                     }
8960                     if (optional && !attrs[attrName]) break;
8961
8962                     parentGet = $parse(attrs[attrName]);
8963                     if (parentGet.literal) {
8964                       compare = equals;
8965                     } else {
8966                       compare = function(a, b) { return a === b || (a !== a && b !== b); };
8967                     }
8968                     parentSet = parentGet.assign || function() {
8969                       // reset the change, or we will throw this exception on every $digest
8970                       lastValue = destination[scopeName] = parentGet(scope);
8971                       throw $compileMinErr('nonassign',
8972                           "Expression '{0}' used with directive '{1}' is non-assignable!",
8973                           attrs[attrName], directive.name);
8974                     };
8975                     lastValue = destination[scopeName] = parentGet(scope);
8976                     var parentValueWatch = function parentValueWatch(parentValue) {
8977                       if (!compare(parentValue, destination[scopeName])) {
8978                         // we are out of sync and need to copy
8979                         if (!compare(parentValue, lastValue)) {
8980                           // parent changed and it has precedence
8981                           destination[scopeName] = parentValue;
8982                         } else {
8983                           // if the parent can be assigned then do so
8984                           parentSet(scope, parentValue = destination[scopeName]);
8985                         }
8986                       }
8987                       return lastValue = parentValue;
8988                     };
8989                     parentValueWatch.$stateful = true;
8990                     var removeWatch;
8991                     if (definition.collection) {
8992                       removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8993                     } else {
8994                       removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8995                     }
8996                     removeWatchCollection.push(removeWatch);
8997                     break;
8998
8999                   case '&':
9000                     // Don't assign Object.prototype method to scope
9001                     parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9002
9003                     // Don't assign noop to destination if expression is not valid
9004                     if (parentGet === noop && optional) break;
9005
9006                     destination[scopeName] = function(locals) {
9007                       return parentGet(scope, locals);
9008                     };
9009                     break;
9010                 }
9011               });
9012
9013               return removeWatchCollection.length && function removeWatches() {
9014                 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9015                   removeWatchCollection[i]();
9016                 }
9017               };
9018             }
9019           }];
9020         }
9021
9022         var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9023         /**
9024          * Converts all accepted directives format into proper directive name.
9025          * @param name Name to normalize
9026          */
9027         function directiveNormalize(name) {
9028           return camelCase(name.replace(PREFIX_REGEXP, ''));
9029         }
9030
9031         /**
9032          * @ngdoc type
9033          * @name $compile.directive.Attributes
9034          *
9035          * @description
9036          * A shared object between directive compile / linking functions which contains normalized DOM
9037          * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9038          * needed since all of these are treated as equivalent in Angular:
9039          *
9040          * ```
9041          *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9042          * ```
9043          */
9044
9045         /**
9046          * @ngdoc property
9047          * @name $compile.directive.Attributes#$attr
9048          *
9049          * @description
9050          * A map of DOM element attribute names to the normalized name. This is
9051          * needed to do reverse lookup from normalized name back to actual name.
9052          */
9053
9054
9055         /**
9056          * @ngdoc method
9057          * @name $compile.directive.Attributes#$set
9058          * @kind function
9059          *
9060          * @description
9061          * Set DOM element attribute value.
9062          *
9063          *
9064          * @param {string} name Normalized element attribute name of the property to modify. The name is
9065          *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9066          *          property to the original name.
9067          * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9068          */
9069
9070
9071
9072         /**
9073          * Closure compiler type information
9074          */
9075
9076         function nodesetLinkingFn(
9077           /* angular.Scope */ scope,
9078           /* NodeList */ nodeList,
9079           /* Element */ rootElement,
9080           /* function(Function) */ boundTranscludeFn
9081         ) {}
9082
9083         function directiveLinkingFn(
9084           /* nodesetLinkingFn */ nodesetLinkingFn,
9085           /* angular.Scope */ scope,
9086           /* Node */ node,
9087           /* Element */ rootElement,
9088           /* function(Function) */ boundTranscludeFn
9089         ) {}
9090
9091         function tokenDifference(str1, str2) {
9092           var values = '',
9093               tokens1 = str1.split(/\s+/),
9094               tokens2 = str2.split(/\s+/);
9095
9096           outer:
9097           for (var i = 0; i < tokens1.length; i++) {
9098             var token = tokens1[i];
9099             for (var j = 0; j < tokens2.length; j++) {
9100               if (token == tokens2[j]) continue outer;
9101             }
9102             values += (values.length > 0 ? ' ' : '') + token;
9103           }
9104           return values;
9105         }
9106
9107         function removeComments(jqNodes) {
9108           jqNodes = jqLite(jqNodes);
9109           var i = jqNodes.length;
9110
9111           if (i <= 1) {
9112             return jqNodes;
9113           }
9114
9115           while (i--) {
9116             var node = jqNodes[i];
9117             if (node.nodeType === NODE_TYPE_COMMENT) {
9118               splice.call(jqNodes, i, 1);
9119             }
9120           }
9121           return jqNodes;
9122         }
9123
9124         var $controllerMinErr = minErr('$controller');
9125
9126
9127         var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9128         function identifierForController(controller, ident) {
9129           if (ident && isString(ident)) return ident;
9130           if (isString(controller)) {
9131             var match = CNTRL_REG.exec(controller);
9132             if (match) return match[3];
9133           }
9134         }
9135
9136
9137         /**
9138          * @ngdoc provider
9139          * @name $controllerProvider
9140          * @description
9141          * The {@link ng.$controller $controller service} is used by Angular to create new
9142          * controllers.
9143          *
9144          * This provider allows controller registration via the
9145          * {@link ng.$controllerProvider#register register} method.
9146          */
9147         function $ControllerProvider() {
9148           var controllers = {},
9149               globals = false;
9150
9151           /**
9152            * @ngdoc method
9153            * @name $controllerProvider#register
9154            * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9155            *    the names and the values are the constructors.
9156            * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9157            *    annotations in the array notation).
9158            */
9159           this.register = function(name, constructor) {
9160             assertNotHasOwnProperty(name, 'controller');
9161             if (isObject(name)) {
9162               extend(controllers, name);
9163             } else {
9164               controllers[name] = constructor;
9165             }
9166           };
9167
9168           /**
9169            * @ngdoc method
9170            * @name $controllerProvider#allowGlobals
9171            * @description If called, allows `$controller` to find controller constructors on `window`
9172            */
9173           this.allowGlobals = function() {
9174             globals = true;
9175           };
9176
9177
9178           this.$get = ['$injector', '$window', function($injector, $window) {
9179
9180             /**
9181              * @ngdoc service
9182              * @name $controller
9183              * @requires $injector
9184              *
9185              * @param {Function|string} constructor If called with a function then it's considered to be the
9186              *    controller constructor function. Otherwise it's considered to be a string which is used
9187              *    to retrieve the controller constructor using the following steps:
9188              *
9189              *    * check if a controller with given name is registered via `$controllerProvider`
9190              *    * check if evaluating the string on the current scope returns a constructor
9191              *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9192              *      `window` object (not recommended)
9193              *
9194              *    The string can use the `controller as property` syntax, where the controller instance is published
9195              *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9196              *    to work correctly.
9197              *
9198              * @param {Object} locals Injection locals for Controller.
9199              * @return {Object} Instance of given controller.
9200              *
9201              * @description
9202              * `$controller` service is responsible for instantiating controllers.
9203              *
9204              * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9205              * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9206              */
9207             return function(expression, locals, later, ident) {
9208               // PRIVATE API:
9209               //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9210               //                     If true, $controller will allocate the object with the correct
9211               //                     prototype chain, but will not invoke the controller until a returned
9212               //                     callback is invoked.
9213               //   param `ident` --- An optional label which overrides the label parsed from the controller
9214               //                     expression, if any.
9215               var instance, match, constructor, identifier;
9216               later = later === true;
9217               if (ident && isString(ident)) {
9218                 identifier = ident;
9219               }
9220
9221               if (isString(expression)) {
9222                 match = expression.match(CNTRL_REG);
9223                 if (!match) {
9224                   throw $controllerMinErr('ctrlfmt',
9225                     "Badly formed controller string '{0}'. " +
9226                     "Must match `__name__ as __id__` or `__name__`.", expression);
9227                 }
9228                 constructor = match[1],
9229                 identifier = identifier || match[3];
9230                 expression = controllers.hasOwnProperty(constructor)
9231                     ? controllers[constructor]
9232                     : getter(locals.$scope, constructor, true) ||
9233                         (globals ? getter($window, constructor, true) : undefined);
9234
9235                 assertArgFn(expression, constructor, true);
9236               }
9237
9238               if (later) {
9239                 // Instantiate controller later:
9240                 // This machinery is used to create an instance of the object before calling the
9241                 // controller's constructor itself.
9242                 //
9243                 // This allows properties to be added to the controller before the constructor is
9244                 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9245                 //
9246                 // This feature is not intended for use by applications, and is thus not documented
9247                 // publicly.
9248                 // Object creation: http://jsperf.com/create-constructor/2
9249                 var controllerPrototype = (isArray(expression) ?
9250                   expression[expression.length - 1] : expression).prototype;
9251                 instance = Object.create(controllerPrototype || null);
9252
9253                 if (identifier) {
9254                   addIdentifier(locals, identifier, instance, constructor || expression.name);
9255                 }
9256
9257                 var instantiate;
9258                 return instantiate = extend(function() {
9259                   var result = $injector.invoke(expression, instance, locals, constructor);
9260                   if (result !== instance && (isObject(result) || isFunction(result))) {
9261                     instance = result;
9262                     if (identifier) {
9263                       // If result changed, re-assign controllerAs value to scope.
9264                       addIdentifier(locals, identifier, instance, constructor || expression.name);
9265                     }
9266                   }
9267                   return instance;
9268                 }, {
9269                   instance: instance,
9270                   identifier: identifier
9271                 });
9272               }
9273
9274               instance = $injector.instantiate(expression, locals, constructor);
9275
9276               if (identifier) {
9277                 addIdentifier(locals, identifier, instance, constructor || expression.name);
9278               }
9279
9280               return instance;
9281             };
9282
9283             function addIdentifier(locals, identifier, instance, name) {
9284               if (!(locals && isObject(locals.$scope))) {
9285                 throw minErr('$controller')('noscp',
9286                   "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9287                   name, identifier);
9288               }
9289
9290               locals.$scope[identifier] = instance;
9291             }
9292           }];
9293         }
9294
9295         /**
9296          * @ngdoc service
9297          * @name $document
9298          * @requires $window
9299          *
9300          * @description
9301          * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9302          *
9303          * @example
9304            <example module="documentExample">
9305              <file name="index.html">
9306                <div ng-controller="ExampleController">
9307                  <p>$document title: <b ng-bind="title"></b></p>
9308                  <p>window.document title: <b ng-bind="windowTitle"></b></p>
9309                </div>
9310              </file>
9311              <file name="script.js">
9312                angular.module('documentExample', [])
9313                  .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9314                    $scope.title = $document[0].title;
9315                    $scope.windowTitle = angular.element(window.document)[0].title;
9316                  }]);
9317              </file>
9318            </example>
9319          */
9320         function $DocumentProvider() {
9321           this.$get = ['$window', function(window) {
9322             return jqLite(window.document);
9323           }];
9324         }
9325
9326         /**
9327          * @ngdoc service
9328          * @name $exceptionHandler
9329          * @requires ng.$log
9330          *
9331          * @description
9332          * Any uncaught exception in angular expressions is delegated to this service.
9333          * The default implementation simply delegates to `$log.error` which logs it into
9334          * the browser console.
9335          *
9336          * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9337          * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9338          *
9339          * ## Example:
9340          *
9341          * ```js
9342          *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9343          *     return function(exception, cause) {
9344          *       exception.message += ' (caused by "' + cause + '")';
9345          *       throw exception;
9346          *     };
9347          *   });
9348          * ```
9349          *
9350          * This example will override the normal action of `$exceptionHandler`, to make angular
9351          * exceptions fail hard when they happen, instead of just logging to the console.
9352          *
9353          * <hr />
9354          * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9355          * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9356          * (unless executed during a digest).
9357          *
9358          * If you wish, you can manually delegate exceptions, e.g.
9359          * `try { ... } catch(e) { $exceptionHandler(e); }`
9360          *
9361          * @param {Error} exception Exception associated with the error.
9362          * @param {string=} cause optional information about the context in which
9363          *       the error was thrown.
9364          *
9365          */
9366         function $ExceptionHandlerProvider() {
9367           this.$get = ['$log', function($log) {
9368             return function(exception, cause) {
9369               $log.error.apply($log, arguments);
9370             };
9371           }];
9372         }
9373
9374         var $$ForceReflowProvider = function() {
9375           this.$get = ['$document', function($document) {
9376             return function(domNode) {
9377               //the line below will force the browser to perform a repaint so
9378               //that all the animated elements within the animation frame will
9379               //be properly updated and drawn on screen. This is required to
9380               //ensure that the preparation animation is properly flushed so that
9381               //the active state picks up from there. DO NOT REMOVE THIS LINE.
9382               //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9383               //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9384               //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9385               if (domNode) {
9386                 if (!domNode.nodeType && domNode instanceof jqLite) {
9387                   domNode = domNode[0];
9388                 }
9389               } else {
9390                 domNode = $document[0].body;
9391               }
9392               return domNode.offsetWidth + 1;
9393             };
9394           }];
9395         };
9396
9397         var APPLICATION_JSON = 'application/json';
9398         var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9399         var JSON_START = /^\[|^\{(?!\{)/;
9400         var JSON_ENDS = {
9401           '[': /]$/,
9402           '{': /}$/
9403         };
9404         var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9405         var $httpMinErr = minErr('$http');
9406         var $httpMinErrLegacyFn = function(method) {
9407           return function() {
9408             throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9409           };
9410         };
9411
9412         function serializeValue(v) {
9413           if (isObject(v)) {
9414             return isDate(v) ? v.toISOString() : toJson(v);
9415           }
9416           return v;
9417         }
9418
9419
9420         function $HttpParamSerializerProvider() {
9421           /**
9422            * @ngdoc service
9423            * @name $httpParamSerializer
9424            * @description
9425            *
9426            * Default {@link $http `$http`} params serializer that converts objects to strings
9427            * according to the following rules:
9428            *
9429            * * `{'foo': 'bar'}` results in `foo=bar`
9430            * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9431            * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9432            * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9433            *
9434            * Note that serializer will sort the request parameters alphabetically.
9435            * */
9436
9437           this.$get = function() {
9438             return function ngParamSerializer(params) {
9439               if (!params) return '';
9440               var parts = [];
9441               forEachSorted(params, function(value, key) {
9442                 if (value === null || isUndefined(value)) return;
9443                 if (isArray(value)) {
9444                   forEach(value, function(v, k) {
9445                     parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9446                   });
9447                 } else {
9448                   parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9449                 }
9450               });
9451
9452               return parts.join('&');
9453             };
9454           };
9455         }
9456
9457         function $HttpParamSerializerJQLikeProvider() {
9458           /**
9459            * @ngdoc service
9460            * @name $httpParamSerializerJQLike
9461            * @description
9462            *
9463            * Alternative {@link $http `$http`} params serializer that follows
9464            * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9465            * The serializer will also sort the params alphabetically.
9466            *
9467            * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9468            *
9469            * ```js
9470            * $http({
9471            *   url: myUrl,
9472            *   method: 'GET',
9473            *   params: myParams,
9474            *   paramSerializer: '$httpParamSerializerJQLike'
9475            * });
9476            * ```
9477            *
9478            * It is also possible to set it as the default `paramSerializer` in the
9479            * {@link $httpProvider#defaults `$httpProvider`}.
9480            *
9481            * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9482            * form data for submission:
9483            *
9484            * ```js
9485            * .controller(function($http, $httpParamSerializerJQLike) {
9486            *   //...
9487            *
9488            *   $http({
9489            *     url: myUrl,
9490            *     method: 'POST',
9491            *     data: $httpParamSerializerJQLike(myData),
9492            *     headers: {
9493            *       'Content-Type': 'application/x-www-form-urlencoded'
9494            *     }
9495            *   });
9496            *
9497            * });
9498            * ```
9499            *
9500            * */
9501           this.$get = function() {
9502             return function jQueryLikeParamSerializer(params) {
9503               if (!params) return '';
9504               var parts = [];
9505               serialize(params, '', true);
9506               return parts.join('&');
9507
9508               function serialize(toSerialize, prefix, topLevel) {
9509                 if (toSerialize === null || isUndefined(toSerialize)) return;
9510                 if (isArray(toSerialize)) {
9511                   forEach(toSerialize, function(value, index) {
9512                     serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9513                   });
9514                 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9515                   forEachSorted(toSerialize, function(value, key) {
9516                     serialize(value, prefix +
9517                         (topLevel ? '' : '[') +
9518                         key +
9519                         (topLevel ? '' : ']'));
9520                   });
9521                 } else {
9522                   parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9523                 }
9524               }
9525             };
9526           };
9527         }
9528
9529         function defaultHttpResponseTransform(data, headers) {
9530           if (isString(data)) {
9531             // Strip json vulnerability protection prefix and trim whitespace
9532             var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9533
9534             if (tempData) {
9535               var contentType = headers('Content-Type');
9536               if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9537                 data = fromJson(tempData);
9538               }
9539             }
9540           }
9541
9542           return data;
9543         }
9544
9545         function isJsonLike(str) {
9546             var jsonStart = str.match(JSON_START);
9547             return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9548         }
9549
9550         /**
9551          * Parse headers into key value object
9552          *
9553          * @param {string} headers Raw headers as a string
9554          * @returns {Object} Parsed headers as key value object
9555          */
9556         function parseHeaders(headers) {
9557           var parsed = createMap(), i;
9558
9559           function fillInParsed(key, val) {
9560             if (key) {
9561               parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9562             }
9563           }
9564
9565           if (isString(headers)) {
9566             forEach(headers.split('\n'), function(line) {
9567               i = line.indexOf(':');
9568               fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9569             });
9570           } else if (isObject(headers)) {
9571             forEach(headers, function(headerVal, headerKey) {
9572               fillInParsed(lowercase(headerKey), trim(headerVal));
9573             });
9574           }
9575
9576           return parsed;
9577         }
9578
9579
9580         /**
9581          * Returns a function that provides access to parsed headers.
9582          *
9583          * Headers are lazy parsed when first requested.
9584          * @see parseHeaders
9585          *
9586          * @param {(string|Object)} headers Headers to provide access to.
9587          * @returns {function(string=)} Returns a getter function which if called with:
9588          *
9589          *   - if called with single an argument returns a single header value or null
9590          *   - if called with no arguments returns an object containing all headers.
9591          */
9592         function headersGetter(headers) {
9593           var headersObj;
9594
9595           return function(name) {
9596             if (!headersObj) headersObj =  parseHeaders(headers);
9597
9598             if (name) {
9599               var value = headersObj[lowercase(name)];
9600               if (value === void 0) {
9601                 value = null;
9602               }
9603               return value;
9604             }
9605
9606             return headersObj;
9607           };
9608         }
9609
9610
9611         /**
9612          * Chain all given functions
9613          *
9614          * This function is used for both request and response transforming
9615          *
9616          * @param {*} data Data to transform.
9617          * @param {function(string=)} headers HTTP headers getter fn.
9618          * @param {number} status HTTP status code of the response.
9619          * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9620          * @returns {*} Transformed data.
9621          */
9622         function transformData(data, headers, status, fns) {
9623           if (isFunction(fns)) {
9624             return fns(data, headers, status);
9625           }
9626
9627           forEach(fns, function(fn) {
9628             data = fn(data, headers, status);
9629           });
9630
9631           return data;
9632         }
9633
9634
9635         function isSuccess(status) {
9636           return 200 <= status && status < 300;
9637         }
9638
9639
9640         /**
9641          * @ngdoc provider
9642          * @name $httpProvider
9643          * @description
9644          * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9645          * */
9646         function $HttpProvider() {
9647           /**
9648            * @ngdoc property
9649            * @name $httpProvider#defaults
9650            * @description
9651            *
9652            * Object containing default values for all {@link ng.$http $http} requests.
9653            *
9654            * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9655            * that will provide the cache for all requests who set their `cache` property to `true`.
9656            * If you set the `defaults.cache = false` then only requests that specify their own custom
9657            * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9658            *
9659            * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9660            * Defaults value is `'XSRF-TOKEN'`.
9661            *
9662            * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9663            * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9664            *
9665            * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9666            * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9667            * setting default headers.
9668            *     - **`defaults.headers.common`**
9669            *     - **`defaults.headers.post`**
9670            *     - **`defaults.headers.put`**
9671            *     - **`defaults.headers.patch`**
9672            *
9673            *
9674            * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9675            *  used to the prepare string representation of request parameters (specified as an object).
9676            *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9677            *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9678            *
9679            **/
9680           var defaults = this.defaults = {
9681             // transform incoming response data
9682             transformResponse: [defaultHttpResponseTransform],
9683
9684             // transform outgoing request data
9685             transformRequest: [function(d) {
9686               return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9687             }],
9688
9689             // default headers
9690             headers: {
9691               common: {
9692                 'Accept': 'application/json, text/plain, */*'
9693               },
9694               post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9695               put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9696               patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9697             },
9698
9699             xsrfCookieName: 'XSRF-TOKEN',
9700             xsrfHeaderName: 'X-XSRF-TOKEN',
9701
9702             paramSerializer: '$httpParamSerializer'
9703           };
9704
9705           var useApplyAsync = false;
9706           /**
9707            * @ngdoc method
9708            * @name $httpProvider#useApplyAsync
9709            * @description
9710            *
9711            * Configure $http service to combine processing of multiple http responses received at around
9712            * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9713            * significant performance improvement for bigger applications that make many HTTP requests
9714            * concurrently (common during application bootstrap).
9715            *
9716            * Defaults to false. If no value is specified, returns the current configured value.
9717            *
9718            * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9719            *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9720            *    to load and share the same digest cycle.
9721            *
9722            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9723            *    otherwise, returns the current configured value.
9724            **/
9725           this.useApplyAsync = function(value) {
9726             if (isDefined(value)) {
9727               useApplyAsync = !!value;
9728               return this;
9729             }
9730             return useApplyAsync;
9731           };
9732
9733           var useLegacyPromise = true;
9734           /**
9735            * @ngdoc method
9736            * @name $httpProvider#useLegacyPromiseExtensions
9737            * @description
9738            *
9739            * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9740            * This should be used to make sure that applications work without these methods.
9741            *
9742            * Defaults to true. If no value is specified, returns the current configured value.
9743            *
9744            * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9745            *
9746            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9747            *    otherwise, returns the current configured value.
9748            **/
9749           this.useLegacyPromiseExtensions = function(value) {
9750             if (isDefined(value)) {
9751               useLegacyPromise = !!value;
9752               return this;
9753             }
9754             return useLegacyPromise;
9755           };
9756
9757           /**
9758            * @ngdoc property
9759            * @name $httpProvider#interceptors
9760            * @description
9761            *
9762            * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9763            * pre-processing of request or postprocessing of responses.
9764            *
9765            * These service factories are ordered by request, i.e. they are applied in the same order as the
9766            * array, on request, but reverse order, on response.
9767            *
9768            * {@link ng.$http#interceptors Interceptors detailed info}
9769            **/
9770           var interceptorFactories = this.interceptors = [];
9771
9772           this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9773               function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9774
9775             var defaultCache = $cacheFactory('$http');
9776
9777             /**
9778              * Make sure that default param serializer is exposed as a function
9779              */
9780             defaults.paramSerializer = isString(defaults.paramSerializer) ?
9781               $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9782
9783             /**
9784              * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9785              * The reversal is needed so that we can build up the interception chain around the
9786              * server request.
9787              */
9788             var reversedInterceptors = [];
9789
9790             forEach(interceptorFactories, function(interceptorFactory) {
9791               reversedInterceptors.unshift(isString(interceptorFactory)
9792                   ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9793             });
9794
9795             /**
9796              * @ngdoc service
9797              * @kind function
9798              * @name $http
9799              * @requires ng.$httpBackend
9800              * @requires $cacheFactory
9801              * @requires $rootScope
9802              * @requires $q
9803              * @requires $injector
9804              *
9805              * @description
9806              * The `$http` service is a core Angular service that facilitates communication with the remote
9807              * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9808              * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9809              *
9810              * For unit testing applications that use `$http` service, see
9811              * {@link ngMock.$httpBackend $httpBackend mock}.
9812              *
9813              * For a higher level of abstraction, please check out the {@link ngResource.$resource
9814              * $resource} service.
9815              *
9816              * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9817              * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9818              * it is important to familiarize yourself with these APIs and the guarantees they provide.
9819              *
9820              *
9821              * ## General usage
9822              * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9823              * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9824              *
9825              * ```js
9826              *   // Simple GET request example:
9827              *   $http({
9828              *     method: 'GET',
9829              *     url: '/someUrl'
9830              *   }).then(function successCallback(response) {
9831              *       // this callback will be called asynchronously
9832              *       // when the response is available
9833              *     }, function errorCallback(response) {
9834              *       // called asynchronously if an error occurs
9835              *       // or server returns response with an error status.
9836              *     });
9837              * ```
9838              *
9839              * The response object has these properties:
9840              *
9841              *   - **data** – `{string|Object}` – The response body transformed with the transform
9842              *     functions.
9843              *   - **status** – `{number}` – HTTP status code of the response.
9844              *   - **headers** – `{function([headerName])}` – Header getter function.
9845              *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9846              *   - **statusText** – `{string}` – HTTP status text of the response.
9847              *
9848              * A response status code between 200 and 299 is considered a success status and
9849              * will result in the success callback being called. Note that if the response is a redirect,
9850              * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9851              * called for such responses.
9852              *
9853              *
9854              * ## Shortcut methods
9855              *
9856              * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9857              * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9858              * last argument.
9859              *
9860              * ```js
9861              *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9862              *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9863              * ```
9864              *
9865              * Complete list of shortcut methods:
9866              *
9867              * - {@link ng.$http#get $http.get}
9868              * - {@link ng.$http#head $http.head}
9869              * - {@link ng.$http#post $http.post}
9870              * - {@link ng.$http#put $http.put}
9871              * - {@link ng.$http#delete $http.delete}
9872              * - {@link ng.$http#jsonp $http.jsonp}
9873              * - {@link ng.$http#patch $http.patch}
9874              *
9875              *
9876              * ## Writing Unit Tests that use $http
9877              * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9878              * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9879              * request using trained responses.
9880              *
9881              * ```
9882              * $httpBackend.expectGET(...);
9883              * $http.get(...);
9884              * $httpBackend.flush();
9885              * ```
9886              *
9887              * ## Deprecation Notice
9888              * <div class="alert alert-danger">
9889              *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9890              *   Use the standard `then` method instead.
9891              *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9892              *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9893              * </div>
9894              *
9895              * ## Setting HTTP Headers
9896              *
9897              * The $http service will automatically add certain HTTP headers to all requests. These defaults
9898              * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9899              * object, which currently contains this default configuration:
9900              *
9901              * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9902              *   - `Accept: application/json, text/plain, * / *`
9903              * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9904              *   - `Content-Type: application/json`
9905              * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9906              *   - `Content-Type: application/json`
9907              *
9908              * To add or overwrite these defaults, simply add or remove a property from these configuration
9909              * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9910              * with the lowercased HTTP method name as the key, e.g.
9911              * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9912              *
9913              * The defaults can also be set at runtime via the `$http.defaults` object in the same
9914              * fashion. For example:
9915              *
9916              * ```
9917              * module.run(function($http) {
9918              *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9919              * });
9920              * ```
9921              *
9922              * In addition, you can supply a `headers` property in the config object passed when
9923              * calling `$http(config)`, which overrides the defaults without changing them globally.
9924              *
9925              * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9926              * Use the `headers` property, setting the desired header to `undefined`. For example:
9927              *
9928              * ```js
9929              * var req = {
9930              *  method: 'POST',
9931              *  url: 'http://example.com',
9932              *  headers: {
9933              *    'Content-Type': undefined
9934              *  },
9935              *  data: { test: 'test' }
9936              * }
9937              *
9938              * $http(req).then(function(){...}, function(){...});
9939              * ```
9940              *
9941              * ## Transforming Requests and Responses
9942              *
9943              * Both requests and responses can be transformed using transformation functions: `transformRequest`
9944              * and `transformResponse`. These properties can be a single function that returns
9945              * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9946              * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9947              *
9948              * ### Default Transformations
9949              *
9950              * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9951              * `defaults.transformResponse` properties. If a request does not provide its own transformations
9952              * then these will be applied.
9953              *
9954              * You can augment or replace the default transformations by modifying these properties by adding to or
9955              * replacing the array.
9956              *
9957              * Angular provides the following default transformations:
9958              *
9959              * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9960              *
9961              * - If the `data` property of the request configuration object contains an object, serialize it
9962              *   into JSON format.
9963              *
9964              * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9965              *
9966              *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9967              *  - If JSON response is detected, deserialize it using a JSON parser.
9968              *
9969              *
9970              * ### Overriding the Default Transformations Per Request
9971              *
9972              * If you wish override the request/response transformations only for a single request then provide
9973              * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9974              * into `$http`.
9975              *
9976              * Note that if you provide these properties on the config object the default transformations will be
9977              * overwritten. If you wish to augment the default transformations then you must include them in your
9978              * local transformation array.
9979              *
9980              * The following code demonstrates adding a new response transformation to be run after the default response
9981              * transformations have been run.
9982              *
9983              * ```js
9984              * function appendTransform(defaults, transform) {
9985              *
9986              *   // We can't guarantee that the default transformation is an array
9987              *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9988              *
9989              *   // Append the new transformation to the defaults
9990              *   return defaults.concat(transform);
9991              * }
9992              *
9993              * $http({
9994              *   url: '...',
9995              *   method: 'GET',
9996              *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9997              *     return doTransform(value);
9998              *   })
9999              * });
10000              * ```
10001              *
10002              *
10003              * ## Caching
10004              *
10005              * To enable caching, set the request configuration `cache` property to `true` (to use default
10006              * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10007              * When the cache is enabled, `$http` stores the response from the server in the specified
10008              * cache. The next time the same request is made, the response is served from the cache without
10009              * sending a request to the server.
10010              *
10011              * Note that even if the response is served from cache, delivery of the data is asynchronous in
10012              * the same way that real requests are.
10013              *
10014              * If there are multiple GET requests for the same URL that should be cached using the same
10015              * cache, but the cache is not populated yet, only one request to the server will be made and
10016              * the remaining requests will be fulfilled using the response from the first request.
10017              *
10018              * You can change the default cache to a new object (built with
10019              * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10020              * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10021              * their `cache` property to `true` will now use this cache object.
10022              *
10023              * If you set the default cache to `false` then only requests that specify their own custom
10024              * cache object will be cached.
10025              *
10026              * ## Interceptors
10027              *
10028              * Before you start creating interceptors, be sure to understand the
10029              * {@link ng.$q $q and deferred/promise APIs}.
10030              *
10031              * For purposes of global error handling, authentication, or any kind of synchronous or
10032              * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10033              * able to intercept requests before they are handed to the server and
10034              * responses before they are handed over to the application code that
10035              * initiated these requests. The interceptors leverage the {@link ng.$q
10036              * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10037              *
10038              * The interceptors are service factories that are registered with the `$httpProvider` by
10039              * adding them to the `$httpProvider.interceptors` array. The factory is called and
10040              * injected with dependencies (if specified) and returns the interceptor.
10041              *
10042              * There are two kinds of interceptors (and two kinds of rejection interceptors):
10043              *
10044              *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10045              *     modify the `config` object or create a new one. The function needs to return the `config`
10046              *     object directly, or a promise containing the `config` or a new `config` object.
10047              *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10048              *     resolved with a rejection.
10049              *   * `response`: interceptors get called with http `response` object. The function is free to
10050              *     modify the `response` object or create a new one. The function needs to return the `response`
10051              *     object directly, or as a promise containing the `response` or a new `response` object.
10052              *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10053              *     resolved with a rejection.
10054              *
10055              *
10056              * ```js
10057              *   // register the interceptor as a service
10058              *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10059              *     return {
10060              *       // optional method
10061              *       'request': function(config) {
10062              *         // do something on success
10063              *         return config;
10064              *       },
10065              *
10066              *       // optional method
10067              *      'requestError': function(rejection) {
10068              *         // do something on error
10069              *         if (canRecover(rejection)) {
10070              *           return responseOrNewPromise
10071              *         }
10072              *         return $q.reject(rejection);
10073              *       },
10074              *
10075              *
10076              *
10077              *       // optional method
10078              *       'response': function(response) {
10079              *         // do something on success
10080              *         return response;
10081              *       },
10082              *
10083              *       // optional method
10084              *      'responseError': function(rejection) {
10085              *         // do something on error
10086              *         if (canRecover(rejection)) {
10087              *           return responseOrNewPromise
10088              *         }
10089              *         return $q.reject(rejection);
10090              *       }
10091              *     };
10092              *   });
10093              *
10094              *   $httpProvider.interceptors.push('myHttpInterceptor');
10095              *
10096              *
10097              *   // alternatively, register the interceptor via an anonymous factory
10098              *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10099              *     return {
10100              *      'request': function(config) {
10101              *          // same as above
10102              *       },
10103              *
10104              *       'response': function(response) {
10105              *          // same as above
10106              *       }
10107              *     };
10108              *   });
10109              * ```
10110              *
10111              * ## Security Considerations
10112              *
10113              * When designing web applications, consider security threats from:
10114              *
10115              * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10116              * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10117              *
10118              * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10119              * pre-configured with strategies that address these issues, but for this to work backend server
10120              * cooperation is required.
10121              *
10122              * ### JSON Vulnerability Protection
10123              *
10124              * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10125              * allows third party website to turn your JSON resource URL into
10126              * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10127              * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10128              * Angular will automatically strip the prefix before processing it as JSON.
10129              *
10130              * For example if your server needs to return:
10131              * ```js
10132              * ['one','two']
10133              * ```
10134              *
10135              * which is vulnerable to attack, your server can return:
10136              * ```js
10137              * )]}',
10138              * ['one','two']
10139              * ```
10140              *
10141              * Angular will strip the prefix, before processing the JSON.
10142              *
10143              *
10144              * ### Cross Site Request Forgery (XSRF) Protection
10145              *
10146              * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10147              * an unauthorized site can gain your user's private data. Angular provides a mechanism
10148              * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10149              * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10150              * JavaScript that runs on your domain could read the cookie, your server can be assured that
10151              * the XHR came from JavaScript running on your domain. The header will not be set for
10152              * cross-domain requests.
10153              *
10154              * To take advantage of this, your server needs to set a token in a JavaScript readable session
10155              * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10156              * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10157              * that only JavaScript running on your domain could have sent the request. The token must be
10158              * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10159              * making up its own tokens). We recommend that the token is a digest of your site's
10160              * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10161              * for added security.
10162              *
10163              * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10164              * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10165              * or the per-request config object.
10166              *
10167              * In order to prevent collisions in environments where multiple Angular apps share the
10168              * same domain or subdomain, we recommend that each application uses unique cookie name.
10169              *
10170              * @param {object} config Object describing the request to be made and how it should be
10171              *    processed. The object has following properties:
10172              *
10173              *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10174              *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10175              *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10176              *      with the `paramSerializer` and appended as GET parameters.
10177              *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10178              *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10179              *      HTTP headers to send to the server. If the return value of a function is null, the
10180              *      header will not be sent. Functions accept a config object as an argument.
10181              *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10182              *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10183              *    - **transformRequest** –
10184              *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10185              *      transform function or an array of such functions. The transform function takes the http
10186              *      request body and headers and returns its transformed (typically serialized) version.
10187              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10188              *      Overriding the Default Transformations}
10189              *    - **transformResponse** –
10190              *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10191              *      transform function or an array of such functions. The transform function takes the http
10192              *      response body, headers and status and returns its transformed (typically deserialized) version.
10193              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10194              *      Overriding the Default TransformationjqLiks}
10195              *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10196              *      prepare the string representation of request parameters (specified as an object).
10197              *      If specified as string, it is interpreted as function registered with the
10198              *      {@link $injector $injector}, which means you can create your own serializer
10199              *      by registering it as a {@link auto.$provide#service service}.
10200              *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10201              *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10202              *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10203              *      GET request, otherwise if a cache instance built with
10204              *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10205              *      caching.
10206              *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10207              *      that should abort the request when resolved.
10208              *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10209              *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10210              *      for more information.
10211              *    - **responseType** - `{string}` - see
10212              *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10213              *
10214              * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10215              *                        when the request succeeds or fails.
10216              *
10217              *
10218              * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10219              *   requests. This is primarily meant to be used for debugging purposes.
10220              *
10221              *
10222              * @example
10223         <example module="httpExample">
10224         <file name="index.html">
10225           <div ng-controller="FetchController">
10226             <select ng-model="method" aria-label="Request method">
10227               <option>GET</option>
10228               <option>JSONP</option>
10229             </select>
10230             <input type="text" ng-model="url" size="80" aria-label="URL" />
10231             <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10232             <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10233             <button id="samplejsonpbtn"
10234               ng-click="updateModel('JSONP',
10235                             'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10236               Sample JSONP
10237             </button>
10238             <button id="invalidjsonpbtn"
10239               ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10240                 Invalid JSONP
10241               </button>
10242             <pre>http status code: {{status}}</pre>
10243             <pre>http response data: {{data}}</pre>
10244           </div>
10245         </file>
10246         <file name="script.js">
10247           angular.module('httpExample', [])
10248             .controller('FetchController', ['$scope', '$http', '$templateCache',
10249               function($scope, $http, $templateCache) {
10250                 $scope.method = 'GET';
10251                 $scope.url = 'http-hello.html';
10252
10253                 $scope.fetch = function() {
10254                   $scope.code = null;
10255                   $scope.response = null;
10256
10257                   $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10258                     then(function(response) {
10259                       $scope.status = response.status;
10260                       $scope.data = response.data;
10261                     }, function(response) {
10262                       $scope.data = response.data || "Request failed";
10263                       $scope.status = response.status;
10264                   });
10265                 };
10266
10267                 $scope.updateModel = function(method, url) {
10268                   $scope.method = method;
10269                   $scope.url = url;
10270                 };
10271               }]);
10272         </file>
10273         <file name="http-hello.html">
10274           Hello, $http!
10275         </file>
10276         <file name="protractor.js" type="protractor">
10277           var status = element(by.binding('status'));
10278           var data = element(by.binding('data'));
10279           var fetchBtn = element(by.id('fetchbtn'));
10280           var sampleGetBtn = element(by.id('samplegetbtn'));
10281           var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10282           var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10283
10284           it('should make an xhr GET request', function() {
10285             sampleGetBtn.click();
10286             fetchBtn.click();
10287             expect(status.getText()).toMatch('200');
10288             expect(data.getText()).toMatch(/Hello, \$http!/);
10289           });
10290
10291         // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10292         // it('should make a JSONP request to angularjs.org', function() {
10293         //   sampleJsonpBtn.click();
10294         //   fetchBtn.click();
10295         //   expect(status.getText()).toMatch('200');
10296         //   expect(data.getText()).toMatch(/Super Hero!/);
10297         // });
10298
10299           it('should make JSONP request to invalid URL and invoke the error handler',
10300               function() {
10301             invalidJsonpBtn.click();
10302             fetchBtn.click();
10303             expect(status.getText()).toMatch('0');
10304             expect(data.getText()).toMatch('Request failed');
10305           });
10306         </file>
10307         </example>
10308              */
10309             function $http(requestConfig) {
10310
10311               if (!angular.isObject(requestConfig)) {
10312                 throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10313               }
10314
10315               var config = extend({
10316                 method: 'get',
10317                 transformRequest: defaults.transformRequest,
10318                 transformResponse: defaults.transformResponse,
10319                 paramSerializer: defaults.paramSerializer
10320               }, requestConfig);
10321
10322               config.headers = mergeHeaders(requestConfig);
10323               config.method = uppercase(config.method);
10324               config.paramSerializer = isString(config.paramSerializer) ?
10325                 $injector.get(config.paramSerializer) : config.paramSerializer;
10326
10327               var serverRequest = function(config) {
10328                 var headers = config.headers;
10329                 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10330
10331                 // strip content-type if data is undefined
10332                 if (isUndefined(reqData)) {
10333                   forEach(headers, function(value, header) {
10334                     if (lowercase(header) === 'content-type') {
10335                         delete headers[header];
10336                     }
10337                   });
10338                 }
10339
10340                 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10341                   config.withCredentials = defaults.withCredentials;
10342                 }
10343
10344                 // send request
10345                 return sendReq(config, reqData).then(transformResponse, transformResponse);
10346               };
10347
10348               var chain = [serverRequest, undefined];
10349               var promise = $q.when(config);
10350
10351               // apply interceptors
10352               forEach(reversedInterceptors, function(interceptor) {
10353                 if (interceptor.request || interceptor.requestError) {
10354                   chain.unshift(interceptor.request, interceptor.requestError);
10355                 }
10356                 if (interceptor.response || interceptor.responseError) {
10357                   chain.push(interceptor.response, interceptor.responseError);
10358                 }
10359               });
10360
10361               while (chain.length) {
10362                 var thenFn = chain.shift();
10363                 var rejectFn = chain.shift();
10364
10365                 promise = promise.then(thenFn, rejectFn);
10366               }
10367
10368               if (useLegacyPromise) {
10369                 promise.success = function(fn) {
10370                   assertArgFn(fn, 'fn');
10371
10372                   promise.then(function(response) {
10373                     fn(response.data, response.status, response.headers, config);
10374                   });
10375                   return promise;
10376                 };
10377
10378                 promise.error = function(fn) {
10379                   assertArgFn(fn, 'fn');
10380
10381                   promise.then(null, function(response) {
10382                     fn(response.data, response.status, response.headers, config);
10383                   });
10384                   return promise;
10385                 };
10386               } else {
10387                 promise.success = $httpMinErrLegacyFn('success');
10388                 promise.error = $httpMinErrLegacyFn('error');
10389               }
10390
10391               return promise;
10392
10393               function transformResponse(response) {
10394                 // make a copy since the response must be cacheable
10395                 var resp = extend({}, response);
10396                 resp.data = transformData(response.data, response.headers, response.status,
10397                                           config.transformResponse);
10398                 return (isSuccess(response.status))
10399                   ? resp
10400                   : $q.reject(resp);
10401               }
10402
10403               function executeHeaderFns(headers, config) {
10404                 var headerContent, processedHeaders = {};
10405
10406                 forEach(headers, function(headerFn, header) {
10407                   if (isFunction(headerFn)) {
10408                     headerContent = headerFn(config);
10409                     if (headerContent != null) {
10410                       processedHeaders[header] = headerContent;
10411                     }
10412                   } else {
10413                     processedHeaders[header] = headerFn;
10414                   }
10415                 });
10416
10417                 return processedHeaders;
10418               }
10419
10420               function mergeHeaders(config) {
10421                 var defHeaders = defaults.headers,
10422                     reqHeaders = extend({}, config.headers),
10423                     defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10424
10425                 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10426
10427                 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10428                 defaultHeadersIteration:
10429                 for (defHeaderName in defHeaders) {
10430                   lowercaseDefHeaderName = lowercase(defHeaderName);
10431
10432                   for (reqHeaderName in reqHeaders) {
10433                     if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10434                       continue defaultHeadersIteration;
10435                     }
10436                   }
10437
10438                   reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10439                 }
10440
10441                 // execute if header value is a function for merged headers
10442                 return executeHeaderFns(reqHeaders, shallowCopy(config));
10443               }
10444             }
10445
10446             $http.pendingRequests = [];
10447
10448             /**
10449              * @ngdoc method
10450              * @name $http#get
10451              *
10452              * @description
10453              * Shortcut method to perform `GET` request.
10454              *
10455              * @param {string} url Relative or absolute URL specifying the destination of the request
10456              * @param {Object=} config Optional configuration object
10457              * @returns {HttpPromise} Future object
10458              */
10459
10460             /**
10461              * @ngdoc method
10462              * @name $http#delete
10463              *
10464              * @description
10465              * Shortcut method to perform `DELETE` request.
10466              *
10467              * @param {string} url Relative or absolute URL specifying the destination of the request
10468              * @param {Object=} config Optional configuration object
10469              * @returns {HttpPromise} Future object
10470              */
10471
10472             /**
10473              * @ngdoc method
10474              * @name $http#head
10475              *
10476              * @description
10477              * Shortcut method to perform `HEAD` request.
10478              *
10479              * @param {string} url Relative or absolute URL specifying the destination of the request
10480              * @param {Object=} config Optional configuration object
10481              * @returns {HttpPromise} Future object
10482              */
10483
10484             /**
10485              * @ngdoc method
10486              * @name $http#jsonp
10487              *
10488              * @description
10489              * Shortcut method to perform `JSONP` request.
10490              *
10491              * @param {string} url Relative or absolute URL specifying the destination of the request.
10492              *                     The name of the callback should be the string `JSON_CALLBACK`.
10493              * @param {Object=} config Optional configuration object
10494              * @returns {HttpPromise} Future object
10495              */
10496             createShortMethods('get', 'delete', 'head', 'jsonp');
10497
10498             /**
10499              * @ngdoc method
10500              * @name $http#post
10501              *
10502              * @description
10503              * Shortcut method to perform `POST` request.
10504              *
10505              * @param {string} url Relative or absolute URL specifying the destination of the request
10506              * @param {*} data Request content
10507              * @param {Object=} config Optional configuration object
10508              * @returns {HttpPromise} Future object
10509              */
10510
10511             /**
10512              * @ngdoc method
10513              * @name $http#put
10514              *
10515              * @description
10516              * Shortcut method to perform `PUT` request.
10517              *
10518              * @param {string} url Relative or absolute URL specifying the destination of the request
10519              * @param {*} data Request content
10520              * @param {Object=} config Optional configuration object
10521              * @returns {HttpPromise} Future object
10522              */
10523
10524              /**
10525               * @ngdoc method
10526               * @name $http#patch
10527               *
10528               * @description
10529               * Shortcut method to perform `PATCH` request.
10530               *
10531               * @param {string} url Relative or absolute URL specifying the destination of the request
10532               * @param {*} data Request content
10533               * @param {Object=} config Optional configuration object
10534               * @returns {HttpPromise} Future object
10535               */
10536             createShortMethodsWithData('post', 'put', 'patch');
10537
10538                 /**
10539                  * @ngdoc property
10540                  * @name $http#defaults
10541                  *
10542                  * @description
10543                  * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10544                  * default headers, withCredentials as well as request and response transformations.
10545                  *
10546                  * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10547                  */
10548             $http.defaults = defaults;
10549
10550
10551             return $http;
10552
10553
10554             function createShortMethods(names) {
10555               forEach(arguments, function(name) {
10556                 $http[name] = function(url, config) {
10557                   return $http(extend({}, config || {}, {
10558                     method: name,
10559                     url: url
10560                   }));
10561                 };
10562               });
10563             }
10564
10565
10566             function createShortMethodsWithData(name) {
10567               forEach(arguments, function(name) {
10568                 $http[name] = function(url, data, config) {
10569                   return $http(extend({}, config || {}, {
10570                     method: name,
10571                     url: url,
10572                     data: data
10573                   }));
10574                 };
10575               });
10576             }
10577
10578
10579             /**
10580              * Makes the request.
10581              *
10582              * !!! ACCESSES CLOSURE VARS:
10583              * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10584              */
10585             function sendReq(config, reqData) {
10586               var deferred = $q.defer(),
10587                   promise = deferred.promise,
10588                   cache,
10589                   cachedResp,
10590                   reqHeaders = config.headers,
10591                   url = buildUrl(config.url, config.paramSerializer(config.params));
10592
10593               $http.pendingRequests.push(config);
10594               promise.then(removePendingReq, removePendingReq);
10595
10596
10597               if ((config.cache || defaults.cache) && config.cache !== false &&
10598                   (config.method === 'GET' || config.method === 'JSONP')) {
10599                 cache = isObject(config.cache) ? config.cache
10600                       : isObject(defaults.cache) ? defaults.cache
10601                       : defaultCache;
10602               }
10603
10604               if (cache) {
10605                 cachedResp = cache.get(url);
10606                 if (isDefined(cachedResp)) {
10607                   if (isPromiseLike(cachedResp)) {
10608                     // cached request has already been sent, but there is no response yet
10609                     cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10610                   } else {
10611                     // serving from cache
10612                     if (isArray(cachedResp)) {
10613                       resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10614                     } else {
10615                       resolvePromise(cachedResp, 200, {}, 'OK');
10616                     }
10617                   }
10618                 } else {
10619                   // put the promise for the non-transformed response into cache as a placeholder
10620                   cache.put(url, promise);
10621                 }
10622               }
10623
10624
10625               // if we won't have the response in cache, set the xsrf headers and
10626               // send the request to the backend
10627               if (isUndefined(cachedResp)) {
10628                 var xsrfValue = urlIsSameOrigin(config.url)
10629                     ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10630                     : undefined;
10631                 if (xsrfValue) {
10632                   reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10633                 }
10634
10635                 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10636                     config.withCredentials, config.responseType);
10637               }
10638
10639               return promise;
10640
10641
10642               /**
10643                * Callback registered to $httpBackend():
10644                *  - caches the response if desired
10645                *  - resolves the raw $http promise
10646                *  - calls $apply
10647                */
10648               function done(status, response, headersString, statusText) {
10649                 if (cache) {
10650                   if (isSuccess(status)) {
10651                     cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10652                   } else {
10653                     // remove promise from the cache
10654                     cache.remove(url);
10655                   }
10656                 }
10657
10658                 function resolveHttpPromise() {
10659                   resolvePromise(response, status, headersString, statusText);
10660                 }
10661
10662                 if (useApplyAsync) {
10663                   $rootScope.$applyAsync(resolveHttpPromise);
10664                 } else {
10665                   resolveHttpPromise();
10666                   if (!$rootScope.$$phase) $rootScope.$apply();
10667                 }
10668               }
10669
10670
10671               /**
10672                * Resolves the raw $http promise.
10673                */
10674               function resolvePromise(response, status, headers, statusText) {
10675                 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10676                 status = status >= -1 ? status : 0;
10677
10678                 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10679                   data: response,
10680                   status: status,
10681                   headers: headersGetter(headers),
10682                   config: config,
10683                   statusText: statusText
10684                 });
10685               }
10686
10687               function resolvePromiseWithResult(result) {
10688                 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10689               }
10690
10691               function removePendingReq() {
10692                 var idx = $http.pendingRequests.indexOf(config);
10693                 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10694               }
10695             }
10696
10697
10698             function buildUrl(url, serializedParams) {
10699               if (serializedParams.length > 0) {
10700                 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10701               }
10702               return url;
10703             }
10704           }];
10705         }
10706
10707         /**
10708          * @ngdoc service
10709          * @name $xhrFactory
10710          *
10711          * @description
10712          * Factory function used to create XMLHttpRequest objects.
10713          *
10714          * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10715          *
10716          * ```
10717          * angular.module('myApp', [])
10718          * .factory('$xhrFactory', function() {
10719          *   return function createXhr(method, url) {
10720          *     return new window.XMLHttpRequest({mozSystem: true});
10721          *   };
10722          * });
10723          * ```
10724          *
10725          * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10726          * @param {string} url URL of the request.
10727          */
10728         function $xhrFactoryProvider() {
10729           this.$get = function() {
10730             return function createXhr() {
10731               return new window.XMLHttpRequest();
10732             };
10733           };
10734         }
10735
10736         /**
10737          * @ngdoc service
10738          * @name $httpBackend
10739          * @requires $window
10740          * @requires $document
10741          * @requires $xhrFactory
10742          *
10743          * @description
10744          * HTTP backend used by the {@link ng.$http service} that delegates to
10745          * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10746          *
10747          * You should never need to use this service directly, instead use the higher-level abstractions:
10748          * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10749          *
10750          * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10751          * $httpBackend} which can be trained with responses.
10752          */
10753         function $HttpBackendProvider() {
10754           this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10755             return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10756           }];
10757         }
10758
10759         function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10760           // TODO(vojta): fix the signature
10761           return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10762             $browser.$$incOutstandingRequestCount();
10763             url = url || $browser.url();
10764
10765             if (lowercase(method) == 'jsonp') {
10766               var callbackId = '_' + (callbacks.counter++).toString(36);
10767               callbacks[callbackId] = function(data) {
10768                 callbacks[callbackId].data = data;
10769                 callbacks[callbackId].called = true;
10770               };
10771
10772               var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10773                   callbackId, function(status, text) {
10774                 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10775                 callbacks[callbackId] = noop;
10776               });
10777             } else {
10778
10779               var xhr = createXhr(method, url);
10780
10781               xhr.open(method, url, true);
10782               forEach(headers, function(value, key) {
10783                 if (isDefined(value)) {
10784                     xhr.setRequestHeader(key, value);
10785                 }
10786               });
10787
10788               xhr.onload = function requestLoaded() {
10789                 var statusText = xhr.statusText || '';
10790
10791                 // responseText is the old-school way of retrieving response (supported by IE9)
10792                 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10793                 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10794
10795                 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10796                 var status = xhr.status === 1223 ? 204 : xhr.status;
10797
10798                 // fix status code when it is 0 (0 status is undocumented).
10799                 // Occurs when accessing file resources or on Android 4.1 stock browser
10800                 // while retrieving files from application cache.
10801                 if (status === 0) {
10802                   status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10803                 }
10804
10805                 completeRequest(callback,
10806                     status,
10807                     response,
10808                     xhr.getAllResponseHeaders(),
10809                     statusText);
10810               };
10811
10812               var requestError = function() {
10813                 // The response is always empty
10814                 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10815                 completeRequest(callback, -1, null, null, '');
10816               };
10817
10818               xhr.onerror = requestError;
10819               xhr.onabort = requestError;
10820
10821               if (withCredentials) {
10822                 xhr.withCredentials = true;
10823               }
10824
10825               if (responseType) {
10826                 try {
10827                   xhr.responseType = responseType;
10828                 } catch (e) {
10829                   // WebKit added support for the json responseType value on 09/03/2013
10830                   // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10831                   // known to throw when setting the value "json" as the response type. Other older
10832                   // browsers implementing the responseType
10833                   //
10834                   // The json response type can be ignored if not supported, because JSON payloads are
10835                   // parsed on the client-side regardless.
10836                   if (responseType !== 'json') {
10837                     throw e;
10838                   }
10839                 }
10840               }
10841
10842               xhr.send(isUndefined(post) ? null : post);
10843             }
10844
10845             if (timeout > 0) {
10846               var timeoutId = $browserDefer(timeoutRequest, timeout);
10847             } else if (isPromiseLike(timeout)) {
10848               timeout.then(timeoutRequest);
10849             }
10850
10851
10852             function timeoutRequest() {
10853               jsonpDone && jsonpDone();
10854               xhr && xhr.abort();
10855             }
10856
10857             function completeRequest(callback, status, response, headersString, statusText) {
10858               // cancel timeout and subsequent timeout promise resolution
10859               if (isDefined(timeoutId)) {
10860                 $browserDefer.cancel(timeoutId);
10861               }
10862               jsonpDone = xhr = null;
10863
10864               callback(status, response, headersString, statusText);
10865               $browser.$$completeOutstandingRequest(noop);
10866             }
10867           };
10868
10869           function jsonpReq(url, callbackId, done) {
10870             // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10871             // - fetches local scripts via XHR and evals them
10872             // - adds and immediately removes script elements from the document
10873             var script = rawDocument.createElement('script'), callback = null;
10874             script.type = "text/javascript";
10875             script.src = url;
10876             script.async = true;
10877
10878             callback = function(event) {
10879               removeEventListenerFn(script, "load", callback);
10880               removeEventListenerFn(script, "error", callback);
10881               rawDocument.body.removeChild(script);
10882               script = null;
10883               var status = -1;
10884               var text = "unknown";
10885
10886               if (event) {
10887                 if (event.type === "load" && !callbacks[callbackId].called) {
10888                   event = { type: "error" };
10889                 }
10890                 text = event.type;
10891                 status = event.type === "error" ? 404 : 200;
10892               }
10893
10894               if (done) {
10895                 done(status, text);
10896               }
10897             };
10898
10899             addEventListenerFn(script, "load", callback);
10900             addEventListenerFn(script, "error", callback);
10901             rawDocument.body.appendChild(script);
10902             return callback;
10903           }
10904         }
10905
10906         var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10907         $interpolateMinErr.throwNoconcat = function(text) {
10908           throw $interpolateMinErr('noconcat',
10909               "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10910               "interpolations that concatenate multiple expressions when a trusted value is " +
10911               "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10912         };
10913
10914         $interpolateMinErr.interr = function(text, err) {
10915           return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10916         };
10917
10918         /**
10919          * @ngdoc provider
10920          * @name $interpolateProvider
10921          *
10922          * @description
10923          *
10924          * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10925          *
10926          * @example
10927         <example module="customInterpolationApp">
10928         <file name="index.html">
10929         <script>
10930           var customInterpolationApp = angular.module('customInterpolationApp', []);
10931
10932           customInterpolationApp.config(function($interpolateProvider) {
10933             $interpolateProvider.startSymbol('//');
10934             $interpolateProvider.endSymbol('//');
10935           });
10936
10937
10938           customInterpolationApp.controller('DemoController', function() {
10939               this.label = "This binding is brought you by // interpolation symbols.";
10940           });
10941         </script>
10942         <div ng-app="App" ng-controller="DemoController as demo">
10943             //demo.label//
10944         </div>
10945         </file>
10946         <file name="protractor.js" type="protractor">
10947           it('should interpolate binding with custom symbols', function() {
10948             expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10949           });
10950         </file>
10951         </example>
10952          */
10953         function $InterpolateProvider() {
10954           var startSymbol = '{{';
10955           var endSymbol = '}}';
10956
10957           /**
10958            * @ngdoc method
10959            * @name $interpolateProvider#startSymbol
10960            * @description
10961            * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10962            *
10963            * @param {string=} value new value to set the starting symbol to.
10964            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10965            */
10966           this.startSymbol = function(value) {
10967             if (value) {
10968               startSymbol = value;
10969               return this;
10970             } else {
10971               return startSymbol;
10972             }
10973           };
10974
10975           /**
10976            * @ngdoc method
10977            * @name $interpolateProvider#endSymbol
10978            * @description
10979            * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10980            *
10981            * @param {string=} value new value to set the ending symbol to.
10982            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10983            */
10984           this.endSymbol = function(value) {
10985             if (value) {
10986               endSymbol = value;
10987               return this;
10988             } else {
10989               return endSymbol;
10990             }
10991           };
10992
10993
10994           this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10995             var startSymbolLength = startSymbol.length,
10996                 endSymbolLength = endSymbol.length,
10997                 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10998                 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10999
11000             function escape(ch) {
11001               return '\\\\\\' + ch;
11002             }
11003
11004             function unescapeText(text) {
11005               return text.replace(escapedStartRegexp, startSymbol).
11006                 replace(escapedEndRegexp, endSymbol);
11007             }
11008
11009             function stringify(value) {
11010               if (value == null) { // null || undefined
11011                 return '';
11012               }
11013               switch (typeof value) {
11014                 case 'string':
11015                   break;
11016                 case 'number':
11017                   value = '' + value;
11018                   break;
11019                 default:
11020                   value = toJson(value);
11021               }
11022
11023               return value;
11024             }
11025
11026             /**
11027              * @ngdoc service
11028              * @name $interpolate
11029              * @kind function
11030              *
11031              * @requires $parse
11032              * @requires $sce
11033              *
11034              * @description
11035              *
11036              * Compiles a string with markup into an interpolation function. This service is used by the
11037              * HTML {@link ng.$compile $compile} service for data binding. See
11038              * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11039              * interpolation markup.
11040              *
11041              *
11042              * ```js
11043              *   var $interpolate = ...; // injected
11044              *   var exp = $interpolate('Hello {{name | uppercase}}!');
11045              *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11046              * ```
11047              *
11048              * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11049              * `true`, the interpolation function will return `undefined` unless all embedded expressions
11050              * evaluate to a value other than `undefined`.
11051              *
11052              * ```js
11053              *   var $interpolate = ...; // injected
11054              *   var context = {greeting: 'Hello', name: undefined };
11055              *
11056              *   // default "forgiving" mode
11057              *   var exp = $interpolate('{{greeting}} {{name}}!');
11058              *   expect(exp(context)).toEqual('Hello !');
11059              *
11060              *   // "allOrNothing" mode
11061              *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11062              *   expect(exp(context)).toBeUndefined();
11063              *   context.name = 'Angular';
11064              *   expect(exp(context)).toEqual('Hello Angular!');
11065              * ```
11066              *
11067              * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11068              *
11069              * ####Escaped Interpolation
11070              * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11071              * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11072              * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11073              * or binding.
11074              *
11075              * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11076              * degree, while also enabling code examples to work without relying on the
11077              * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11078              *
11079              * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11080              * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11081              * interpolation start/end markers with their escaped counterparts.**
11082              *
11083              * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11084              * output when the $interpolate service processes the text. So, for HTML elements interpolated
11085              * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11086              * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11087              * this is typically useful only when user-data is used in rendering a template from the server, or
11088              * when otherwise untrusted data is used by a directive.
11089              *
11090              * <example>
11091              *  <file name="index.html">
11092              *    <div ng-init="username='A user'">
11093              *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11094              *        </p>
11095              *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11096              *        application, but fails to accomplish their task, because the server has correctly
11097              *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11098              *        characters.</p>
11099              *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11100              *        from the database by an administrator.</p>
11101              *    </div>
11102              *  </file>
11103              * </example>
11104              *
11105              * @param {string} text The text with markup to interpolate.
11106              * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11107              *    embedded expression in order to return an interpolation function. Strings with no
11108              *    embedded expression will return null for the interpolation function.
11109              * @param {string=} trustedContext when provided, the returned function passes the interpolated
11110              *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11111              *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11112              *    provides Strict Contextual Escaping for details.
11113              * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11114              *    unless all embedded expressions evaluate to a value other than `undefined`.
11115              * @returns {function(context)} an interpolation function which is used to compute the
11116              *    interpolated string. The function has these parameters:
11117              *
11118              * - `context`: evaluation context for all expressions embedded in the interpolated text
11119              */
11120             function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11121               allOrNothing = !!allOrNothing;
11122               var startIndex,
11123                   endIndex,
11124                   index = 0,
11125                   expressions = [],
11126                   parseFns = [],
11127                   textLength = text.length,
11128                   exp,
11129                   concat = [],
11130                   expressionPositions = [];
11131
11132               while (index < textLength) {
11133                 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11134                      ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11135                   if (index !== startIndex) {
11136                     concat.push(unescapeText(text.substring(index, startIndex)));
11137                   }
11138                   exp = text.substring(startIndex + startSymbolLength, endIndex);
11139                   expressions.push(exp);
11140                   parseFns.push($parse(exp, parseStringifyInterceptor));
11141                   index = endIndex + endSymbolLength;
11142                   expressionPositions.push(concat.length);
11143                   concat.push('');
11144                 } else {
11145                   // we did not find an interpolation, so we have to add the remainder to the separators array
11146                   if (index !== textLength) {
11147                     concat.push(unescapeText(text.substring(index)));
11148                   }
11149                   break;
11150                 }
11151               }
11152
11153               // Concatenating expressions makes it hard to reason about whether some combination of
11154               // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11155               // single expression be used for iframe[src], object[src], etc., we ensure that the value
11156               // that's used is assigned or constructed by some JS code somewhere that is more testable or
11157               // make it obvious that you bound the value to some user controlled value.  This helps reduce
11158               // the load when auditing for XSS issues.
11159               if (trustedContext && concat.length > 1) {
11160                   $interpolateMinErr.throwNoconcat(text);
11161               }
11162
11163               if (!mustHaveExpression || expressions.length) {
11164                 var compute = function(values) {
11165                   for (var i = 0, ii = expressions.length; i < ii; i++) {
11166                     if (allOrNothing && isUndefined(values[i])) return;
11167                     concat[expressionPositions[i]] = values[i];
11168                   }
11169                   return concat.join('');
11170                 };
11171
11172                 var getValue = function(value) {
11173                   return trustedContext ?
11174                     $sce.getTrusted(trustedContext, value) :
11175                     $sce.valueOf(value);
11176                 };
11177
11178                 return extend(function interpolationFn(context) {
11179                     var i = 0;
11180                     var ii = expressions.length;
11181                     var values = new Array(ii);
11182
11183                     try {
11184                       for (; i < ii; i++) {
11185                         values[i] = parseFns[i](context);
11186                       }
11187
11188                       return compute(values);
11189                     } catch (err) {
11190                       $exceptionHandler($interpolateMinErr.interr(text, err));
11191                     }
11192
11193                   }, {
11194                   // all of these properties are undocumented for now
11195                   exp: text, //just for compatibility with regular watchers created via $watch
11196                   expressions: expressions,
11197                   $$watchDelegate: function(scope, listener) {
11198                     var lastValue;
11199                     return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11200                       var currValue = compute(values);
11201                       if (isFunction(listener)) {
11202                         listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11203                       }
11204                       lastValue = currValue;
11205                     });
11206                   }
11207                 });
11208               }
11209
11210               function parseStringifyInterceptor(value) {
11211                 try {
11212                   value = getValue(value);
11213                   return allOrNothing && !isDefined(value) ? value : stringify(value);
11214                 } catch (err) {
11215                   $exceptionHandler($interpolateMinErr.interr(text, err));
11216                 }
11217               }
11218             }
11219
11220
11221             /**
11222              * @ngdoc method
11223              * @name $interpolate#startSymbol
11224              * @description
11225              * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11226              *
11227              * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11228              * the symbol.
11229              *
11230              * @returns {string} start symbol.
11231              */
11232             $interpolate.startSymbol = function() {
11233               return startSymbol;
11234             };
11235
11236
11237             /**
11238              * @ngdoc method
11239              * @name $interpolate#endSymbol
11240              * @description
11241              * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11242              *
11243              * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11244              * the symbol.
11245              *
11246              * @returns {string} end symbol.
11247              */
11248             $interpolate.endSymbol = function() {
11249               return endSymbol;
11250             };
11251
11252             return $interpolate;
11253           }];
11254         }
11255
11256         function $IntervalProvider() {
11257           this.$get = ['$rootScope', '$window', '$q', '$$q',
11258                function($rootScope,   $window,   $q,   $$q) {
11259             var intervals = {};
11260
11261
11262              /**
11263               * @ngdoc service
11264               * @name $interval
11265               *
11266               * @description
11267               * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11268               * milliseconds.
11269               *
11270               * The return value of registering an interval function is a promise. This promise will be
11271               * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11272               * run indefinitely if `count` is not defined. The value of the notification will be the
11273               * number of iterations that have run.
11274               * To cancel an interval, call `$interval.cancel(promise)`.
11275               *
11276               * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11277               * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11278               * time.
11279               *
11280               * <div class="alert alert-warning">
11281               * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11282               * with them.  In particular they are not automatically destroyed when a controller's scope or a
11283               * directive's element are destroyed.
11284               * You should take this into consideration and make sure to always cancel the interval at the
11285               * appropriate moment.  See the example below for more details on how and when to do this.
11286               * </div>
11287               *
11288               * @param {function()} fn A function that should be called repeatedly.
11289               * @param {number} delay Number of milliseconds between each function call.
11290               * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11291               *   indefinitely.
11292               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11293               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11294               * @param {...*=} Pass additional parameters to the executed function.
11295               * @returns {promise} A promise which will be notified on each iteration.
11296               *
11297               * @example
11298               * <example module="intervalExample">
11299               * <file name="index.html">
11300               *   <script>
11301               *     angular.module('intervalExample', [])
11302               *       .controller('ExampleController', ['$scope', '$interval',
11303               *         function($scope, $interval) {
11304               *           $scope.format = 'M/d/yy h:mm:ss a';
11305               *           $scope.blood_1 = 100;
11306               *           $scope.blood_2 = 120;
11307               *
11308               *           var stop;
11309               *           $scope.fight = function() {
11310               *             // Don't start a new fight if we are already fighting
11311               *             if ( angular.isDefined(stop) ) return;
11312               *
11313               *             stop = $interval(function() {
11314               *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11315               *                 $scope.blood_1 = $scope.blood_1 - 3;
11316               *                 $scope.blood_2 = $scope.blood_2 - 4;
11317               *               } else {
11318               *                 $scope.stopFight();
11319               *               }
11320               *             }, 100);
11321               *           };
11322               *
11323               *           $scope.stopFight = function() {
11324               *             if (angular.isDefined(stop)) {
11325               *               $interval.cancel(stop);
11326               *               stop = undefined;
11327               *             }
11328               *           };
11329               *
11330               *           $scope.resetFight = function() {
11331               *             $scope.blood_1 = 100;
11332               *             $scope.blood_2 = 120;
11333               *           };
11334               *
11335               *           $scope.$on('$destroy', function() {
11336               *             // Make sure that the interval is destroyed too
11337               *             $scope.stopFight();
11338               *           });
11339               *         }])
11340               *       // Register the 'myCurrentTime' directive factory method.
11341               *       // We inject $interval and dateFilter service since the factory method is DI.
11342               *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11343               *         function($interval, dateFilter) {
11344               *           // return the directive link function. (compile function not needed)
11345               *           return function(scope, element, attrs) {
11346               *             var format,  // date format
11347               *                 stopTime; // so that we can cancel the time updates
11348               *
11349               *             // used to update the UI
11350               *             function updateTime() {
11351               *               element.text(dateFilter(new Date(), format));
11352               *             }
11353               *
11354               *             // watch the expression, and update the UI on change.
11355               *             scope.$watch(attrs.myCurrentTime, function(value) {
11356               *               format = value;
11357               *               updateTime();
11358               *             });
11359               *
11360               *             stopTime = $interval(updateTime, 1000);
11361               *
11362               *             // listen on DOM destroy (removal) event, and cancel the next UI update
11363               *             // to prevent updating time after the DOM element was removed.
11364               *             element.on('$destroy', function() {
11365               *               $interval.cancel(stopTime);
11366               *             });
11367               *           }
11368               *         }]);
11369               *   </script>
11370               *
11371               *   <div>
11372               *     <div ng-controller="ExampleController">
11373               *       <label>Date format: <input ng-model="format"></label> <hr/>
11374               *       Current time is: <span my-current-time="format"></span>
11375               *       <hr/>
11376               *       Blood 1 : <font color='red'>{{blood_1}}</font>
11377               *       Blood 2 : <font color='red'>{{blood_2}}</font>
11378               *       <button type="button" data-ng-click="fight()">Fight</button>
11379               *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11380               *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11381               *     </div>
11382               *   </div>
11383               *
11384               * </file>
11385               * </example>
11386               */
11387             function interval(fn, delay, count, invokeApply) {
11388               var hasParams = arguments.length > 4,
11389                   args = hasParams ? sliceArgs(arguments, 4) : [],
11390                   setInterval = $window.setInterval,
11391                   clearInterval = $window.clearInterval,
11392                   iteration = 0,
11393                   skipApply = (isDefined(invokeApply) && !invokeApply),
11394                   deferred = (skipApply ? $$q : $q).defer(),
11395                   promise = deferred.promise;
11396
11397               count = isDefined(count) ? count : 0;
11398
11399               promise.then(null, null, (!hasParams) ? fn : function() {
11400                 fn.apply(null, args);
11401               });
11402
11403               promise.$$intervalId = setInterval(function tick() {
11404                 deferred.notify(iteration++);
11405
11406                 if (count > 0 && iteration >= count) {
11407                   deferred.resolve(iteration);
11408                   clearInterval(promise.$$intervalId);
11409                   delete intervals[promise.$$intervalId];
11410                 }
11411
11412                 if (!skipApply) $rootScope.$apply();
11413
11414               }, delay);
11415
11416               intervals[promise.$$intervalId] = deferred;
11417
11418               return promise;
11419             }
11420
11421
11422              /**
11423               * @ngdoc method
11424               * @name $interval#cancel
11425               *
11426               * @description
11427               * Cancels a task associated with the `promise`.
11428               *
11429               * @param {Promise=} promise returned by the `$interval` function.
11430               * @returns {boolean} Returns `true` if the task was successfully canceled.
11431               */
11432             interval.cancel = function(promise) {
11433               if (promise && promise.$$intervalId in intervals) {
11434                 intervals[promise.$$intervalId].reject('canceled');
11435                 $window.clearInterval(promise.$$intervalId);
11436                 delete intervals[promise.$$intervalId];
11437                 return true;
11438               }
11439               return false;
11440             };
11441
11442             return interval;
11443           }];
11444         }
11445
11446         /**
11447          * @ngdoc service
11448          * @name $locale
11449          *
11450          * @description
11451          * $locale service provides localization rules for various Angular components. As of right now the
11452          * only public api is:
11453          *
11454          * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11455          */
11456
11457         var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11458             DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11459         var $locationMinErr = minErr('$location');
11460
11461
11462         /**
11463          * Encode path using encodeUriSegment, ignoring forward slashes
11464          *
11465          * @param {string} path Path to encode
11466          * @returns {string}
11467          */
11468         function encodePath(path) {
11469           var segments = path.split('/'),
11470               i = segments.length;
11471
11472           while (i--) {
11473             segments[i] = encodeUriSegment(segments[i]);
11474           }
11475
11476           return segments.join('/');
11477         }
11478
11479         function parseAbsoluteUrl(absoluteUrl, locationObj) {
11480           var parsedUrl = urlResolve(absoluteUrl);
11481
11482           locationObj.$$protocol = parsedUrl.protocol;
11483           locationObj.$$host = parsedUrl.hostname;
11484           locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11485         }
11486
11487
11488         function parseAppUrl(relativeUrl, locationObj) {
11489           var prefixed = (relativeUrl.charAt(0) !== '/');
11490           if (prefixed) {
11491             relativeUrl = '/' + relativeUrl;
11492           }
11493           var match = urlResolve(relativeUrl);
11494           locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11495               match.pathname.substring(1) : match.pathname);
11496           locationObj.$$search = parseKeyValue(match.search);
11497           locationObj.$$hash = decodeURIComponent(match.hash);
11498
11499           // make sure path starts with '/';
11500           if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11501             locationObj.$$path = '/' + locationObj.$$path;
11502           }
11503         }
11504
11505
11506         /**
11507          *
11508          * @param {string} begin
11509          * @param {string} whole
11510          * @returns {string} returns text from whole after begin or undefined if it does not begin with
11511          *                   expected string.
11512          */
11513         function beginsWith(begin, whole) {
11514           if (whole.indexOf(begin) === 0) {
11515             return whole.substr(begin.length);
11516           }
11517         }
11518
11519
11520         function stripHash(url) {
11521           var index = url.indexOf('#');
11522           return index == -1 ? url : url.substr(0, index);
11523         }
11524
11525         function trimEmptyHash(url) {
11526           return url.replace(/(#.+)|#$/, '$1');
11527         }
11528
11529
11530         function stripFile(url) {
11531           return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11532         }
11533
11534         /* return the server only (scheme://host:port) */
11535         function serverBase(url) {
11536           return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11537         }
11538
11539
11540         /**
11541          * LocationHtml5Url represents an url
11542          * This object is exposed as $location service when HTML5 mode is enabled and supported
11543          *
11544          * @constructor
11545          * @param {string} appBase application base URL
11546          * @param {string} appBaseNoFile application base URL stripped of any filename
11547          * @param {string} basePrefix url path prefix
11548          */
11549         function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11550           this.$$html5 = true;
11551           basePrefix = basePrefix || '';
11552           parseAbsoluteUrl(appBase, this);
11553
11554
11555           /**
11556            * Parse given html5 (regular) url string into properties
11557            * @param {string} url HTML5 url
11558            * @private
11559            */
11560           this.$$parse = function(url) {
11561             var pathUrl = beginsWith(appBaseNoFile, url);
11562             if (!isString(pathUrl)) {
11563               throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11564                   appBaseNoFile);
11565             }
11566
11567             parseAppUrl(pathUrl, this);
11568
11569             if (!this.$$path) {
11570               this.$$path = '/';
11571             }
11572
11573             this.$$compose();
11574           };
11575
11576           /**
11577            * Compose url and update `absUrl` property
11578            * @private
11579            */
11580           this.$$compose = function() {
11581             var search = toKeyValue(this.$$search),
11582                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11583
11584             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11585             this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11586           };
11587
11588           this.$$parseLinkUrl = function(url, relHref) {
11589             if (relHref && relHref[0] === '#') {
11590               // special case for links to hash fragments:
11591               // keep the old url and only replace the hash fragment
11592               this.hash(relHref.slice(1));
11593               return true;
11594             }
11595             var appUrl, prevAppUrl;
11596             var rewrittenUrl;
11597
11598             if (isDefined(appUrl = beginsWith(appBase, url))) {
11599               prevAppUrl = appUrl;
11600               if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11601                 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11602               } else {
11603                 rewrittenUrl = appBase + prevAppUrl;
11604               }
11605             } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11606               rewrittenUrl = appBaseNoFile + appUrl;
11607             } else if (appBaseNoFile == url + '/') {
11608               rewrittenUrl = appBaseNoFile;
11609             }
11610             if (rewrittenUrl) {
11611               this.$$parse(rewrittenUrl);
11612             }
11613             return !!rewrittenUrl;
11614           };
11615         }
11616
11617
11618         /**
11619          * LocationHashbangUrl represents url
11620          * This object is exposed as $location service when developer doesn't opt into html5 mode.
11621          * It also serves as the base class for html5 mode fallback on legacy browsers.
11622          *
11623          * @constructor
11624          * @param {string} appBase application base URL
11625          * @param {string} appBaseNoFile application base URL stripped of any filename
11626          * @param {string} hashPrefix hashbang prefix
11627          */
11628         function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11629
11630           parseAbsoluteUrl(appBase, this);
11631
11632
11633           /**
11634            * Parse given hashbang url into properties
11635            * @param {string} url Hashbang url
11636            * @private
11637            */
11638           this.$$parse = function(url) {
11639             var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11640             var withoutHashUrl;
11641
11642             if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11643
11644               // The rest of the url starts with a hash so we have
11645               // got either a hashbang path or a plain hash fragment
11646               withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11647               if (isUndefined(withoutHashUrl)) {
11648                 // There was no hashbang prefix so we just have a hash fragment
11649                 withoutHashUrl = withoutBaseUrl;
11650               }
11651
11652             } else {
11653               // There was no hashbang path nor hash fragment:
11654               // If we are in HTML5 mode we use what is left as the path;
11655               // Otherwise we ignore what is left
11656               if (this.$$html5) {
11657                 withoutHashUrl = withoutBaseUrl;
11658               } else {
11659                 withoutHashUrl = '';
11660                 if (isUndefined(withoutBaseUrl)) {
11661                   appBase = url;
11662                   this.replace();
11663                 }
11664               }
11665             }
11666
11667             parseAppUrl(withoutHashUrl, this);
11668
11669             this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11670
11671             this.$$compose();
11672
11673             /*
11674              * In Windows, on an anchor node on documents loaded from
11675              * the filesystem, the browser will return a pathname
11676              * prefixed with the drive name ('/C:/path') when a
11677              * pathname without a drive is set:
11678              *  * a.setAttribute('href', '/foo')
11679              *   * a.pathname === '/C:/foo' //true
11680              *
11681              * Inside of Angular, we're always using pathnames that
11682              * do not include drive names for routing.
11683              */
11684             function removeWindowsDriveName(path, url, base) {
11685               /*
11686               Matches paths for file protocol on windows,
11687               such as /C:/foo/bar, and captures only /foo/bar.
11688               */
11689               var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11690
11691               var firstPathSegmentMatch;
11692
11693               //Get the relative path from the input URL.
11694               if (url.indexOf(base) === 0) {
11695                 url = url.replace(base, '');
11696               }
11697
11698               // The input URL intentionally contains a first path segment that ends with a colon.
11699               if (windowsFilePathExp.exec(url)) {
11700                 return path;
11701               }
11702
11703               firstPathSegmentMatch = windowsFilePathExp.exec(path);
11704               return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11705             }
11706           };
11707
11708           /**
11709            * Compose hashbang url and update `absUrl` property
11710            * @private
11711            */
11712           this.$$compose = function() {
11713             var search = toKeyValue(this.$$search),
11714                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11715
11716             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11717             this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11718           };
11719
11720           this.$$parseLinkUrl = function(url, relHref) {
11721             if (stripHash(appBase) == stripHash(url)) {
11722               this.$$parse(url);
11723               return true;
11724             }
11725             return false;
11726           };
11727         }
11728
11729
11730         /**
11731          * LocationHashbangUrl represents url
11732          * This object is exposed as $location service when html5 history api is enabled but the browser
11733          * does not support it.
11734          *
11735          * @constructor
11736          * @param {string} appBase application base URL
11737          * @param {string} appBaseNoFile application base URL stripped of any filename
11738          * @param {string} hashPrefix hashbang prefix
11739          */
11740         function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11741           this.$$html5 = true;
11742           LocationHashbangUrl.apply(this, arguments);
11743
11744           this.$$parseLinkUrl = function(url, relHref) {
11745             if (relHref && relHref[0] === '#') {
11746               // special case for links to hash fragments:
11747               // keep the old url and only replace the hash fragment
11748               this.hash(relHref.slice(1));
11749               return true;
11750             }
11751
11752             var rewrittenUrl;
11753             var appUrl;
11754
11755             if (appBase == stripHash(url)) {
11756               rewrittenUrl = url;
11757             } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11758               rewrittenUrl = appBase + hashPrefix + appUrl;
11759             } else if (appBaseNoFile === url + '/') {
11760               rewrittenUrl = appBaseNoFile;
11761             }
11762             if (rewrittenUrl) {
11763               this.$$parse(rewrittenUrl);
11764             }
11765             return !!rewrittenUrl;
11766           };
11767
11768           this.$$compose = function() {
11769             var search = toKeyValue(this.$$search),
11770                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11771
11772             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11773             // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11774             this.$$absUrl = appBase + hashPrefix + this.$$url;
11775           };
11776
11777         }
11778
11779
11780         var locationPrototype = {
11781
11782           /**
11783            * Are we in html5 mode?
11784            * @private
11785            */
11786           $$html5: false,
11787
11788           /**
11789            * Has any change been replacing?
11790            * @private
11791            */
11792           $$replace: false,
11793
11794           /**
11795            * @ngdoc method
11796            * @name $location#absUrl
11797            *
11798            * @description
11799            * This method is getter only.
11800            *
11801            * Return full url representation with all segments encoded according to rules specified in
11802            * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11803            *
11804            *
11805            * ```js
11806            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11807            * var absUrl = $location.absUrl();
11808            * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11809            * ```
11810            *
11811            * @return {string} full url
11812            */
11813           absUrl: locationGetter('$$absUrl'),
11814
11815           /**
11816            * @ngdoc method
11817            * @name $location#url
11818            *
11819            * @description
11820            * This method is getter / setter.
11821            *
11822            * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11823            *
11824            * Change path, search and hash, when called with parameter and return `$location`.
11825            *
11826            *
11827            * ```js
11828            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11829            * var url = $location.url();
11830            * // => "/some/path?foo=bar&baz=xoxo"
11831            * ```
11832            *
11833            * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11834            * @return {string} url
11835            */
11836           url: function(url) {
11837             if (isUndefined(url)) {
11838               return this.$$url;
11839             }
11840
11841             var match = PATH_MATCH.exec(url);
11842             if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11843             if (match[2] || match[1] || url === '') this.search(match[3] || '');
11844             this.hash(match[5] || '');
11845
11846             return this;
11847           },
11848
11849           /**
11850            * @ngdoc method
11851            * @name $location#protocol
11852            *
11853            * @description
11854            * This method is getter only.
11855            *
11856            * Return protocol of current url.
11857            *
11858            *
11859            * ```js
11860            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11861            * var protocol = $location.protocol();
11862            * // => "http"
11863            * ```
11864            *
11865            * @return {string} protocol of current url
11866            */
11867           protocol: locationGetter('$$protocol'),
11868
11869           /**
11870            * @ngdoc method
11871            * @name $location#host
11872            *
11873            * @description
11874            * This method is getter only.
11875            *
11876            * Return host of current url.
11877            *
11878            * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11879            *
11880            *
11881            * ```js
11882            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11883            * var host = $location.host();
11884            * // => "example.com"
11885            *
11886            * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11887            * host = $location.host();
11888            * // => "example.com"
11889            * host = location.host;
11890            * // => "example.com:8080"
11891            * ```
11892            *
11893            * @return {string} host of current url.
11894            */
11895           host: locationGetter('$$host'),
11896
11897           /**
11898            * @ngdoc method
11899            * @name $location#port
11900            *
11901            * @description
11902            * This method is getter only.
11903            *
11904            * Return port of current url.
11905            *
11906            *
11907            * ```js
11908            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11909            * var port = $location.port();
11910            * // => 80
11911            * ```
11912            *
11913            * @return {Number} port
11914            */
11915           port: locationGetter('$$port'),
11916
11917           /**
11918            * @ngdoc method
11919            * @name $location#path
11920            *
11921            * @description
11922            * This method is getter / setter.
11923            *
11924            * Return path of current url when called without any parameter.
11925            *
11926            * Change path when called with parameter and return `$location`.
11927            *
11928            * Note: Path should always begin with forward slash (/), this method will add the forward slash
11929            * if it is missing.
11930            *
11931            *
11932            * ```js
11933            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11934            * var path = $location.path();
11935            * // => "/some/path"
11936            * ```
11937            *
11938            * @param {(string|number)=} path New path
11939            * @return {string} path
11940            */
11941           path: locationGetterSetter('$$path', function(path) {
11942             path = path !== null ? path.toString() : '';
11943             return path.charAt(0) == '/' ? path : '/' + path;
11944           }),
11945
11946           /**
11947            * @ngdoc method
11948            * @name $location#search
11949            *
11950            * @description
11951            * This method is getter / setter.
11952            *
11953            * Return search part (as object) of current url when called without any parameter.
11954            *
11955            * Change search part when called with parameter and return `$location`.
11956            *
11957            *
11958            * ```js
11959            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11960            * var searchObject = $location.search();
11961            * // => {foo: 'bar', baz: 'xoxo'}
11962            *
11963            * // set foo to 'yipee'
11964            * $location.search('foo', 'yipee');
11965            * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11966            * ```
11967            *
11968            * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11969            * hash object.
11970            *
11971            * When called with a single argument the method acts as a setter, setting the `search` component
11972            * of `$location` to the specified value.
11973            *
11974            * If the argument is a hash object containing an array of values, these values will be encoded
11975            * as duplicate search parameters in the url.
11976            *
11977            * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11978            * will override only a single search property.
11979            *
11980            * If `paramValue` is an array, it will override the property of the `search` component of
11981            * `$location` specified via the first argument.
11982            *
11983            * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11984            *
11985            * If `paramValue` is `true`, the property specified via the first argument will be added with no
11986            * value nor trailing equal sign.
11987            *
11988            * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11989            * one or more arguments returns `$location` object itself.
11990            */
11991           search: function(search, paramValue) {
11992             switch (arguments.length) {
11993               case 0:
11994                 return this.$$search;
11995               case 1:
11996                 if (isString(search) || isNumber(search)) {
11997                   search = search.toString();
11998                   this.$$search = parseKeyValue(search);
11999                 } else if (isObject(search)) {
12000                   search = copy(search, {});
12001                   // remove object undefined or null properties
12002                   forEach(search, function(value, key) {
12003                     if (value == null) delete search[key];
12004                   });
12005
12006                   this.$$search = search;
12007                 } else {
12008                   throw $locationMinErr('isrcharg',
12009                       'The first argument of the `$location#search()` call must be a string or an object.');
12010                 }
12011                 break;
12012               default:
12013                 if (isUndefined(paramValue) || paramValue === null) {
12014                   delete this.$$search[search];
12015                 } else {
12016                   this.$$search[search] = paramValue;
12017                 }
12018             }
12019
12020             this.$$compose();
12021             return this;
12022           },
12023
12024           /**
12025            * @ngdoc method
12026            * @name $location#hash
12027            *
12028            * @description
12029            * This method is getter / setter.
12030            *
12031            * Returns the hash fragment when called without any parameters.
12032            *
12033            * Changes the hash fragment when called with a parameter and returns `$location`.
12034            *
12035            *
12036            * ```js
12037            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12038            * var hash = $location.hash();
12039            * // => "hashValue"
12040            * ```
12041            *
12042            * @param {(string|number)=} hash New hash fragment
12043            * @return {string} hash
12044            */
12045           hash: locationGetterSetter('$$hash', function(hash) {
12046             return hash !== null ? hash.toString() : '';
12047           }),
12048
12049           /**
12050            * @ngdoc method
12051            * @name $location#replace
12052            *
12053            * @description
12054            * If called, all changes to $location during the current `$digest` will replace the current history
12055            * record, instead of adding a new one.
12056            */
12057           replace: function() {
12058             this.$$replace = true;
12059             return this;
12060           }
12061         };
12062
12063         forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12064           Location.prototype = Object.create(locationPrototype);
12065
12066           /**
12067            * @ngdoc method
12068            * @name $location#state
12069            *
12070            * @description
12071            * This method is getter / setter.
12072            *
12073            * Return the history state object when called without any parameter.
12074            *
12075            * Change the history state object when called with one parameter and return `$location`.
12076            * The state object is later passed to `pushState` or `replaceState`.
12077            *
12078            * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12079            * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12080            * older browsers (like IE9 or Android < 4.0), don't use this method.
12081            *
12082            * @param {object=} state State object for pushState or replaceState
12083            * @return {object} state
12084            */
12085           Location.prototype.state = function(state) {
12086             if (!arguments.length) {
12087               return this.$$state;
12088             }
12089
12090             if (Location !== LocationHtml5Url || !this.$$html5) {
12091               throw $locationMinErr('nostate', 'History API state support is available only ' +
12092                 'in HTML5 mode and only in browsers supporting HTML5 History API');
12093             }
12094             // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12095             // but we're changing the $$state reference to $browser.state() during the $digest
12096             // so the modification window is narrow.
12097             this.$$state = isUndefined(state) ? null : state;
12098
12099             return this;
12100           };
12101         });
12102
12103
12104         function locationGetter(property) {
12105           return function() {
12106             return this[property];
12107           };
12108         }
12109
12110
12111         function locationGetterSetter(property, preprocess) {
12112           return function(value) {
12113             if (isUndefined(value)) {
12114               return this[property];
12115             }
12116
12117             this[property] = preprocess(value);
12118             this.$$compose();
12119
12120             return this;
12121           };
12122         }
12123
12124
12125         /**
12126          * @ngdoc service
12127          * @name $location
12128          *
12129          * @requires $rootElement
12130          *
12131          * @description
12132          * The $location service parses the URL in the browser address bar (based on the
12133          * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12134          * available to your application. Changes to the URL in the address bar are reflected into
12135          * $location service and changes to $location are reflected into the browser address bar.
12136          *
12137          * **The $location service:**
12138          *
12139          * - Exposes the current URL in the browser address bar, so you can
12140          *   - Watch and observe the URL.
12141          *   - Change the URL.
12142          * - Synchronizes the URL with the browser when the user
12143          *   - Changes the address bar.
12144          *   - Clicks the back or forward button (or clicks a History link).
12145          *   - Clicks on a link.
12146          * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12147          *
12148          * For more information see {@link guide/$location Developer Guide: Using $location}
12149          */
12150
12151         /**
12152          * @ngdoc provider
12153          * @name $locationProvider
12154          * @description
12155          * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12156          */
12157         function $LocationProvider() {
12158           var hashPrefix = '',
12159               html5Mode = {
12160                 enabled: false,
12161                 requireBase: true,
12162                 rewriteLinks: true
12163               };
12164
12165           /**
12166            * @ngdoc method
12167            * @name $locationProvider#hashPrefix
12168            * @description
12169            * @param {string=} prefix Prefix for hash part (containing path and search)
12170            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12171            */
12172           this.hashPrefix = function(prefix) {
12173             if (isDefined(prefix)) {
12174               hashPrefix = prefix;
12175               return this;
12176             } else {
12177               return hashPrefix;
12178             }
12179           };
12180
12181           /**
12182            * @ngdoc method
12183            * @name $locationProvider#html5Mode
12184            * @description
12185            * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12186            *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12187            *   properties:
12188            *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12189            *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12190            *     support `pushState`.
12191            *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12192            *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12193            *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12194            *     See the {@link guide/$location $location guide for more information}
12195            *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12196            *     enables/disables url rewriting for relative links.
12197            *
12198            * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12199            */
12200           this.html5Mode = function(mode) {
12201             if (isBoolean(mode)) {
12202               html5Mode.enabled = mode;
12203               return this;
12204             } else if (isObject(mode)) {
12205
12206               if (isBoolean(mode.enabled)) {
12207                 html5Mode.enabled = mode.enabled;
12208               }
12209
12210               if (isBoolean(mode.requireBase)) {
12211                 html5Mode.requireBase = mode.requireBase;
12212               }
12213
12214               if (isBoolean(mode.rewriteLinks)) {
12215                 html5Mode.rewriteLinks = mode.rewriteLinks;
12216               }
12217
12218               return this;
12219             } else {
12220               return html5Mode;
12221             }
12222           };
12223
12224           /**
12225            * @ngdoc event
12226            * @name $location#$locationChangeStart
12227            * @eventType broadcast on root scope
12228            * @description
12229            * Broadcasted before a URL will change.
12230            *
12231            * This change can be prevented by calling
12232            * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12233            * details about event object. Upon successful change
12234            * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12235            *
12236            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12237            * the browser supports the HTML5 History API.
12238            *
12239            * @param {Object} angularEvent Synthetic event object.
12240            * @param {string} newUrl New URL
12241            * @param {string=} oldUrl URL that was before it was changed.
12242            * @param {string=} newState New history state object
12243            * @param {string=} oldState History state object that was before it was changed.
12244            */
12245
12246           /**
12247            * @ngdoc event
12248            * @name $location#$locationChangeSuccess
12249            * @eventType broadcast on root scope
12250            * @description
12251            * Broadcasted after a URL was changed.
12252            *
12253            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12254            * the browser supports the HTML5 History API.
12255            *
12256            * @param {Object} angularEvent Synthetic event object.
12257            * @param {string} newUrl New URL
12258            * @param {string=} oldUrl URL that was before it was changed.
12259            * @param {string=} newState New history state object
12260            * @param {string=} oldState History state object that was before it was changed.
12261            */
12262
12263           this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12264               function($rootScope, $browser, $sniffer, $rootElement, $window) {
12265             var $location,
12266                 LocationMode,
12267                 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12268                 initialUrl = $browser.url(),
12269                 appBase;
12270
12271             if (html5Mode.enabled) {
12272               if (!baseHref && html5Mode.requireBase) {
12273                 throw $locationMinErr('nobase',
12274                   "$location in HTML5 mode requires a <base> tag to be present!");
12275               }
12276               appBase = serverBase(initialUrl) + (baseHref || '/');
12277               LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12278             } else {
12279               appBase = stripHash(initialUrl);
12280               LocationMode = LocationHashbangUrl;
12281             }
12282             var appBaseNoFile = stripFile(appBase);
12283
12284             $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12285             $location.$$parseLinkUrl(initialUrl, initialUrl);
12286
12287             $location.$$state = $browser.state();
12288
12289             var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12290
12291             function setBrowserUrlWithFallback(url, replace, state) {
12292               var oldUrl = $location.url();
12293               var oldState = $location.$$state;
12294               try {
12295                 $browser.url(url, replace, state);
12296
12297                 // Make sure $location.state() returns referentially identical (not just deeply equal)
12298                 // state object; this makes possible quick checking if the state changed in the digest
12299                 // loop. Checking deep equality would be too expensive.
12300                 $location.$$state = $browser.state();
12301               } catch (e) {
12302                 // Restore old values if pushState fails
12303                 $location.url(oldUrl);
12304                 $location.$$state = oldState;
12305
12306                 throw e;
12307               }
12308             }
12309
12310             $rootElement.on('click', function(event) {
12311               // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12312               // currently we open nice url link and redirect then
12313
12314               if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12315
12316               var elm = jqLite(event.target);
12317
12318               // traverse the DOM up to find first A tag
12319               while (nodeName_(elm[0]) !== 'a') {
12320                 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12321                 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12322               }
12323
12324               var absHref = elm.prop('href');
12325               // get the actual href attribute - see
12326               // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12327               var relHref = elm.attr('href') || elm.attr('xlink:href');
12328
12329               if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12330                 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12331                 // an animation.
12332                 absHref = urlResolve(absHref.animVal).href;
12333               }
12334
12335               // Ignore when url is started with javascript: or mailto:
12336               if (IGNORE_URI_REGEXP.test(absHref)) return;
12337
12338               if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12339                 if ($location.$$parseLinkUrl(absHref, relHref)) {
12340                   // We do a preventDefault for all urls that are part of the angular application,
12341                   // in html5mode and also without, so that we are able to abort navigation without
12342                   // getting double entries in the location history.
12343                   event.preventDefault();
12344                   // update location manually
12345                   if ($location.absUrl() != $browser.url()) {
12346                     $rootScope.$apply();
12347                     // hack to work around FF6 bug 684208 when scenario runner clicks on links
12348                     $window.angular['ff-684208-preventDefault'] = true;
12349                   }
12350                 }
12351               }
12352             });
12353
12354
12355             // rewrite hashbang url <> html5 url
12356             if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12357               $browser.url($location.absUrl(), true);
12358             }
12359
12360             var initializing = true;
12361
12362             // update $location when $browser url changes
12363             $browser.onUrlChange(function(newUrl, newState) {
12364
12365               if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12366                 // If we are navigating outside of the app then force a reload
12367                 $window.location.href = newUrl;
12368                 return;
12369               }
12370
12371               $rootScope.$evalAsync(function() {
12372                 var oldUrl = $location.absUrl();
12373                 var oldState = $location.$$state;
12374                 var defaultPrevented;
12375                 newUrl = trimEmptyHash(newUrl);
12376                 $location.$$parse(newUrl);
12377                 $location.$$state = newState;
12378
12379                 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12380                     newState, oldState).defaultPrevented;
12381
12382                 // if the location was changed by a `$locationChangeStart` handler then stop
12383                 // processing this location change
12384                 if ($location.absUrl() !== newUrl) return;
12385
12386                 if (defaultPrevented) {
12387                   $location.$$parse(oldUrl);
12388                   $location.$$state = oldState;
12389                   setBrowserUrlWithFallback(oldUrl, false, oldState);
12390                 } else {
12391                   initializing = false;
12392                   afterLocationChange(oldUrl, oldState);
12393                 }
12394               });
12395               if (!$rootScope.$$phase) $rootScope.$digest();
12396             });
12397
12398             // update browser
12399             $rootScope.$watch(function $locationWatch() {
12400               var oldUrl = trimEmptyHash($browser.url());
12401               var newUrl = trimEmptyHash($location.absUrl());
12402               var oldState = $browser.state();
12403               var currentReplace = $location.$$replace;
12404               var urlOrStateChanged = oldUrl !== newUrl ||
12405                 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12406
12407               if (initializing || urlOrStateChanged) {
12408                 initializing = false;
12409
12410                 $rootScope.$evalAsync(function() {
12411                   var newUrl = $location.absUrl();
12412                   var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12413                       $location.$$state, oldState).defaultPrevented;
12414
12415                   // if the location was changed by a `$locationChangeStart` handler then stop
12416                   // processing this location change
12417                   if ($location.absUrl() !== newUrl) return;
12418
12419                   if (defaultPrevented) {
12420                     $location.$$parse(oldUrl);
12421                     $location.$$state = oldState;
12422                   } else {
12423                     if (urlOrStateChanged) {
12424                       setBrowserUrlWithFallback(newUrl, currentReplace,
12425                                                 oldState === $location.$$state ? null : $location.$$state);
12426                     }
12427                     afterLocationChange(oldUrl, oldState);
12428                   }
12429                 });
12430               }
12431
12432               $location.$$replace = false;
12433
12434               // we don't need to return anything because $evalAsync will make the digest loop dirty when
12435               // there is a change
12436             });
12437
12438             return $location;
12439
12440             function afterLocationChange(oldUrl, oldState) {
12441               $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12442                 $location.$$state, oldState);
12443             }
12444         }];
12445         }
12446
12447         /**
12448          * @ngdoc service
12449          * @name $log
12450          * @requires $window
12451          *
12452          * @description
12453          * Simple service for logging. Default implementation safely writes the message
12454          * into the browser's console (if present).
12455          *
12456          * The main purpose of this service is to simplify debugging and troubleshooting.
12457          *
12458          * The default is to log `debug` messages. You can use
12459          * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12460          *
12461          * @example
12462            <example module="logExample">
12463              <file name="script.js">
12464                angular.module('logExample', [])
12465                  .controller('LogController', ['$scope', '$log', function($scope, $log) {
12466                    $scope.$log = $log;
12467                    $scope.message = 'Hello World!';
12468                  }]);
12469              </file>
12470              <file name="index.html">
12471                <div ng-controller="LogController">
12472                  <p>Reload this page with open console, enter text and hit the log button...</p>
12473                  <label>Message:
12474                  <input type="text" ng-model="message" /></label>
12475                  <button ng-click="$log.log(message)">log</button>
12476                  <button ng-click="$log.warn(message)">warn</button>
12477                  <button ng-click="$log.info(message)">info</button>
12478                  <button ng-click="$log.error(message)">error</button>
12479                  <button ng-click="$log.debug(message)">debug</button>
12480                </div>
12481              </file>
12482            </example>
12483          */
12484
12485         /**
12486          * @ngdoc provider
12487          * @name $logProvider
12488          * @description
12489          * Use the `$logProvider` to configure how the application logs messages
12490          */
12491         function $LogProvider() {
12492           var debug = true,
12493               self = this;
12494
12495           /**
12496            * @ngdoc method
12497            * @name $logProvider#debugEnabled
12498            * @description
12499            * @param {boolean=} flag enable or disable debug level messages
12500            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12501            */
12502           this.debugEnabled = function(flag) {
12503             if (isDefined(flag)) {
12504               debug = flag;
12505             return this;
12506             } else {
12507               return debug;
12508             }
12509           };
12510
12511           this.$get = ['$window', function($window) {
12512             return {
12513               /**
12514                * @ngdoc method
12515                * @name $log#log
12516                *
12517                * @description
12518                * Write a log message
12519                */
12520               log: consoleLog('log'),
12521
12522               /**
12523                * @ngdoc method
12524                * @name $log#info
12525                *
12526                * @description
12527                * Write an information message
12528                */
12529               info: consoleLog('info'),
12530
12531               /**
12532                * @ngdoc method
12533                * @name $log#warn
12534                *
12535                * @description
12536                * Write a warning message
12537                */
12538               warn: consoleLog('warn'),
12539
12540               /**
12541                * @ngdoc method
12542                * @name $log#error
12543                *
12544                * @description
12545                * Write an error message
12546                */
12547               error: consoleLog('error'),
12548
12549               /**
12550                * @ngdoc method
12551                * @name $log#debug
12552                *
12553                * @description
12554                * Write a debug message
12555                */
12556               debug: (function() {
12557                 var fn = consoleLog('debug');
12558
12559                 return function() {
12560                   if (debug) {
12561                     fn.apply(self, arguments);
12562                   }
12563                 };
12564               }())
12565             };
12566
12567             function formatError(arg) {
12568               if (arg instanceof Error) {
12569                 if (arg.stack) {
12570                   arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12571                       ? 'Error: ' + arg.message + '\n' + arg.stack
12572                       : arg.stack;
12573                 } else if (arg.sourceURL) {
12574                   arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12575                 }
12576               }
12577               return arg;
12578             }
12579
12580             function consoleLog(type) {
12581               var console = $window.console || {},
12582                   logFn = console[type] || console.log || noop,
12583                   hasApply = false;
12584
12585               // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12586               // The reason behind this is that console.log has type "object" in IE8...
12587               try {
12588                 hasApply = !!logFn.apply;
12589               } catch (e) {}
12590
12591               if (hasApply) {
12592                 return function() {
12593                   var args = [];
12594                   forEach(arguments, function(arg) {
12595                     args.push(formatError(arg));
12596                   });
12597                   return logFn.apply(console, args);
12598                 };
12599               }
12600
12601               // we are IE which either doesn't have window.console => this is noop and we do nothing,
12602               // or we are IE where console.log doesn't have apply so we log at least first 2 args
12603               return function(arg1, arg2) {
12604                 logFn(arg1, arg2 == null ? '' : arg2);
12605               };
12606             }
12607           }];
12608         }
12609
12610         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12611          *     Any commits to this file should be reviewed with security in mind.  *
12612          *   Changes to this file can potentially create security vulnerabilities. *
12613          *          An approval from 2 Core members with history of modifying      *
12614          *                         this file is required.                          *
12615          *                                                                         *
12616          *  Does the change somehow allow for arbitrary javascript to be executed? *
12617          *    Or allows for someone to change the prototype of built-in objects?   *
12618          *     Or gives undesired access to variables likes document or window?    *
12619          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12620
12621         var $parseMinErr = minErr('$parse');
12622
12623         // Sandboxing Angular Expressions
12624         // ------------------------------
12625         // Angular expressions are generally considered safe because these expressions only have direct
12626         // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12627         // obtaining a reference to native JS functions such as the Function constructor.
12628         //
12629         // As an example, consider the following Angular expression:
12630         //
12631         //   {}.toString.constructor('alert("evil JS code")')
12632         //
12633         // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12634         // against the expression language, but not to prevent exploits that were enabled by exposing
12635         // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12636         // practice and therefore we are not even trying to protect against interaction with an object
12637         // explicitly exposed in this way.
12638         //
12639         // In general, it is not possible to access a Window object from an angular expression unless a
12640         // window or some DOM object that has a reference to window is published onto a Scope.
12641         // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12642         // native objects.
12643         //
12644         // See https://docs.angularjs.org/guide/security
12645
12646
12647         function ensureSafeMemberName(name, fullExpression) {
12648           if (name === "__defineGetter__" || name === "__defineSetter__"
12649               || name === "__lookupGetter__" || name === "__lookupSetter__"
12650               || name === "__proto__") {
12651             throw $parseMinErr('isecfld',
12652                 'Attempting to access a disallowed field in Angular expressions! '
12653                 + 'Expression: {0}', fullExpression);
12654           }
12655           return name;
12656         }
12657
12658         function getStringValue(name, fullExpression) {
12659           // From the JavaScript docs:
12660           // Property names must be strings. This means that non-string objects cannot be used
12661           // as keys in an object. Any non-string object, including a number, is typecasted
12662           // into a string via the toString method.
12663           //
12664           // So, to ensure that we are checking the same `name` that JavaScript would use,
12665           // we cast it to a string, if possible.
12666           // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12667           // this is, this will handle objects that misbehave.
12668           name = name + '';
12669           if (!isString(name)) {
12670             throw $parseMinErr('iseccst',
12671                 'Cannot convert object to primitive value! '
12672                 + 'Expression: {0}', fullExpression);
12673           }
12674           return name;
12675         }
12676
12677         function ensureSafeObject(obj, fullExpression) {
12678           // nifty check if obj is Function that is fast and works across iframes and other contexts
12679           if (obj) {
12680             if (obj.constructor === obj) {
12681               throw $parseMinErr('isecfn',
12682                   'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12683                   fullExpression);
12684             } else if (// isWindow(obj)
12685                 obj.window === obj) {
12686               throw $parseMinErr('isecwindow',
12687                   'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12688                   fullExpression);
12689             } else if (// isElement(obj)
12690                 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12691               throw $parseMinErr('isecdom',
12692                   'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12693                   fullExpression);
12694             } else if (// block Object so that we can't get hold of dangerous Object.* methods
12695                 obj === Object) {
12696               throw $parseMinErr('isecobj',
12697                   'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12698                   fullExpression);
12699             }
12700           }
12701           return obj;
12702         }
12703
12704         var CALL = Function.prototype.call;
12705         var APPLY = Function.prototype.apply;
12706         var BIND = Function.prototype.bind;
12707
12708         function ensureSafeFunction(obj, fullExpression) {
12709           if (obj) {
12710             if (obj.constructor === obj) {
12711               throw $parseMinErr('isecfn',
12712                 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12713                 fullExpression);
12714             } else if (obj === CALL || obj === APPLY || obj === BIND) {
12715               throw $parseMinErr('isecff',
12716                 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12717                 fullExpression);
12718             }
12719           }
12720         }
12721
12722         function ensureSafeAssignContext(obj, fullExpression) {
12723           if (obj) {
12724             if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12725                 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12726               throw $parseMinErr('isecaf',
12727                 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12728             }
12729           }
12730         }
12731
12732         var OPERATORS = createMap();
12733         forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12734         var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12735
12736
12737         /////////////////////////////////////////
12738
12739
12740         /**
12741          * @constructor
12742          */
12743         var Lexer = function(options) {
12744           this.options = options;
12745         };
12746
12747         Lexer.prototype = {
12748           constructor: Lexer,
12749
12750           lex: function(text) {
12751             this.text = text;
12752             this.index = 0;
12753             this.tokens = [];
12754
12755             while (this.index < this.text.length) {
12756               var ch = this.text.charAt(this.index);
12757               if (ch === '"' || ch === "'") {
12758                 this.readString(ch);
12759               } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12760                 this.readNumber();
12761               } else if (this.isIdent(ch)) {
12762                 this.readIdent();
12763               } else if (this.is(ch, '(){}[].,;:?')) {
12764                 this.tokens.push({index: this.index, text: ch});
12765                 this.index++;
12766               } else if (this.isWhitespace(ch)) {
12767                 this.index++;
12768               } else {
12769                 var ch2 = ch + this.peek();
12770                 var ch3 = ch2 + this.peek(2);
12771                 var op1 = OPERATORS[ch];
12772                 var op2 = OPERATORS[ch2];
12773                 var op3 = OPERATORS[ch3];
12774                 if (op1 || op2 || op3) {
12775                   var token = op3 ? ch3 : (op2 ? ch2 : ch);
12776                   this.tokens.push({index: this.index, text: token, operator: true});
12777                   this.index += token.length;
12778                 } else {
12779                   this.throwError('Unexpected next character ', this.index, this.index + 1);
12780                 }
12781               }
12782             }
12783             return this.tokens;
12784           },
12785
12786           is: function(ch, chars) {
12787             return chars.indexOf(ch) !== -1;
12788           },
12789
12790           peek: function(i) {
12791             var num = i || 1;
12792             return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12793           },
12794
12795           isNumber: function(ch) {
12796             return ('0' <= ch && ch <= '9') && typeof ch === "string";
12797           },
12798
12799           isWhitespace: function(ch) {
12800             // IE treats non-breaking space as \u00A0
12801             return (ch === ' ' || ch === '\r' || ch === '\t' ||
12802                     ch === '\n' || ch === '\v' || ch === '\u00A0');
12803           },
12804
12805           isIdent: function(ch) {
12806             return ('a' <= ch && ch <= 'z' ||
12807                     'A' <= ch && ch <= 'Z' ||
12808                     '_' === ch || ch === '$');
12809           },
12810
12811           isExpOperator: function(ch) {
12812             return (ch === '-' || ch === '+' || this.isNumber(ch));
12813           },
12814
12815           throwError: function(error, start, end) {
12816             end = end || this.index;
12817             var colStr = (isDefined(start)
12818                     ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12819                     : ' ' + end);
12820             throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12821                 error, colStr, this.text);
12822           },
12823
12824           readNumber: function() {
12825             var number = '';
12826             var start = this.index;
12827             while (this.index < this.text.length) {
12828               var ch = lowercase(this.text.charAt(this.index));
12829               if (ch == '.' || this.isNumber(ch)) {
12830                 number += ch;
12831               } else {
12832                 var peekCh = this.peek();
12833                 if (ch == 'e' && this.isExpOperator(peekCh)) {
12834                   number += ch;
12835                 } else if (this.isExpOperator(ch) &&
12836                     peekCh && this.isNumber(peekCh) &&
12837                     number.charAt(number.length - 1) == 'e') {
12838                   number += ch;
12839                 } else if (this.isExpOperator(ch) &&
12840                     (!peekCh || !this.isNumber(peekCh)) &&
12841                     number.charAt(number.length - 1) == 'e') {
12842                   this.throwError('Invalid exponent');
12843                 } else {
12844                   break;
12845                 }
12846               }
12847               this.index++;
12848             }
12849             this.tokens.push({
12850               index: start,
12851               text: number,
12852               constant: true,
12853               value: Number(number)
12854             });
12855           },
12856
12857           readIdent: function() {
12858             var start = this.index;
12859             while (this.index < this.text.length) {
12860               var ch = this.text.charAt(this.index);
12861               if (!(this.isIdent(ch) || this.isNumber(ch))) {
12862                 break;
12863               }
12864               this.index++;
12865             }
12866             this.tokens.push({
12867               index: start,
12868               text: this.text.slice(start, this.index),
12869               identifier: true
12870             });
12871           },
12872
12873           readString: function(quote) {
12874             var start = this.index;
12875             this.index++;
12876             var string = '';
12877             var rawString = quote;
12878             var escape = false;
12879             while (this.index < this.text.length) {
12880               var ch = this.text.charAt(this.index);
12881               rawString += ch;
12882               if (escape) {
12883                 if (ch === 'u') {
12884                   var hex = this.text.substring(this.index + 1, this.index + 5);
12885                   if (!hex.match(/[\da-f]{4}/i)) {
12886                     this.throwError('Invalid unicode escape [\\u' + hex + ']');
12887                   }
12888                   this.index += 4;
12889                   string += String.fromCharCode(parseInt(hex, 16));
12890                 } else {
12891                   var rep = ESCAPE[ch];
12892                   string = string + (rep || ch);
12893                 }
12894                 escape = false;
12895               } else if (ch === '\\') {
12896                 escape = true;
12897               } else if (ch === quote) {
12898                 this.index++;
12899                 this.tokens.push({
12900                   index: start,
12901                   text: rawString,
12902                   constant: true,
12903                   value: string
12904                 });
12905                 return;
12906               } else {
12907                 string += ch;
12908               }
12909               this.index++;
12910             }
12911             this.throwError('Unterminated quote', start);
12912           }
12913         };
12914
12915         var AST = function(lexer, options) {
12916           this.lexer = lexer;
12917           this.options = options;
12918         };
12919
12920         AST.Program = 'Program';
12921         AST.ExpressionStatement = 'ExpressionStatement';
12922         AST.AssignmentExpression = 'AssignmentExpression';
12923         AST.ConditionalExpression = 'ConditionalExpression';
12924         AST.LogicalExpression = 'LogicalExpression';
12925         AST.BinaryExpression = 'BinaryExpression';
12926         AST.UnaryExpression = 'UnaryExpression';
12927         AST.CallExpression = 'CallExpression';
12928         AST.MemberExpression = 'MemberExpression';
12929         AST.Identifier = 'Identifier';
12930         AST.Literal = 'Literal';
12931         AST.ArrayExpression = 'ArrayExpression';
12932         AST.Property = 'Property';
12933         AST.ObjectExpression = 'ObjectExpression';
12934         AST.ThisExpression = 'ThisExpression';
12935
12936         // Internal use only
12937         AST.NGValueParameter = 'NGValueParameter';
12938
12939         AST.prototype = {
12940           ast: function(text) {
12941             this.text = text;
12942             this.tokens = this.lexer.lex(text);
12943
12944             var value = this.program();
12945
12946             if (this.tokens.length !== 0) {
12947               this.throwError('is an unexpected token', this.tokens[0]);
12948             }
12949
12950             return value;
12951           },
12952
12953           program: function() {
12954             var body = [];
12955             while (true) {
12956               if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12957                 body.push(this.expressionStatement());
12958               if (!this.expect(';')) {
12959                 return { type: AST.Program, body: body};
12960               }
12961             }
12962           },
12963
12964           expressionStatement: function() {
12965             return { type: AST.ExpressionStatement, expression: this.filterChain() };
12966           },
12967
12968           filterChain: function() {
12969             var left = this.expression();
12970             var token;
12971             while ((token = this.expect('|'))) {
12972               left = this.filter(left);
12973             }
12974             return left;
12975           },
12976
12977           expression: function() {
12978             return this.assignment();
12979           },
12980
12981           assignment: function() {
12982             var result = this.ternary();
12983             if (this.expect('=')) {
12984               result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12985             }
12986             return result;
12987           },
12988
12989           ternary: function() {
12990             var test = this.logicalOR();
12991             var alternate;
12992             var consequent;
12993             if (this.expect('?')) {
12994               alternate = this.expression();
12995               if (this.consume(':')) {
12996                 consequent = this.expression();
12997                 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12998               }
12999             }
13000             return test;
13001           },
13002
13003           logicalOR: function() {
13004             var left = this.logicalAND();
13005             while (this.expect('||')) {
13006               left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13007             }
13008             return left;
13009           },
13010
13011           logicalAND: function() {
13012             var left = this.equality();
13013             while (this.expect('&&')) {
13014               left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13015             }
13016             return left;
13017           },
13018
13019           equality: function() {
13020             var left = this.relational();
13021             var token;
13022             while ((token = this.expect('==','!=','===','!=='))) {
13023               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13024             }
13025             return left;
13026           },
13027
13028           relational: function() {
13029             var left = this.additive();
13030             var token;
13031             while ((token = this.expect('<', '>', '<=', '>='))) {
13032               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13033             }
13034             return left;
13035           },
13036
13037           additive: function() {
13038             var left = this.multiplicative();
13039             var token;
13040             while ((token = this.expect('+','-'))) {
13041               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13042             }
13043             return left;
13044           },
13045
13046           multiplicative: function() {
13047             var left = this.unary();
13048             var token;
13049             while ((token = this.expect('*','/','%'))) {
13050               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13051             }
13052             return left;
13053           },
13054
13055           unary: function() {
13056             var token;
13057             if ((token = this.expect('+', '-', '!'))) {
13058               return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13059             } else {
13060               return this.primary();
13061             }
13062           },
13063
13064           primary: function() {
13065             var primary;
13066             if (this.expect('(')) {
13067               primary = this.filterChain();
13068               this.consume(')');
13069             } else if (this.expect('[')) {
13070               primary = this.arrayDeclaration();
13071             } else if (this.expect('{')) {
13072               primary = this.object();
13073             } else if (this.constants.hasOwnProperty(this.peek().text)) {
13074               primary = copy(this.constants[this.consume().text]);
13075             } else if (this.peek().identifier) {
13076               primary = this.identifier();
13077             } else if (this.peek().constant) {
13078               primary = this.constant();
13079             } else {
13080               this.throwError('not a primary expression', this.peek());
13081             }
13082
13083             var next;
13084             while ((next = this.expect('(', '[', '.'))) {
13085               if (next.text === '(') {
13086                 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13087                 this.consume(')');
13088               } else if (next.text === '[') {
13089                 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13090                 this.consume(']');
13091               } else if (next.text === '.') {
13092                 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13093               } else {
13094                 this.throwError('IMPOSSIBLE');
13095               }
13096             }
13097             return primary;
13098           },
13099
13100           filter: function(baseExpression) {
13101             var args = [baseExpression];
13102             var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13103
13104             while (this.expect(':')) {
13105               args.push(this.expression());
13106             }
13107
13108             return result;
13109           },
13110
13111           parseArguments: function() {
13112             var args = [];
13113             if (this.peekToken().text !== ')') {
13114               do {
13115                 args.push(this.expression());
13116               } while (this.expect(','));
13117             }
13118             return args;
13119           },
13120
13121           identifier: function() {
13122             var token = this.consume();
13123             if (!token.identifier) {
13124               this.throwError('is not a valid identifier', token);
13125             }
13126             return { type: AST.Identifier, name: token.text };
13127           },
13128
13129           constant: function() {
13130             // TODO check that it is a constant
13131             return { type: AST.Literal, value: this.consume().value };
13132           },
13133
13134           arrayDeclaration: function() {
13135             var elements = [];
13136             if (this.peekToken().text !== ']') {
13137               do {
13138                 if (this.peek(']')) {
13139                   // Support trailing commas per ES5.1.
13140                   break;
13141                 }
13142                 elements.push(this.expression());
13143               } while (this.expect(','));
13144             }
13145             this.consume(']');
13146
13147             return { type: AST.ArrayExpression, elements: elements };
13148           },
13149
13150           object: function() {
13151             var properties = [], property;
13152             if (this.peekToken().text !== '}') {
13153               do {
13154                 if (this.peek('}')) {
13155                   // Support trailing commas per ES5.1.
13156                   break;
13157                 }
13158                 property = {type: AST.Property, kind: 'init'};
13159                 if (this.peek().constant) {
13160                   property.key = this.constant();
13161                 } else if (this.peek().identifier) {
13162                   property.key = this.identifier();
13163                 } else {
13164                   this.throwError("invalid key", this.peek());
13165                 }
13166                 this.consume(':');
13167                 property.value = this.expression();
13168                 properties.push(property);
13169               } while (this.expect(','));
13170             }
13171             this.consume('}');
13172
13173             return {type: AST.ObjectExpression, properties: properties };
13174           },
13175
13176           throwError: function(msg, token) {
13177             throw $parseMinErr('syntax',
13178                 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13179                   token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13180           },
13181
13182           consume: function(e1) {
13183             if (this.tokens.length === 0) {
13184               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13185             }
13186
13187             var token = this.expect(e1);
13188             if (!token) {
13189               this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13190             }
13191             return token;
13192           },
13193
13194           peekToken: function() {
13195             if (this.tokens.length === 0) {
13196               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13197             }
13198             return this.tokens[0];
13199           },
13200
13201           peek: function(e1, e2, e3, e4) {
13202             return this.peekAhead(0, e1, e2, e3, e4);
13203           },
13204
13205           peekAhead: function(i, e1, e2, e3, e4) {
13206             if (this.tokens.length > i) {
13207               var token = this.tokens[i];
13208               var t = token.text;
13209               if (t === e1 || t === e2 || t === e3 || t === e4 ||
13210                   (!e1 && !e2 && !e3 && !e4)) {
13211                 return token;
13212               }
13213             }
13214             return false;
13215           },
13216
13217           expect: function(e1, e2, e3, e4) {
13218             var token = this.peek(e1, e2, e3, e4);
13219             if (token) {
13220               this.tokens.shift();
13221               return token;
13222             }
13223             return false;
13224           },
13225
13226
13227           /* `undefined` is not a constant, it is an identifier,
13228            * but using it as an identifier is not supported
13229            */
13230           constants: {
13231             'true': { type: AST.Literal, value: true },
13232             'false': { type: AST.Literal, value: false },
13233             'null': { type: AST.Literal, value: null },
13234             'undefined': {type: AST.Literal, value: undefined },
13235             'this': {type: AST.ThisExpression }
13236           }
13237         };
13238
13239         function ifDefined(v, d) {
13240           return typeof v !== 'undefined' ? v : d;
13241         }
13242
13243         function plusFn(l, r) {
13244           if (typeof l === 'undefined') return r;
13245           if (typeof r === 'undefined') return l;
13246           return l + r;
13247         }
13248
13249         function isStateless($filter, filterName) {
13250           var fn = $filter(filterName);
13251           return !fn.$stateful;
13252         }
13253
13254         function findConstantAndWatchExpressions(ast, $filter) {
13255           var allConstants;
13256           var argsToWatch;
13257           switch (ast.type) {
13258           case AST.Program:
13259             allConstants = true;
13260             forEach(ast.body, function(expr) {
13261               findConstantAndWatchExpressions(expr.expression, $filter);
13262               allConstants = allConstants && expr.expression.constant;
13263             });
13264             ast.constant = allConstants;
13265             break;
13266           case AST.Literal:
13267             ast.constant = true;
13268             ast.toWatch = [];
13269             break;
13270           case AST.UnaryExpression:
13271             findConstantAndWatchExpressions(ast.argument, $filter);
13272             ast.constant = ast.argument.constant;
13273             ast.toWatch = ast.argument.toWatch;
13274             break;
13275           case AST.BinaryExpression:
13276             findConstantAndWatchExpressions(ast.left, $filter);
13277             findConstantAndWatchExpressions(ast.right, $filter);
13278             ast.constant = ast.left.constant && ast.right.constant;
13279             ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13280             break;
13281           case AST.LogicalExpression:
13282             findConstantAndWatchExpressions(ast.left, $filter);
13283             findConstantAndWatchExpressions(ast.right, $filter);
13284             ast.constant = ast.left.constant && ast.right.constant;
13285             ast.toWatch = ast.constant ? [] : [ast];
13286             break;
13287           case AST.ConditionalExpression:
13288             findConstantAndWatchExpressions(ast.test, $filter);
13289             findConstantAndWatchExpressions(ast.alternate, $filter);
13290             findConstantAndWatchExpressions(ast.consequent, $filter);
13291             ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13292             ast.toWatch = ast.constant ? [] : [ast];
13293             break;
13294           case AST.Identifier:
13295             ast.constant = false;
13296             ast.toWatch = [ast];
13297             break;
13298           case AST.MemberExpression:
13299             findConstantAndWatchExpressions(ast.object, $filter);
13300             if (ast.computed) {
13301               findConstantAndWatchExpressions(ast.property, $filter);
13302             }
13303             ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13304             ast.toWatch = [ast];
13305             break;
13306           case AST.CallExpression:
13307             allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13308             argsToWatch = [];
13309             forEach(ast.arguments, function(expr) {
13310               findConstantAndWatchExpressions(expr, $filter);
13311               allConstants = allConstants && expr.constant;
13312               if (!expr.constant) {
13313                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13314               }
13315             });
13316             ast.constant = allConstants;
13317             ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13318             break;
13319           case AST.AssignmentExpression:
13320             findConstantAndWatchExpressions(ast.left, $filter);
13321             findConstantAndWatchExpressions(ast.right, $filter);
13322             ast.constant = ast.left.constant && ast.right.constant;
13323             ast.toWatch = [ast];
13324             break;
13325           case AST.ArrayExpression:
13326             allConstants = true;
13327             argsToWatch = [];
13328             forEach(ast.elements, function(expr) {
13329               findConstantAndWatchExpressions(expr, $filter);
13330               allConstants = allConstants && expr.constant;
13331               if (!expr.constant) {
13332                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13333               }
13334             });
13335             ast.constant = allConstants;
13336             ast.toWatch = argsToWatch;
13337             break;
13338           case AST.ObjectExpression:
13339             allConstants = true;
13340             argsToWatch = [];
13341             forEach(ast.properties, function(property) {
13342               findConstantAndWatchExpressions(property.value, $filter);
13343               allConstants = allConstants && property.value.constant;
13344               if (!property.value.constant) {
13345                 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13346               }
13347             });
13348             ast.constant = allConstants;
13349             ast.toWatch = argsToWatch;
13350             break;
13351           case AST.ThisExpression:
13352             ast.constant = false;
13353             ast.toWatch = [];
13354             break;
13355           }
13356         }
13357
13358         function getInputs(body) {
13359           if (body.length != 1) return;
13360           var lastExpression = body[0].expression;
13361           var candidate = lastExpression.toWatch;
13362           if (candidate.length !== 1) return candidate;
13363           return candidate[0] !== lastExpression ? candidate : undefined;
13364         }
13365
13366         function isAssignable(ast) {
13367           return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13368         }
13369
13370         function assignableAST(ast) {
13371           if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13372             return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13373           }
13374         }
13375
13376         function isLiteral(ast) {
13377           return ast.body.length === 0 ||
13378               ast.body.length === 1 && (
13379               ast.body[0].expression.type === AST.Literal ||
13380               ast.body[0].expression.type === AST.ArrayExpression ||
13381               ast.body[0].expression.type === AST.ObjectExpression);
13382         }
13383
13384         function isConstant(ast) {
13385           return ast.constant;
13386         }
13387
13388         function ASTCompiler(astBuilder, $filter) {
13389           this.astBuilder = astBuilder;
13390           this.$filter = $filter;
13391         }
13392
13393         ASTCompiler.prototype = {
13394           compile: function(expression, expensiveChecks) {
13395             var self = this;
13396             var ast = this.astBuilder.ast(expression);
13397             this.state = {
13398               nextId: 0,
13399               filters: {},
13400               expensiveChecks: expensiveChecks,
13401               fn: {vars: [], body: [], own: {}},
13402               assign: {vars: [], body: [], own: {}},
13403               inputs: []
13404             };
13405             findConstantAndWatchExpressions(ast, self.$filter);
13406             var extra = '';
13407             var assignable;
13408             this.stage = 'assign';
13409             if ((assignable = assignableAST(ast))) {
13410               this.state.computing = 'assign';
13411               var result = this.nextId();
13412               this.recurse(assignable, result);
13413               this.return_(result);
13414               extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13415             }
13416             var toWatch = getInputs(ast.body);
13417             self.stage = 'inputs';
13418             forEach(toWatch, function(watch, key) {
13419               var fnKey = 'fn' + key;
13420               self.state[fnKey] = {vars: [], body: [], own: {}};
13421               self.state.computing = fnKey;
13422               var intoId = self.nextId();
13423               self.recurse(watch, intoId);
13424               self.return_(intoId);
13425               self.state.inputs.push(fnKey);
13426               watch.watchId = key;
13427             });
13428             this.state.computing = 'fn';
13429             this.stage = 'main';
13430             this.recurse(ast);
13431             var fnString =
13432               // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13433               // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13434               '"' + this.USE + ' ' + this.STRICT + '";\n' +
13435               this.filterPrefix() +
13436               'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13437               extra +
13438               this.watchFns() +
13439               'return fn;';
13440
13441             /* jshint -W054 */
13442             var fn = (new Function('$filter',
13443                 'ensureSafeMemberName',
13444                 'ensureSafeObject',
13445                 'ensureSafeFunction',
13446                 'getStringValue',
13447                 'ensureSafeAssignContext',
13448                 'ifDefined',
13449                 'plus',
13450                 'text',
13451                 fnString))(
13452                   this.$filter,
13453                   ensureSafeMemberName,
13454                   ensureSafeObject,
13455                   ensureSafeFunction,
13456                   getStringValue,
13457                   ensureSafeAssignContext,
13458                   ifDefined,
13459                   plusFn,
13460                   expression);
13461             /* jshint +W054 */
13462             this.state = this.stage = undefined;
13463             fn.literal = isLiteral(ast);
13464             fn.constant = isConstant(ast);
13465             return fn;
13466           },
13467
13468           USE: 'use',
13469
13470           STRICT: 'strict',
13471
13472           watchFns: function() {
13473             var result = [];
13474             var fns = this.state.inputs;
13475             var self = this;
13476             forEach(fns, function(name) {
13477               result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13478             });
13479             if (fns.length) {
13480               result.push('fn.inputs=[' + fns.join(',') + '];');
13481             }
13482             return result.join('');
13483           },
13484
13485           generateFunction: function(name, params) {
13486             return 'function(' + params + '){' +
13487                 this.varsPrefix(name) +
13488                 this.body(name) +
13489                 '};';
13490           },
13491
13492           filterPrefix: function() {
13493             var parts = [];
13494             var self = this;
13495             forEach(this.state.filters, function(id, filter) {
13496               parts.push(id + '=$filter(' + self.escape(filter) + ')');
13497             });
13498             if (parts.length) return 'var ' + parts.join(',') + ';';
13499             return '';
13500           },
13501
13502           varsPrefix: function(section) {
13503             return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13504           },
13505
13506           body: function(section) {
13507             return this.state[section].body.join('');
13508           },
13509
13510           recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13511             var left, right, self = this, args, expression;
13512             recursionFn = recursionFn || noop;
13513             if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13514               intoId = intoId || this.nextId();
13515               this.if_('i',
13516                 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13517                 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13518               );
13519               return;
13520             }
13521             switch (ast.type) {
13522             case AST.Program:
13523               forEach(ast.body, function(expression, pos) {
13524                 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13525                 if (pos !== ast.body.length - 1) {
13526                   self.current().body.push(right, ';');
13527                 } else {
13528                   self.return_(right);
13529                 }
13530               });
13531               break;
13532             case AST.Literal:
13533               expression = this.escape(ast.value);
13534               this.assign(intoId, expression);
13535               recursionFn(expression);
13536               break;
13537             case AST.UnaryExpression:
13538               this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13539               expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13540               this.assign(intoId, expression);
13541               recursionFn(expression);
13542               break;
13543             case AST.BinaryExpression:
13544               this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13545               this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13546               if (ast.operator === '+') {
13547                 expression = this.plus(left, right);
13548               } else if (ast.operator === '-') {
13549                 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13550               } else {
13551                 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13552               }
13553               this.assign(intoId, expression);
13554               recursionFn(expression);
13555               break;
13556             case AST.LogicalExpression:
13557               intoId = intoId || this.nextId();
13558               self.recurse(ast.left, intoId);
13559               self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13560               recursionFn(intoId);
13561               break;
13562             case AST.ConditionalExpression:
13563               intoId = intoId || this.nextId();
13564               self.recurse(ast.test, intoId);
13565               self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13566               recursionFn(intoId);
13567               break;
13568             case AST.Identifier:
13569               intoId = intoId || this.nextId();
13570               if (nameId) {
13571                 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13572                 nameId.computed = false;
13573                 nameId.name = ast.name;
13574               }
13575               ensureSafeMemberName(ast.name);
13576               self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13577                 function() {
13578                   self.if_(self.stage === 'inputs' || 's', function() {
13579                     if (create && create !== 1) {
13580                       self.if_(
13581                         self.not(self.nonComputedMember('s', ast.name)),
13582                         self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13583                     }
13584                     self.assign(intoId, self.nonComputedMember('s', ast.name));
13585                   });
13586                 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13587                 );
13588               if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13589                 self.addEnsureSafeObject(intoId);
13590               }
13591               recursionFn(intoId);
13592               break;
13593             case AST.MemberExpression:
13594               left = nameId && (nameId.context = this.nextId()) || this.nextId();
13595               intoId = intoId || this.nextId();
13596               self.recurse(ast.object, left, undefined, function() {
13597                 self.if_(self.notNull(left), function() {
13598                   if (ast.computed) {
13599                     right = self.nextId();
13600                     self.recurse(ast.property, right);
13601                     self.getStringValue(right);
13602                     self.addEnsureSafeMemberName(right);
13603                     if (create && create !== 1) {
13604                       self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13605                     }
13606                     expression = self.ensureSafeObject(self.computedMember(left, right));
13607                     self.assign(intoId, expression);
13608                     if (nameId) {
13609                       nameId.computed = true;
13610                       nameId.name = right;
13611                     }
13612                   } else {
13613                     ensureSafeMemberName(ast.property.name);
13614                     if (create && create !== 1) {
13615                       self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13616                     }
13617                     expression = self.nonComputedMember(left, ast.property.name);
13618                     if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13619                       expression = self.ensureSafeObject(expression);
13620                     }
13621                     self.assign(intoId, expression);
13622                     if (nameId) {
13623                       nameId.computed = false;
13624                       nameId.name = ast.property.name;
13625                     }
13626                   }
13627                 }, function() {
13628                   self.assign(intoId, 'undefined');
13629                 });
13630                 recursionFn(intoId);
13631               }, !!create);
13632               break;
13633             case AST.CallExpression:
13634               intoId = intoId || this.nextId();
13635               if (ast.filter) {
13636                 right = self.filter(ast.callee.name);
13637                 args = [];
13638                 forEach(ast.arguments, function(expr) {
13639                   var argument = self.nextId();
13640                   self.recurse(expr, argument);
13641                   args.push(argument);
13642                 });
13643                 expression = right + '(' + args.join(',') + ')';
13644                 self.assign(intoId, expression);
13645                 recursionFn(intoId);
13646               } else {
13647                 right = self.nextId();
13648                 left = {};
13649                 args = [];
13650                 self.recurse(ast.callee, right, left, function() {
13651                   self.if_(self.notNull(right), function() {
13652                     self.addEnsureSafeFunction(right);
13653                     forEach(ast.arguments, function(expr) {
13654                       self.recurse(expr, self.nextId(), undefined, function(argument) {
13655                         args.push(self.ensureSafeObject(argument));
13656                       });
13657                     });
13658                     if (left.name) {
13659                       if (!self.state.expensiveChecks) {
13660                         self.addEnsureSafeObject(left.context);
13661                       }
13662                       expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13663                     } else {
13664                       expression = right + '(' + args.join(',') + ')';
13665                     }
13666                     expression = self.ensureSafeObject(expression);
13667                     self.assign(intoId, expression);
13668                   }, function() {
13669                     self.assign(intoId, 'undefined');
13670                   });
13671                   recursionFn(intoId);
13672                 });
13673               }
13674               break;
13675             case AST.AssignmentExpression:
13676               right = this.nextId();
13677               left = {};
13678               if (!isAssignable(ast.left)) {
13679                 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13680               }
13681               this.recurse(ast.left, undefined, left, function() {
13682                 self.if_(self.notNull(left.context), function() {
13683                   self.recurse(ast.right, right);
13684                   self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13685                   self.addEnsureSafeAssignContext(left.context);
13686                   expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13687                   self.assign(intoId, expression);
13688                   recursionFn(intoId || expression);
13689                 });
13690               }, 1);
13691               break;
13692             case AST.ArrayExpression:
13693               args = [];
13694               forEach(ast.elements, function(expr) {
13695                 self.recurse(expr, self.nextId(), undefined, function(argument) {
13696                   args.push(argument);
13697                 });
13698               });
13699               expression = '[' + args.join(',') + ']';
13700               this.assign(intoId, expression);
13701               recursionFn(expression);
13702               break;
13703             case AST.ObjectExpression:
13704               args = [];
13705               forEach(ast.properties, function(property) {
13706                 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13707                   args.push(self.escape(
13708                       property.key.type === AST.Identifier ? property.key.name :
13709                         ('' + property.key.value)) +
13710                       ':' + expr);
13711                 });
13712               });
13713               expression = '{' + args.join(',') + '}';
13714               this.assign(intoId, expression);
13715               recursionFn(expression);
13716               break;
13717             case AST.ThisExpression:
13718               this.assign(intoId, 's');
13719               recursionFn('s');
13720               break;
13721             case AST.NGValueParameter:
13722               this.assign(intoId, 'v');
13723               recursionFn('v');
13724               break;
13725             }
13726           },
13727
13728           getHasOwnProperty: function(element, property) {
13729             var key = element + '.' + property;
13730             var own = this.current().own;
13731             if (!own.hasOwnProperty(key)) {
13732               own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13733             }
13734             return own[key];
13735           },
13736
13737           assign: function(id, value) {
13738             if (!id) return;
13739             this.current().body.push(id, '=', value, ';');
13740             return id;
13741           },
13742
13743           filter: function(filterName) {
13744             if (!this.state.filters.hasOwnProperty(filterName)) {
13745               this.state.filters[filterName] = this.nextId(true);
13746             }
13747             return this.state.filters[filterName];
13748           },
13749
13750           ifDefined: function(id, defaultValue) {
13751             return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13752           },
13753
13754           plus: function(left, right) {
13755             return 'plus(' + left + ',' + right + ')';
13756           },
13757
13758           return_: function(id) {
13759             this.current().body.push('return ', id, ';');
13760           },
13761
13762           if_: function(test, alternate, consequent) {
13763             if (test === true) {
13764               alternate();
13765             } else {
13766               var body = this.current().body;
13767               body.push('if(', test, '){');
13768               alternate();
13769               body.push('}');
13770               if (consequent) {
13771                 body.push('else{');
13772                 consequent();
13773                 body.push('}');
13774               }
13775             }
13776           },
13777
13778           not: function(expression) {
13779             return '!(' + expression + ')';
13780           },
13781
13782           notNull: function(expression) {
13783             return expression + '!=null';
13784           },
13785
13786           nonComputedMember: function(left, right) {
13787             return left + '.' + right;
13788           },
13789
13790           computedMember: function(left, right) {
13791             return left + '[' + right + ']';
13792           },
13793
13794           member: function(left, right, computed) {
13795             if (computed) return this.computedMember(left, right);
13796             return this.nonComputedMember(left, right);
13797           },
13798
13799           addEnsureSafeObject: function(item) {
13800             this.current().body.push(this.ensureSafeObject(item), ';');
13801           },
13802
13803           addEnsureSafeMemberName: function(item) {
13804             this.current().body.push(this.ensureSafeMemberName(item), ';');
13805           },
13806
13807           addEnsureSafeFunction: function(item) {
13808             this.current().body.push(this.ensureSafeFunction(item), ';');
13809           },
13810
13811           addEnsureSafeAssignContext: function(item) {
13812             this.current().body.push(this.ensureSafeAssignContext(item), ';');
13813           },
13814
13815           ensureSafeObject: function(item) {
13816             return 'ensureSafeObject(' + item + ',text)';
13817           },
13818
13819           ensureSafeMemberName: function(item) {
13820             return 'ensureSafeMemberName(' + item + ',text)';
13821           },
13822
13823           ensureSafeFunction: function(item) {
13824             return 'ensureSafeFunction(' + item + ',text)';
13825           },
13826
13827           getStringValue: function(item) {
13828             this.assign(item, 'getStringValue(' + item + ',text)');
13829           },
13830
13831           ensureSafeAssignContext: function(item) {
13832             return 'ensureSafeAssignContext(' + item + ',text)';
13833           },
13834
13835           lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13836             var self = this;
13837             return function() {
13838               self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13839             };
13840           },
13841
13842           lazyAssign: function(id, value) {
13843             var self = this;
13844             return function() {
13845               self.assign(id, value);
13846             };
13847           },
13848
13849           stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13850
13851           stringEscapeFn: function(c) {
13852             return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13853           },
13854
13855           escape: function(value) {
13856             if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13857             if (isNumber(value)) return value.toString();
13858             if (value === true) return 'true';
13859             if (value === false) return 'false';
13860             if (value === null) return 'null';
13861             if (typeof value === 'undefined') return 'undefined';
13862
13863             throw $parseMinErr('esc', 'IMPOSSIBLE');
13864           },
13865
13866           nextId: function(skip, init) {
13867             var id = 'v' + (this.state.nextId++);
13868             if (!skip) {
13869               this.current().vars.push(id + (init ? '=' + init : ''));
13870             }
13871             return id;
13872           },
13873
13874           current: function() {
13875             return this.state[this.state.computing];
13876           }
13877         };
13878
13879
13880         function ASTInterpreter(astBuilder, $filter) {
13881           this.astBuilder = astBuilder;
13882           this.$filter = $filter;
13883         }
13884
13885         ASTInterpreter.prototype = {
13886           compile: function(expression, expensiveChecks) {
13887             var self = this;
13888             var ast = this.astBuilder.ast(expression);
13889             this.expression = expression;
13890             this.expensiveChecks = expensiveChecks;
13891             findConstantAndWatchExpressions(ast, self.$filter);
13892             var assignable;
13893             var assign;
13894             if ((assignable = assignableAST(ast))) {
13895               assign = this.recurse(assignable);
13896             }
13897             var toWatch = getInputs(ast.body);
13898             var inputs;
13899             if (toWatch) {
13900               inputs = [];
13901               forEach(toWatch, function(watch, key) {
13902                 var input = self.recurse(watch);
13903                 watch.input = input;
13904                 inputs.push(input);
13905                 watch.watchId = key;
13906               });
13907             }
13908             var expressions = [];
13909             forEach(ast.body, function(expression) {
13910               expressions.push(self.recurse(expression.expression));
13911             });
13912             var fn = ast.body.length === 0 ? function() {} :
13913                      ast.body.length === 1 ? expressions[0] :
13914                      function(scope, locals) {
13915                        var lastValue;
13916                        forEach(expressions, function(exp) {
13917                          lastValue = exp(scope, locals);
13918                        });
13919                        return lastValue;
13920                      };
13921             if (assign) {
13922               fn.assign = function(scope, value, locals) {
13923                 return assign(scope, locals, value);
13924               };
13925             }
13926             if (inputs) {
13927               fn.inputs = inputs;
13928             }
13929             fn.literal = isLiteral(ast);
13930             fn.constant = isConstant(ast);
13931             return fn;
13932           },
13933
13934           recurse: function(ast, context, create) {
13935             var left, right, self = this, args, expression;
13936             if (ast.input) {
13937               return this.inputs(ast.input, ast.watchId);
13938             }
13939             switch (ast.type) {
13940             case AST.Literal:
13941               return this.value(ast.value, context);
13942             case AST.UnaryExpression:
13943               right = this.recurse(ast.argument);
13944               return this['unary' + ast.operator](right, context);
13945             case AST.BinaryExpression:
13946               left = this.recurse(ast.left);
13947               right = this.recurse(ast.right);
13948               return this['binary' + ast.operator](left, right, context);
13949             case AST.LogicalExpression:
13950               left = this.recurse(ast.left);
13951               right = this.recurse(ast.right);
13952               return this['binary' + ast.operator](left, right, context);
13953             case AST.ConditionalExpression:
13954               return this['ternary?:'](
13955                 this.recurse(ast.test),
13956                 this.recurse(ast.alternate),
13957                 this.recurse(ast.consequent),
13958                 context
13959               );
13960             case AST.Identifier:
13961               ensureSafeMemberName(ast.name, self.expression);
13962               return self.identifier(ast.name,
13963                                      self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13964                                      context, create, self.expression);
13965             case AST.MemberExpression:
13966               left = this.recurse(ast.object, false, !!create);
13967               if (!ast.computed) {
13968                 ensureSafeMemberName(ast.property.name, self.expression);
13969                 right = ast.property.name;
13970               }
13971               if (ast.computed) right = this.recurse(ast.property);
13972               return ast.computed ?
13973                 this.computedMember(left, right, context, create, self.expression) :
13974                 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13975             case AST.CallExpression:
13976               args = [];
13977               forEach(ast.arguments, function(expr) {
13978                 args.push(self.recurse(expr));
13979               });
13980               if (ast.filter) right = this.$filter(ast.callee.name);
13981               if (!ast.filter) right = this.recurse(ast.callee, true);
13982               return ast.filter ?
13983                 function(scope, locals, assign, inputs) {
13984                   var values = [];
13985                   for (var i = 0; i < args.length; ++i) {
13986                     values.push(args[i](scope, locals, assign, inputs));
13987                   }
13988                   var value = right.apply(undefined, values, inputs);
13989                   return context ? {context: undefined, name: undefined, value: value} : value;
13990                 } :
13991                 function(scope, locals, assign, inputs) {
13992                   var rhs = right(scope, locals, assign, inputs);
13993                   var value;
13994                   if (rhs.value != null) {
13995                     ensureSafeObject(rhs.context, self.expression);
13996                     ensureSafeFunction(rhs.value, self.expression);
13997                     var values = [];
13998                     for (var i = 0; i < args.length; ++i) {
13999                       values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14000                     }
14001                     value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14002                   }
14003                   return context ? {value: value} : value;
14004                 };
14005             case AST.AssignmentExpression:
14006               left = this.recurse(ast.left, true, 1);
14007               right = this.recurse(ast.right);
14008               return function(scope, locals, assign, inputs) {
14009                 var lhs = left(scope, locals, assign, inputs);
14010                 var rhs = right(scope, locals, assign, inputs);
14011                 ensureSafeObject(lhs.value, self.expression);
14012                 ensureSafeAssignContext(lhs.context);
14013                 lhs.context[lhs.name] = rhs;
14014                 return context ? {value: rhs} : rhs;
14015               };
14016             case AST.ArrayExpression:
14017               args = [];
14018               forEach(ast.elements, function(expr) {
14019                 args.push(self.recurse(expr));
14020               });
14021               return function(scope, locals, assign, inputs) {
14022                 var value = [];
14023                 for (var i = 0; i < args.length; ++i) {
14024                   value.push(args[i](scope, locals, assign, inputs));
14025                 }
14026                 return context ? {value: value} : value;
14027               };
14028             case AST.ObjectExpression:
14029               args = [];
14030               forEach(ast.properties, function(property) {
14031                 args.push({key: property.key.type === AST.Identifier ?
14032                                 property.key.name :
14033                                 ('' + property.key.value),
14034                            value: self.recurse(property.value)
14035                 });
14036               });
14037               return function(scope, locals, assign, inputs) {
14038                 var value = {};
14039                 for (var i = 0; i < args.length; ++i) {
14040                   value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14041                 }
14042                 return context ? {value: value} : value;
14043               };
14044             case AST.ThisExpression:
14045               return function(scope) {
14046                 return context ? {value: scope} : scope;
14047               };
14048             case AST.NGValueParameter:
14049               return function(scope, locals, assign, inputs) {
14050                 return context ? {value: assign} : assign;
14051               };
14052             }
14053           },
14054
14055           'unary+': function(argument, context) {
14056             return function(scope, locals, assign, inputs) {
14057               var arg = argument(scope, locals, assign, inputs);
14058               if (isDefined(arg)) {
14059                 arg = +arg;
14060               } else {
14061                 arg = 0;
14062               }
14063               return context ? {value: arg} : arg;
14064             };
14065           },
14066           'unary-': function(argument, context) {
14067             return function(scope, locals, assign, inputs) {
14068               var arg = argument(scope, locals, assign, inputs);
14069               if (isDefined(arg)) {
14070                 arg = -arg;
14071               } else {
14072                 arg = 0;
14073               }
14074               return context ? {value: arg} : arg;
14075             };
14076           },
14077           'unary!': function(argument, context) {
14078             return function(scope, locals, assign, inputs) {
14079               var arg = !argument(scope, locals, assign, inputs);
14080               return context ? {value: arg} : arg;
14081             };
14082           },
14083           'binary+': function(left, right, context) {
14084             return function(scope, locals, assign, inputs) {
14085               var lhs = left(scope, locals, assign, inputs);
14086               var rhs = right(scope, locals, assign, inputs);
14087               var arg = plusFn(lhs, rhs);
14088               return context ? {value: arg} : arg;
14089             };
14090           },
14091           'binary-': function(left, right, context) {
14092             return function(scope, locals, assign, inputs) {
14093               var lhs = left(scope, locals, assign, inputs);
14094               var rhs = right(scope, locals, assign, inputs);
14095               var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14096               return context ? {value: arg} : arg;
14097             };
14098           },
14099           'binary*': function(left, right, context) {
14100             return function(scope, locals, assign, inputs) {
14101               var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14102               return context ? {value: arg} : arg;
14103             };
14104           },
14105           'binary/': function(left, right, context) {
14106             return function(scope, locals, assign, inputs) {
14107               var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14108               return context ? {value: arg} : arg;
14109             };
14110           },
14111           'binary%': function(left, right, context) {
14112             return function(scope, locals, assign, inputs) {
14113               var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14114               return context ? {value: arg} : arg;
14115             };
14116           },
14117           'binary===': function(left, right, context) {
14118             return function(scope, locals, assign, inputs) {
14119               var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14120               return context ? {value: arg} : arg;
14121             };
14122           },
14123           'binary!==': function(left, right, context) {
14124             return function(scope, locals, assign, inputs) {
14125               var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14126               return context ? {value: arg} : arg;
14127             };
14128           },
14129           'binary==': function(left, right, context) {
14130             return function(scope, locals, assign, inputs) {
14131               var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14132               return context ? {value: arg} : arg;
14133             };
14134           },
14135           'binary!=': function(left, right, context) {
14136             return function(scope, locals, assign, inputs) {
14137               var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14138               return context ? {value: arg} : arg;
14139             };
14140           },
14141           'binary<': function(left, right, context) {
14142             return function(scope, locals, assign, inputs) {
14143               var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14144               return context ? {value: arg} : arg;
14145             };
14146           },
14147           'binary>': function(left, right, context) {
14148             return function(scope, locals, assign, inputs) {
14149               var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14150               return context ? {value: arg} : arg;
14151             };
14152           },
14153           'binary<=': function(left, right, context) {
14154             return function(scope, locals, assign, inputs) {
14155               var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14156               return context ? {value: arg} : arg;
14157             };
14158           },
14159           'binary>=': function(left, right, context) {
14160             return function(scope, locals, assign, inputs) {
14161               var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14162               return context ? {value: arg} : arg;
14163             };
14164           },
14165           'binary&&': function(left, right, context) {
14166             return function(scope, locals, assign, inputs) {
14167               var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14168               return context ? {value: arg} : arg;
14169             };
14170           },
14171           'binary||': function(left, right, context) {
14172             return function(scope, locals, assign, inputs) {
14173               var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14174               return context ? {value: arg} : arg;
14175             };
14176           },
14177           'ternary?:': function(test, alternate, consequent, context) {
14178             return function(scope, locals, assign, inputs) {
14179               var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14180               return context ? {value: arg} : arg;
14181             };
14182           },
14183           value: function(value, context) {
14184             return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14185           },
14186           identifier: function(name, expensiveChecks, context, create, expression) {
14187             return function(scope, locals, assign, inputs) {
14188               var base = locals && (name in locals) ? locals : scope;
14189               if (create && create !== 1 && base && !(base[name])) {
14190                 base[name] = {};
14191               }
14192               var value = base ? base[name] : undefined;
14193               if (expensiveChecks) {
14194                 ensureSafeObject(value, expression);
14195               }
14196               if (context) {
14197                 return {context: base, name: name, value: value};
14198               } else {
14199                 return value;
14200               }
14201             };
14202           },
14203           computedMember: function(left, right, context, create, expression) {
14204             return function(scope, locals, assign, inputs) {
14205               var lhs = left(scope, locals, assign, inputs);
14206               var rhs;
14207               var value;
14208               if (lhs != null) {
14209                 rhs = right(scope, locals, assign, inputs);
14210                 rhs = getStringValue(rhs);
14211                 ensureSafeMemberName(rhs, expression);
14212                 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14213                   lhs[rhs] = {};
14214                 }
14215                 value = lhs[rhs];
14216                 ensureSafeObject(value, expression);
14217               }
14218               if (context) {
14219                 return {context: lhs, name: rhs, value: value};
14220               } else {
14221                 return value;
14222               }
14223             };
14224           },
14225           nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14226             return function(scope, locals, assign, inputs) {
14227               var lhs = left(scope, locals, assign, inputs);
14228               if (create && create !== 1 && lhs && !(lhs[right])) {
14229                 lhs[right] = {};
14230               }
14231               var value = lhs != null ? lhs[right] : undefined;
14232               if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14233                 ensureSafeObject(value, expression);
14234               }
14235               if (context) {
14236                 return {context: lhs, name: right, value: value};
14237               } else {
14238                 return value;
14239               }
14240             };
14241           },
14242           inputs: function(input, watchId) {
14243             return function(scope, value, locals, inputs) {
14244               if (inputs) return inputs[watchId];
14245               return input(scope, value, locals);
14246             };
14247           }
14248         };
14249
14250         /**
14251          * @constructor
14252          */
14253         var Parser = function(lexer, $filter, options) {
14254           this.lexer = lexer;
14255           this.$filter = $filter;
14256           this.options = options;
14257           this.ast = new AST(this.lexer);
14258           this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14259                                            new ASTCompiler(this.ast, $filter);
14260         };
14261
14262         Parser.prototype = {
14263           constructor: Parser,
14264
14265           parse: function(text) {
14266             return this.astCompiler.compile(text, this.options.expensiveChecks);
14267           }
14268         };
14269
14270         var getterFnCacheDefault = createMap();
14271         var getterFnCacheExpensive = createMap();
14272
14273         function isPossiblyDangerousMemberName(name) {
14274           return name == 'constructor';
14275         }
14276
14277         var objectValueOf = Object.prototype.valueOf;
14278
14279         function getValueOf(value) {
14280           return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14281         }
14282
14283         ///////////////////////////////////
14284
14285         /**
14286          * @ngdoc service
14287          * @name $parse
14288          * @kind function
14289          *
14290          * @description
14291          *
14292          * Converts Angular {@link guide/expression expression} into a function.
14293          *
14294          * ```js
14295          *   var getter = $parse('user.name');
14296          *   var setter = getter.assign;
14297          *   var context = {user:{name:'angular'}};
14298          *   var locals = {user:{name:'local'}};
14299          *
14300          *   expect(getter(context)).toEqual('angular');
14301          *   setter(context, 'newValue');
14302          *   expect(context.user.name).toEqual('newValue');
14303          *   expect(getter(context, locals)).toEqual('local');
14304          * ```
14305          *
14306          *
14307          * @param {string} expression String expression to compile.
14308          * @returns {function(context, locals)} a function which represents the compiled expression:
14309          *
14310          *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14311          *      are evaluated against (typically a scope object).
14312          *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14313          *      `context`.
14314          *
14315          *    The returned function also has the following properties:
14316          *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14317          *        literal.
14318          *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14319          *        constant literals.
14320          *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14321          *        set to a function to change its value on the given context.
14322          *
14323          */
14324
14325
14326         /**
14327          * @ngdoc provider
14328          * @name $parseProvider
14329          *
14330          * @description
14331          * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14332          *  service.
14333          */
14334         function $ParseProvider() {
14335           var cacheDefault = createMap();
14336           var cacheExpensive = createMap();
14337
14338           this.$get = ['$filter', function($filter) {
14339             var noUnsafeEval = csp().noUnsafeEval;
14340             var $parseOptions = {
14341                   csp: noUnsafeEval,
14342                   expensiveChecks: false
14343                 },
14344                 $parseOptionsExpensive = {
14345                   csp: noUnsafeEval,
14346                   expensiveChecks: true
14347                 };
14348
14349             return function $parse(exp, interceptorFn, expensiveChecks) {
14350               var parsedExpression, oneTime, cacheKey;
14351
14352               switch (typeof exp) {
14353                 case 'string':
14354                   exp = exp.trim();
14355                   cacheKey = exp;
14356
14357                   var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14358                   parsedExpression = cache[cacheKey];
14359
14360                   if (!parsedExpression) {
14361                     if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14362                       oneTime = true;
14363                       exp = exp.substring(2);
14364                     }
14365                     var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14366                     var lexer = new Lexer(parseOptions);
14367                     var parser = new Parser(lexer, $filter, parseOptions);
14368                     parsedExpression = parser.parse(exp);
14369                     if (parsedExpression.constant) {
14370                       parsedExpression.$$watchDelegate = constantWatchDelegate;
14371                     } else if (oneTime) {
14372                       parsedExpression.$$watchDelegate = parsedExpression.literal ?
14373                           oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14374                     } else if (parsedExpression.inputs) {
14375                       parsedExpression.$$watchDelegate = inputsWatchDelegate;
14376                     }
14377                     cache[cacheKey] = parsedExpression;
14378                   }
14379                   return addInterceptor(parsedExpression, interceptorFn);
14380
14381                 case 'function':
14382                   return addInterceptor(exp, interceptorFn);
14383
14384                 default:
14385                   return noop;
14386               }
14387             };
14388
14389             function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14390
14391               if (newValue == null || oldValueOfValue == null) { // null/undefined
14392                 return newValue === oldValueOfValue;
14393               }
14394
14395               if (typeof newValue === 'object') {
14396
14397                 // attempt to convert the value to a primitive type
14398                 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14399                 //             be cheaply dirty-checked
14400                 newValue = getValueOf(newValue);
14401
14402                 if (typeof newValue === 'object') {
14403                   // objects/arrays are not supported - deep-watching them would be too expensive
14404                   return false;
14405                 }
14406
14407                 // fall-through to the primitive equality check
14408               }
14409
14410               //Primitive or NaN
14411               return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14412             }
14413
14414             function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14415               var inputExpressions = parsedExpression.inputs;
14416               var lastResult;
14417
14418               if (inputExpressions.length === 1) {
14419                 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14420                 inputExpressions = inputExpressions[0];
14421                 return scope.$watch(function expressionInputWatch(scope) {
14422                   var newInputValue = inputExpressions(scope);
14423                   if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14424                     lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14425                     oldInputValueOf = newInputValue && getValueOf(newInputValue);
14426                   }
14427                   return lastResult;
14428                 }, listener, objectEquality, prettyPrintExpression);
14429               }
14430
14431               var oldInputValueOfValues = [];
14432               var oldInputValues = [];
14433               for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14434                 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14435                 oldInputValues[i] = null;
14436               }
14437
14438               return scope.$watch(function expressionInputsWatch(scope) {
14439                 var changed = false;
14440
14441                 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14442                   var newInputValue = inputExpressions[i](scope);
14443                   if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14444                     oldInputValues[i] = newInputValue;
14445                     oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14446                   }
14447                 }
14448
14449                 if (changed) {
14450                   lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14451                 }
14452
14453                 return lastResult;
14454               }, listener, objectEquality, prettyPrintExpression);
14455             }
14456
14457             function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14458               var unwatch, lastValue;
14459               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14460                 return parsedExpression(scope);
14461               }, function oneTimeListener(value, old, scope) {
14462                 lastValue = value;
14463                 if (isFunction(listener)) {
14464                   listener.apply(this, arguments);
14465                 }
14466                 if (isDefined(value)) {
14467                   scope.$$postDigest(function() {
14468                     if (isDefined(lastValue)) {
14469                       unwatch();
14470                     }
14471                   });
14472                 }
14473               }, objectEquality);
14474             }
14475
14476             function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14477               var unwatch, lastValue;
14478               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14479                 return parsedExpression(scope);
14480               }, function oneTimeListener(value, old, scope) {
14481                 lastValue = value;
14482                 if (isFunction(listener)) {
14483                   listener.call(this, value, old, scope);
14484                 }
14485                 if (isAllDefined(value)) {
14486                   scope.$$postDigest(function() {
14487                     if (isAllDefined(lastValue)) unwatch();
14488                   });
14489                 }
14490               }, objectEquality);
14491
14492               function isAllDefined(value) {
14493                 var allDefined = true;
14494                 forEach(value, function(val) {
14495                   if (!isDefined(val)) allDefined = false;
14496                 });
14497                 return allDefined;
14498               }
14499             }
14500
14501             function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14502               var unwatch;
14503               return unwatch = scope.$watch(function constantWatch(scope) {
14504                 return parsedExpression(scope);
14505               }, function constantListener(value, old, scope) {
14506                 if (isFunction(listener)) {
14507                   listener.apply(this, arguments);
14508                 }
14509                 unwatch();
14510               }, objectEquality);
14511             }
14512
14513             function addInterceptor(parsedExpression, interceptorFn) {
14514               if (!interceptorFn) return parsedExpression;
14515               var watchDelegate = parsedExpression.$$watchDelegate;
14516               var useInputs = false;
14517
14518               var regularWatch =
14519                   watchDelegate !== oneTimeLiteralWatchDelegate &&
14520                   watchDelegate !== oneTimeWatchDelegate;
14521
14522               var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14523                 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14524                 return interceptorFn(value, scope, locals);
14525               } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14526                 var value = parsedExpression(scope, locals, assign, inputs);
14527                 var result = interceptorFn(value, scope, locals);
14528                 // we only return the interceptor's result if the
14529                 // initial value is defined (for bind-once)
14530                 return isDefined(value) ? result : value;
14531               };
14532
14533               // Propagate $$watchDelegates other then inputsWatchDelegate
14534               if (parsedExpression.$$watchDelegate &&
14535                   parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14536                 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14537               } else if (!interceptorFn.$stateful) {
14538                 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14539                 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14540                 fn.$$watchDelegate = inputsWatchDelegate;
14541                 useInputs = !parsedExpression.inputs;
14542                 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14543               }
14544
14545               return fn;
14546             }
14547           }];
14548         }
14549
14550         /**
14551          * @ngdoc service
14552          * @name $q
14553          * @requires $rootScope
14554          *
14555          * @description
14556          * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14557          * when they are done processing.
14558          *
14559          * This is an implementation of promises/deferred objects inspired by
14560          * [Kris Kowal's Q](https://github.com/kriskowal/q).
14561          *
14562          * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14563          * implementations, and the other which resembles ES6 promises to some degree.
14564          *
14565          * # $q constructor
14566          *
14567          * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14568          * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14569          * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14570          *
14571          * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14572          * available yet.
14573          *
14574          * It can be used like so:
14575          *
14576          * ```js
14577          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14578          *   // are available in the current lexical scope (they could have been injected or passed in).
14579          *
14580          *   function asyncGreet(name) {
14581          *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14582          *     return $q(function(resolve, reject) {
14583          *       setTimeout(function() {
14584          *         if (okToGreet(name)) {
14585          *           resolve('Hello, ' + name + '!');
14586          *         } else {
14587          *           reject('Greeting ' + name + ' is not allowed.');
14588          *         }
14589          *       }, 1000);
14590          *     });
14591          *   }
14592          *
14593          *   var promise = asyncGreet('Robin Hood');
14594          *   promise.then(function(greeting) {
14595          *     alert('Success: ' + greeting);
14596          *   }, function(reason) {
14597          *     alert('Failed: ' + reason);
14598          *   });
14599          * ```
14600          *
14601          * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14602          *
14603          * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14604          *
14605          * However, the more traditional CommonJS-style usage is still available, and documented below.
14606          *
14607          * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14608          * interface for interacting with an object that represents the result of an action that is
14609          * performed asynchronously, and may or may not be finished at any given point in time.
14610          *
14611          * From the perspective of dealing with error handling, deferred and promise APIs are to
14612          * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14613          *
14614          * ```js
14615          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14616          *   // are available in the current lexical scope (they could have been injected or passed in).
14617          *
14618          *   function asyncGreet(name) {
14619          *     var deferred = $q.defer();
14620          *
14621          *     setTimeout(function() {
14622          *       deferred.notify('About to greet ' + name + '.');
14623          *
14624          *       if (okToGreet(name)) {
14625          *         deferred.resolve('Hello, ' + name + '!');
14626          *       } else {
14627          *         deferred.reject('Greeting ' + name + ' is not allowed.');
14628          *       }
14629          *     }, 1000);
14630          *
14631          *     return deferred.promise;
14632          *   }
14633          *
14634          *   var promise = asyncGreet('Robin Hood');
14635          *   promise.then(function(greeting) {
14636          *     alert('Success: ' + greeting);
14637          *   }, function(reason) {
14638          *     alert('Failed: ' + reason);
14639          *   }, function(update) {
14640          *     alert('Got notification: ' + update);
14641          *   });
14642          * ```
14643          *
14644          * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14645          * comes in the way of guarantees that promise and deferred APIs make, see
14646          * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14647          *
14648          * Additionally the promise api allows for composition that is very hard to do with the
14649          * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14650          * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14651          * section on serial or parallel joining of promises.
14652          *
14653          * # The Deferred API
14654          *
14655          * A new instance of deferred is constructed by calling `$q.defer()`.
14656          *
14657          * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14658          * that can be used for signaling the successful or unsuccessful completion, as well as the status
14659          * of the task.
14660          *
14661          * **Methods**
14662          *
14663          * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14664          *   constructed via `$q.reject`, the promise will be rejected instead.
14665          * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14666          *   resolving it with a rejection constructed via `$q.reject`.
14667          * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14668          *   multiple times before the promise is either resolved or rejected.
14669          *
14670          * **Properties**
14671          *
14672          * - promise – `{Promise}` – promise object associated with this deferred.
14673          *
14674          *
14675          * # The Promise API
14676          *
14677          * A new promise instance is created when a deferred instance is created and can be retrieved by
14678          * calling `deferred.promise`.
14679          *
14680          * The purpose of the promise object is to allow for interested parties to get access to the result
14681          * of the deferred task when it completes.
14682          *
14683          * **Methods**
14684          *
14685          * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14686          *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14687          *   as soon as the result is available. The callbacks are called with a single argument: the result
14688          *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14689          *   provide a progress indication, before the promise is resolved or rejected.
14690          *
14691          *   This method *returns a new promise* which is resolved or rejected via the return value of the
14692          *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14693          *   with the value which is resolved in that promise using
14694          *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14695          *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14696          *   resolved or rejected from the notifyCallback method.
14697          *
14698          * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14699          *
14700          * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14701          *   but to do so without modifying the final value. This is useful to release resources or do some
14702          *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14703          *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14704          *   more information.
14705          *
14706          * # Chaining promises
14707          *
14708          * Because calling the `then` method of a promise returns a new derived promise, it is easily
14709          * possible to create a chain of promises:
14710          *
14711          * ```js
14712          *   promiseB = promiseA.then(function(result) {
14713          *     return result + 1;
14714          *   });
14715          *
14716          *   // promiseB will be resolved immediately after promiseA is resolved and its value
14717          *   // will be the result of promiseA incremented by 1
14718          * ```
14719          *
14720          * It is possible to create chains of any length and since a promise can be resolved with another
14721          * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14722          * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14723          * $http's response interceptors.
14724          *
14725          *
14726          * # Differences between Kris Kowal's Q and $q
14727          *
14728          *  There are two main differences:
14729          *
14730          * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14731          *   mechanism in angular, which means faster propagation of resolution or rejection into your
14732          *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14733          * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14734          *   all the important functionality needed for common async tasks.
14735          *
14736          *  # Testing
14737          *
14738          *  ```js
14739          *    it('should simulate promise', inject(function($q, $rootScope) {
14740          *      var deferred = $q.defer();
14741          *      var promise = deferred.promise;
14742          *      var resolvedValue;
14743          *
14744          *      promise.then(function(value) { resolvedValue = value; });
14745          *      expect(resolvedValue).toBeUndefined();
14746          *
14747          *      // Simulate resolving of promise
14748          *      deferred.resolve(123);
14749          *      // Note that the 'then' function does not get called synchronously.
14750          *      // This is because we want the promise API to always be async, whether or not
14751          *      // it got called synchronously or asynchronously.
14752          *      expect(resolvedValue).toBeUndefined();
14753          *
14754          *      // Propagate promise resolution to 'then' functions using $apply().
14755          *      $rootScope.$apply();
14756          *      expect(resolvedValue).toEqual(123);
14757          *    }));
14758          *  ```
14759          *
14760          * @param {function(function, function)} resolver Function which is responsible for resolving or
14761          *   rejecting the newly created promise. The first parameter is a function which resolves the
14762          *   promise, the second parameter is a function which rejects the promise.
14763          *
14764          * @returns {Promise} The newly created promise.
14765          */
14766         function $QProvider() {
14767
14768           this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14769             return qFactory(function(callback) {
14770               $rootScope.$evalAsync(callback);
14771             }, $exceptionHandler);
14772           }];
14773         }
14774
14775         function $$QProvider() {
14776           this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14777             return qFactory(function(callback) {
14778               $browser.defer(callback);
14779             }, $exceptionHandler);
14780           }];
14781         }
14782
14783         /**
14784          * Constructs a promise manager.
14785          *
14786          * @param {function(function)} nextTick Function for executing functions in the next turn.
14787          * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14788          *     debugging purposes.
14789          * @returns {object} Promise manager.
14790          */
14791         function qFactory(nextTick, exceptionHandler) {
14792           var $qMinErr = minErr('$q', TypeError);
14793           function callOnce(self, resolveFn, rejectFn) {
14794             var called = false;
14795             function wrap(fn) {
14796               return function(value) {
14797                 if (called) return;
14798                 called = true;
14799                 fn.call(self, value);
14800               };
14801             }
14802
14803             return [wrap(resolveFn), wrap(rejectFn)];
14804           }
14805
14806           /**
14807            * @ngdoc method
14808            * @name ng.$q#defer
14809            * @kind function
14810            *
14811            * @description
14812            * Creates a `Deferred` object which represents a task which will finish in the future.
14813            *
14814            * @returns {Deferred} Returns a new instance of deferred.
14815            */
14816           var defer = function() {
14817             return new Deferred();
14818           };
14819
14820           function Promise() {
14821             this.$$state = { status: 0 };
14822           }
14823
14824           extend(Promise.prototype, {
14825             then: function(onFulfilled, onRejected, progressBack) {
14826               if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14827                 return this;
14828               }
14829               var result = new Deferred();
14830
14831               this.$$state.pending = this.$$state.pending || [];
14832               this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14833               if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14834
14835               return result.promise;
14836             },
14837
14838             "catch": function(callback) {
14839               return this.then(null, callback);
14840             },
14841
14842             "finally": function(callback, progressBack) {
14843               return this.then(function(value) {
14844                 return handleCallback(value, true, callback);
14845               }, function(error) {
14846                 return handleCallback(error, false, callback);
14847               }, progressBack);
14848             }
14849           });
14850
14851           //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14852           function simpleBind(context, fn) {
14853             return function(value) {
14854               fn.call(context, value);
14855             };
14856           }
14857
14858           function processQueue(state) {
14859             var fn, deferred, pending;
14860
14861             pending = state.pending;
14862             state.processScheduled = false;
14863             state.pending = undefined;
14864             for (var i = 0, ii = pending.length; i < ii; ++i) {
14865               deferred = pending[i][0];
14866               fn = pending[i][state.status];
14867               try {
14868                 if (isFunction(fn)) {
14869                   deferred.resolve(fn(state.value));
14870                 } else if (state.status === 1) {
14871                   deferred.resolve(state.value);
14872                 } else {
14873                   deferred.reject(state.value);
14874                 }
14875               } catch (e) {
14876                 deferred.reject(e);
14877                 exceptionHandler(e);
14878               }
14879             }
14880           }
14881
14882           function scheduleProcessQueue(state) {
14883             if (state.processScheduled || !state.pending) return;
14884             state.processScheduled = true;
14885             nextTick(function() { processQueue(state); });
14886           }
14887
14888           function Deferred() {
14889             this.promise = new Promise();
14890             //Necessary to support unbound execution :/
14891             this.resolve = simpleBind(this, this.resolve);
14892             this.reject = simpleBind(this, this.reject);
14893             this.notify = simpleBind(this, this.notify);
14894           }
14895
14896           extend(Deferred.prototype, {
14897             resolve: function(val) {
14898               if (this.promise.$$state.status) return;
14899               if (val === this.promise) {
14900                 this.$$reject($qMinErr(
14901                   'qcycle',
14902                   "Expected promise to be resolved with value other than itself '{0}'",
14903                   val));
14904               } else {
14905                 this.$$resolve(val);
14906               }
14907
14908             },
14909
14910             $$resolve: function(val) {
14911               var then, fns;
14912
14913               fns = callOnce(this, this.$$resolve, this.$$reject);
14914               try {
14915                 if ((isObject(val) || isFunction(val))) then = val && val.then;
14916                 if (isFunction(then)) {
14917                   this.promise.$$state.status = -1;
14918                   then.call(val, fns[0], fns[1], this.notify);
14919                 } else {
14920                   this.promise.$$state.value = val;
14921                   this.promise.$$state.status = 1;
14922                   scheduleProcessQueue(this.promise.$$state);
14923                 }
14924               } catch (e) {
14925                 fns[1](e);
14926                 exceptionHandler(e);
14927               }
14928             },
14929
14930             reject: function(reason) {
14931               if (this.promise.$$state.status) return;
14932               this.$$reject(reason);
14933             },
14934
14935             $$reject: function(reason) {
14936               this.promise.$$state.value = reason;
14937               this.promise.$$state.status = 2;
14938               scheduleProcessQueue(this.promise.$$state);
14939             },
14940
14941             notify: function(progress) {
14942               var callbacks = this.promise.$$state.pending;
14943
14944               if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14945                 nextTick(function() {
14946                   var callback, result;
14947                   for (var i = 0, ii = callbacks.length; i < ii; i++) {
14948                     result = callbacks[i][0];
14949                     callback = callbacks[i][3];
14950                     try {
14951                       result.notify(isFunction(callback) ? callback(progress) : progress);
14952                     } catch (e) {
14953                       exceptionHandler(e);
14954                     }
14955                   }
14956                 });
14957               }
14958             }
14959           });
14960
14961           /**
14962            * @ngdoc method
14963            * @name $q#reject
14964            * @kind function
14965            *
14966            * @description
14967            * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14968            * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14969            * a promise chain, you don't need to worry about it.
14970            *
14971            * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14972            * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14973            * a promise error callback and you want to forward the error to the promise derived from the
14974            * current promise, you have to "rethrow" the error by returning a rejection constructed via
14975            * `reject`.
14976            *
14977            * ```js
14978            *   promiseB = promiseA.then(function(result) {
14979            *     // success: do something and resolve promiseB
14980            *     //          with the old or a new result
14981            *     return result;
14982            *   }, function(reason) {
14983            *     // error: handle the error if possible and
14984            *     //        resolve promiseB with newPromiseOrValue,
14985            *     //        otherwise forward the rejection to promiseB
14986            *     if (canHandle(reason)) {
14987            *      // handle the error and recover
14988            *      return newPromiseOrValue;
14989            *     }
14990            *     return $q.reject(reason);
14991            *   });
14992            * ```
14993            *
14994            * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14995            * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14996            */
14997           var reject = function(reason) {
14998             var result = new Deferred();
14999             result.reject(reason);
15000             return result.promise;
15001           };
15002
15003           var makePromise = function makePromise(value, resolved) {
15004             var result = new Deferred();
15005             if (resolved) {
15006               result.resolve(value);
15007             } else {
15008               result.reject(value);
15009             }
15010             return result.promise;
15011           };
15012
15013           var handleCallback = function handleCallback(value, isResolved, callback) {
15014             var callbackOutput = null;
15015             try {
15016               if (isFunction(callback)) callbackOutput = callback();
15017             } catch (e) {
15018               return makePromise(e, false);
15019             }
15020             if (isPromiseLike(callbackOutput)) {
15021               return callbackOutput.then(function() {
15022                 return makePromise(value, isResolved);
15023               }, function(error) {
15024                 return makePromise(error, false);
15025               });
15026             } else {
15027               return makePromise(value, isResolved);
15028             }
15029           };
15030
15031           /**
15032            * @ngdoc method
15033            * @name $q#when
15034            * @kind function
15035            *
15036            * @description
15037            * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15038            * This is useful when you are dealing with an object that might or might not be a promise, or if
15039            * the promise comes from a source that can't be trusted.
15040            *
15041            * @param {*} value Value or a promise
15042            * @param {Function=} successCallback
15043            * @param {Function=} errorCallback
15044            * @param {Function=} progressCallback
15045            * @returns {Promise} Returns a promise of the passed value or promise
15046            */
15047
15048
15049           var when = function(value, callback, errback, progressBack) {
15050             var result = new Deferred();
15051             result.resolve(value);
15052             return result.promise.then(callback, errback, progressBack);
15053           };
15054
15055           /**
15056            * @ngdoc method
15057            * @name $q#resolve
15058            * @kind function
15059            *
15060            * @description
15061            * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15062            *
15063            * @param {*} value Value or a promise
15064            * @param {Function=} successCallback
15065            * @param {Function=} errorCallback
15066            * @param {Function=} progressCallback
15067            * @returns {Promise} Returns a promise of the passed value or promise
15068            */
15069           var resolve = when;
15070
15071           /**
15072            * @ngdoc method
15073            * @name $q#all
15074            * @kind function
15075            *
15076            * @description
15077            * Combines multiple promises into a single promise that is resolved when all of the input
15078            * promises are resolved.
15079            *
15080            * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15081            * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15082            *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15083            *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15084            *   with the same rejection value.
15085            */
15086
15087           function all(promises) {
15088             var deferred = new Deferred(),
15089                 counter = 0,
15090                 results = isArray(promises) ? [] : {};
15091
15092             forEach(promises, function(promise, key) {
15093               counter++;
15094               when(promise).then(function(value) {
15095                 if (results.hasOwnProperty(key)) return;
15096                 results[key] = value;
15097                 if (!(--counter)) deferred.resolve(results);
15098               }, function(reason) {
15099                 if (results.hasOwnProperty(key)) return;
15100                 deferred.reject(reason);
15101               });
15102             });
15103
15104             if (counter === 0) {
15105               deferred.resolve(results);
15106             }
15107
15108             return deferred.promise;
15109           }
15110
15111           var $Q = function Q(resolver) {
15112             if (!isFunction(resolver)) {
15113               throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15114             }
15115
15116             if (!(this instanceof Q)) {
15117               // More useful when $Q is the Promise itself.
15118               return new Q(resolver);
15119             }
15120
15121             var deferred = new Deferred();
15122
15123             function resolveFn(value) {
15124               deferred.resolve(value);
15125             }
15126
15127             function rejectFn(reason) {
15128               deferred.reject(reason);
15129             }
15130
15131             resolver(resolveFn, rejectFn);
15132
15133             return deferred.promise;
15134           };
15135
15136           $Q.defer = defer;
15137           $Q.reject = reject;
15138           $Q.when = when;
15139           $Q.resolve = resolve;
15140           $Q.all = all;
15141
15142           return $Q;
15143         }
15144
15145         function $$RAFProvider() { //rAF
15146           this.$get = ['$window', '$timeout', function($window, $timeout) {
15147             var requestAnimationFrame = $window.requestAnimationFrame ||
15148                                         $window.webkitRequestAnimationFrame;
15149
15150             var cancelAnimationFrame = $window.cancelAnimationFrame ||
15151                                        $window.webkitCancelAnimationFrame ||
15152                                        $window.webkitCancelRequestAnimationFrame;
15153
15154             var rafSupported = !!requestAnimationFrame;
15155             var raf = rafSupported
15156               ? function(fn) {
15157                   var id = requestAnimationFrame(fn);
15158                   return function() {
15159                     cancelAnimationFrame(id);
15160                   };
15161                 }
15162               : function(fn) {
15163                   var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15164                   return function() {
15165                     $timeout.cancel(timer);
15166                   };
15167                 };
15168
15169             raf.supported = rafSupported;
15170
15171             return raf;
15172           }];
15173         }
15174
15175         /**
15176          * DESIGN NOTES
15177          *
15178          * The design decisions behind the scope are heavily favored for speed and memory consumption.
15179          *
15180          * The typical use of scope is to watch the expressions, which most of the time return the same
15181          * value as last time so we optimize the operation.
15182          *
15183          * Closures construction is expensive in terms of speed as well as memory:
15184          *   - No closures, instead use prototypical inheritance for API
15185          *   - Internal state needs to be stored on scope directly, which means that private state is
15186          *     exposed as $$____ properties
15187          *
15188          * Loop operations are optimized by using while(count--) { ... }
15189          *   - This means that in order to keep the same order of execution as addition we have to add
15190          *     items to the array at the beginning (unshift) instead of at the end (push)
15191          *
15192          * Child scopes are created and removed often
15193          *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15194          *
15195          * There are fewer watches than observers. This is why you don't want the observer to be implemented
15196          * in the same way as watch. Watch requires return of the initialization function which is expensive
15197          * to construct.
15198          */
15199
15200
15201         /**
15202          * @ngdoc provider
15203          * @name $rootScopeProvider
15204          * @description
15205          *
15206          * Provider for the $rootScope service.
15207          */
15208
15209         /**
15210          * @ngdoc method
15211          * @name $rootScopeProvider#digestTtl
15212          * @description
15213          *
15214          * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15215          * assuming that the model is unstable.
15216          *
15217          * The current default is 10 iterations.
15218          *
15219          * In complex applications it's possible that the dependencies between `$watch`s will result in
15220          * several digest iterations. However if an application needs more than the default 10 digest
15221          * iterations for its model to stabilize then you should investigate what is causing the model to
15222          * continuously change during the digest.
15223          *
15224          * Increasing the TTL could have performance implications, so you should not change it without
15225          * proper justification.
15226          *
15227          * @param {number} limit The number of digest iterations.
15228          */
15229
15230
15231         /**
15232          * @ngdoc service
15233          * @name $rootScope
15234          * @description
15235          *
15236          * Every application has a single root {@link ng.$rootScope.Scope scope}.
15237          * All other scopes are descendant scopes of the root scope. Scopes provide separation
15238          * between the model and the view, via a mechanism for watching the model for changes.
15239          * They also provide event emission/broadcast and subscription facility. See the
15240          * {@link guide/scope developer guide on scopes}.
15241          */
15242         function $RootScopeProvider() {
15243           var TTL = 10;
15244           var $rootScopeMinErr = minErr('$rootScope');
15245           var lastDirtyWatch = null;
15246           var applyAsyncId = null;
15247
15248           this.digestTtl = function(value) {
15249             if (arguments.length) {
15250               TTL = value;
15251             }
15252             return TTL;
15253           };
15254
15255           function createChildScopeClass(parent) {
15256             function ChildScope() {
15257               this.$$watchers = this.$$nextSibling =
15258                   this.$$childHead = this.$$childTail = null;
15259               this.$$listeners = {};
15260               this.$$listenerCount = {};
15261               this.$$watchersCount = 0;
15262               this.$id = nextUid();
15263               this.$$ChildScope = null;
15264             }
15265             ChildScope.prototype = parent;
15266             return ChildScope;
15267           }
15268
15269           this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15270               function($injector, $exceptionHandler, $parse, $browser) {
15271
15272             function destroyChildScope($event) {
15273                 $event.currentScope.$$destroyed = true;
15274             }
15275
15276             function cleanUpScope($scope) {
15277
15278               if (msie === 9) {
15279                 // There is a memory leak in IE9 if all child scopes are not disconnected
15280                 // completely when a scope is destroyed. So this code will recurse up through
15281                 // all this scopes children
15282                 //
15283                 // See issue https://github.com/angular/angular.js/issues/10706
15284                 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15285                 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15286               }
15287
15288               // The code below works around IE9 and V8's memory leaks
15289               //
15290               // See:
15291               // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15292               // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15293               // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15294
15295               $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15296                   $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15297             }
15298
15299             /**
15300              * @ngdoc type
15301              * @name $rootScope.Scope
15302              *
15303              * @description
15304              * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15305              * {@link auto.$injector $injector}. Child scopes are created using the
15306              * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15307              * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15308              * an in-depth introduction and usage examples.
15309              *
15310              *
15311              * # Inheritance
15312              * A scope can inherit from a parent scope, as in this example:
15313              * ```js
15314                  var parent = $rootScope;
15315                  var child = parent.$new();
15316
15317                  parent.salutation = "Hello";
15318                  expect(child.salutation).toEqual('Hello');
15319
15320                  child.salutation = "Welcome";
15321                  expect(child.salutation).toEqual('Welcome');
15322                  expect(parent.salutation).toEqual('Hello');
15323              * ```
15324              *
15325              * When interacting with `Scope` in tests, additional helper methods are available on the
15326              * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15327              * details.
15328              *
15329              *
15330              * @param {Object.<string, function()>=} providers Map of service factory which need to be
15331              *                                       provided for the current scope. Defaults to {@link ng}.
15332              * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15333              *                              append/override services provided by `providers`. This is handy
15334              *                              when unit-testing and having the need to override a default
15335              *                              service.
15336              * @returns {Object} Newly created scope.
15337              *
15338              */
15339             function Scope() {
15340               this.$id = nextUid();
15341               this.$$phase = this.$parent = this.$$watchers =
15342                              this.$$nextSibling = this.$$prevSibling =
15343                              this.$$childHead = this.$$childTail = null;
15344               this.$root = this;
15345               this.$$destroyed = false;
15346               this.$$listeners = {};
15347               this.$$listenerCount = {};
15348               this.$$watchersCount = 0;
15349               this.$$isolateBindings = null;
15350             }
15351
15352             /**
15353              * @ngdoc property
15354              * @name $rootScope.Scope#$id
15355              *
15356              * @description
15357              * Unique scope ID (monotonically increasing) useful for debugging.
15358              */
15359
15360              /**
15361               * @ngdoc property
15362               * @name $rootScope.Scope#$parent
15363               *
15364               * @description
15365               * Reference to the parent scope.
15366               */
15367
15368               /**
15369                * @ngdoc property
15370                * @name $rootScope.Scope#$root
15371                *
15372                * @description
15373                * Reference to the root scope.
15374                */
15375
15376             Scope.prototype = {
15377               constructor: Scope,
15378               /**
15379                * @ngdoc method
15380                * @name $rootScope.Scope#$new
15381                * @kind function
15382                *
15383                * @description
15384                * Creates a new child {@link ng.$rootScope.Scope scope}.
15385                *
15386                * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15387                * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15388                *
15389                * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15390                * desired for the scope and its child scopes to be permanently detached from the parent and
15391                * thus stop participating in model change detection and listener notification by invoking.
15392                *
15393                * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15394                *         parent scope. The scope is isolated, as it can not see parent scope properties.
15395                *         When creating widgets, it is useful for the widget to not accidentally read parent
15396                *         state.
15397                *
15398                * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15399                *                              of the newly created scope. Defaults to `this` scope if not provided.
15400                *                              This is used when creating a transclude scope to correctly place it
15401                *                              in the scope hierarchy while maintaining the correct prototypical
15402                *                              inheritance.
15403                *
15404                * @returns {Object} The newly created child scope.
15405                *
15406                */
15407               $new: function(isolate, parent) {
15408                 var child;
15409
15410                 parent = parent || this;
15411
15412                 if (isolate) {
15413                   child = new Scope();
15414                   child.$root = this.$root;
15415                 } else {
15416                   // Only create a child scope class if somebody asks for one,
15417                   // but cache it to allow the VM to optimize lookups.
15418                   if (!this.$$ChildScope) {
15419                     this.$$ChildScope = createChildScopeClass(this);
15420                   }
15421                   child = new this.$$ChildScope();
15422                 }
15423                 child.$parent = parent;
15424                 child.$$prevSibling = parent.$$childTail;
15425                 if (parent.$$childHead) {
15426                   parent.$$childTail.$$nextSibling = child;
15427                   parent.$$childTail = child;
15428                 } else {
15429                   parent.$$childHead = parent.$$childTail = child;
15430                 }
15431
15432                 // When the new scope is not isolated or we inherit from `this`, and
15433                 // the parent scope is destroyed, the property `$$destroyed` is inherited
15434                 // prototypically. In all other cases, this property needs to be set
15435                 // when the parent scope is destroyed.
15436                 // The listener needs to be added after the parent is set
15437                 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15438
15439                 return child;
15440               },
15441
15442               /**
15443                * @ngdoc method
15444                * @name $rootScope.Scope#$watch
15445                * @kind function
15446                *
15447                * @description
15448                * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15449                *
15450                * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15451                *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15452                *   its value when executed multiple times with the same input because it may be executed multiple
15453                *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15454                *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15455                * - The `listener` is called only when the value from the current `watchExpression` and the
15456                *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15457                *   see below). Inequality is determined according to reference inequality,
15458                *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15459                *    via the `!==` Javascript operator, unless `objectEquality == true`
15460                *   (see next point)
15461                * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15462                *   according to the {@link angular.equals} function. To save the value of the object for
15463                *   later comparison, the {@link angular.copy} function is used. This therefore means that
15464                *   watching complex objects will have adverse memory and performance implications.
15465                * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15466                *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15467                *   iteration limit is 10 to prevent an infinite loop deadlock.
15468                *
15469                *
15470                * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15471                * you can register a `watchExpression` function with no `listener`. (Be prepared for
15472                * multiple calls to your `watchExpression` because it will execute multiple times in a
15473                * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15474                *
15475                * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15476                * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15477                * watcher. In rare cases, this is undesirable because the listener is called when the result
15478                * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15479                * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15480                * listener was called due to initialization.
15481                *
15482                *
15483                *
15484                * # Example
15485                * ```js
15486                    // let's assume that scope was dependency injected as the $rootScope
15487                    var scope = $rootScope;
15488                    scope.name = 'misko';
15489                    scope.counter = 0;
15490
15491                    expect(scope.counter).toEqual(0);
15492                    scope.$watch('name', function(newValue, oldValue) {
15493                      scope.counter = scope.counter + 1;
15494                    });
15495                    expect(scope.counter).toEqual(0);
15496
15497                    scope.$digest();
15498                    // the listener is always called during the first $digest loop after it was registered
15499                    expect(scope.counter).toEqual(1);
15500
15501                    scope.$digest();
15502                    // but now it will not be called unless the value changes
15503                    expect(scope.counter).toEqual(1);
15504
15505                    scope.name = 'adam';
15506                    scope.$digest();
15507                    expect(scope.counter).toEqual(2);
15508
15509
15510
15511                    // Using a function as a watchExpression
15512                    var food;
15513                    scope.foodCounter = 0;
15514                    expect(scope.foodCounter).toEqual(0);
15515                    scope.$watch(
15516                      // This function returns the value being watched. It is called for each turn of the $digest loop
15517                      function() { return food; },
15518                      // This is the change listener, called when the value returned from the above function changes
15519                      function(newValue, oldValue) {
15520                        if ( newValue !== oldValue ) {
15521                          // Only increment the counter if the value changed
15522                          scope.foodCounter = scope.foodCounter + 1;
15523                        }
15524                      }
15525                    );
15526                    // No digest has been run so the counter will be zero
15527                    expect(scope.foodCounter).toEqual(0);
15528
15529                    // Run the digest but since food has not changed count will still be zero
15530                    scope.$digest();
15531                    expect(scope.foodCounter).toEqual(0);
15532
15533                    // Update food and run digest.  Now the counter will increment
15534                    food = 'cheeseburger';
15535                    scope.$digest();
15536                    expect(scope.foodCounter).toEqual(1);
15537
15538                * ```
15539                *
15540                *
15541                *
15542                * @param {(function()|string)} watchExpression Expression that is evaluated on each
15543                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15544                *    a call to the `listener`.
15545                *
15546                *    - `string`: Evaluated as {@link guide/expression expression}
15547                *    - `function(scope)`: called with current `scope` as a parameter.
15548                * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15549                *    of `watchExpression` changes.
15550                *
15551                *    - `newVal` contains the current value of the `watchExpression`
15552                *    - `oldVal` contains the previous value of the `watchExpression`
15553                *    - `scope` refers to the current scope
15554                * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15555                *     comparing for reference equality.
15556                * @returns {function()} Returns a deregistration function for this listener.
15557                */
15558               $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15559                 var get = $parse(watchExp);
15560
15561                 if (get.$$watchDelegate) {
15562                   return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15563                 }
15564                 var scope = this,
15565                     array = scope.$$watchers,
15566                     watcher = {
15567                       fn: listener,
15568                       last: initWatchVal,
15569                       get: get,
15570                       exp: prettyPrintExpression || watchExp,
15571                       eq: !!objectEquality
15572                     };
15573
15574                 lastDirtyWatch = null;
15575
15576                 if (!isFunction(listener)) {
15577                   watcher.fn = noop;
15578                 }
15579
15580                 if (!array) {
15581                   array = scope.$$watchers = [];
15582                 }
15583                 // we use unshift since we use a while loop in $digest for speed.
15584                 // the while loop reads in reverse order.
15585                 array.unshift(watcher);
15586                 incrementWatchersCount(this, 1);
15587
15588                 return function deregisterWatch() {
15589                   if (arrayRemove(array, watcher) >= 0) {
15590                     incrementWatchersCount(scope, -1);
15591                   }
15592                   lastDirtyWatch = null;
15593                 };
15594               },
15595
15596               /**
15597                * @ngdoc method
15598                * @name $rootScope.Scope#$watchGroup
15599                * @kind function
15600                *
15601                * @description
15602                * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15603                * If any one expression in the collection changes the `listener` is executed.
15604                *
15605                * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15606                *   call to $digest() to see if any items changes.
15607                * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15608                *
15609                * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15610                * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15611                *
15612                * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15613                *    expression in `watchExpressions` changes
15614                *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15615                *    those of `watchExpression`
15616                *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15617                *    those of `watchExpression`
15618                *    The `scope` refers to the current scope.
15619                * @returns {function()} Returns a de-registration function for all listeners.
15620                */
15621               $watchGroup: function(watchExpressions, listener) {
15622                 var oldValues = new Array(watchExpressions.length);
15623                 var newValues = new Array(watchExpressions.length);
15624                 var deregisterFns = [];
15625                 var self = this;
15626                 var changeReactionScheduled = false;
15627                 var firstRun = true;
15628
15629                 if (!watchExpressions.length) {
15630                   // No expressions means we call the listener ASAP
15631                   var shouldCall = true;
15632                   self.$evalAsync(function() {
15633                     if (shouldCall) listener(newValues, newValues, self);
15634                   });
15635                   return function deregisterWatchGroup() {
15636                     shouldCall = false;
15637                   };
15638                 }
15639
15640                 if (watchExpressions.length === 1) {
15641                   // Special case size of one
15642                   return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15643                     newValues[0] = value;
15644                     oldValues[0] = oldValue;
15645                     listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15646                   });
15647                 }
15648
15649                 forEach(watchExpressions, function(expr, i) {
15650                   var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15651                     newValues[i] = value;
15652                     oldValues[i] = oldValue;
15653                     if (!changeReactionScheduled) {
15654                       changeReactionScheduled = true;
15655                       self.$evalAsync(watchGroupAction);
15656                     }
15657                   });
15658                   deregisterFns.push(unwatchFn);
15659                 });
15660
15661                 function watchGroupAction() {
15662                   changeReactionScheduled = false;
15663
15664                   if (firstRun) {
15665                     firstRun = false;
15666                     listener(newValues, newValues, self);
15667                   } else {
15668                     listener(newValues, oldValues, self);
15669                   }
15670                 }
15671
15672                 return function deregisterWatchGroup() {
15673                   while (deregisterFns.length) {
15674                     deregisterFns.shift()();
15675                   }
15676                 };
15677               },
15678
15679
15680               /**
15681                * @ngdoc method
15682                * @name $rootScope.Scope#$watchCollection
15683                * @kind function
15684                *
15685                * @description
15686                * Shallow watches the properties of an object and fires whenever any of the properties change
15687                * (for arrays, this implies watching the array items; for object maps, this implies watching
15688                * the properties). If a change is detected, the `listener` callback is fired.
15689                *
15690                * - The `obj` collection is observed via standard $watch operation and is examined on every
15691                *   call to $digest() to see if any items have been added, removed, or moved.
15692                * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15693                *   adding, removing, and moving items belonging to an object or array.
15694                *
15695                *
15696                * # Example
15697                * ```js
15698                   $scope.names = ['igor', 'matias', 'misko', 'james'];
15699                   $scope.dataCount = 4;
15700
15701                   $scope.$watchCollection('names', function(newNames, oldNames) {
15702                     $scope.dataCount = newNames.length;
15703                   });
15704
15705                   expect($scope.dataCount).toEqual(4);
15706                   $scope.$digest();
15707
15708                   //still at 4 ... no changes
15709                   expect($scope.dataCount).toEqual(4);
15710
15711                   $scope.names.pop();
15712                   $scope.$digest();
15713
15714                   //now there's been a change
15715                   expect($scope.dataCount).toEqual(3);
15716                * ```
15717                *
15718                *
15719                * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15720                *    expression value should evaluate to an object or an array which is observed on each
15721                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15722                *    collection will trigger a call to the `listener`.
15723                *
15724                * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15725                *    when a change is detected.
15726                *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15727                *    - The `oldCollection` object is a copy of the former collection data.
15728                *      Due to performance considerations, the`oldCollection` value is computed only if the
15729                *      `listener` function declares two or more arguments.
15730                *    - The `scope` argument refers to the current scope.
15731                *
15732                * @returns {function()} Returns a de-registration function for this listener. When the
15733                *    de-registration function is executed, the internal watch operation is terminated.
15734                */
15735               $watchCollection: function(obj, listener) {
15736                 $watchCollectionInterceptor.$stateful = true;
15737
15738                 var self = this;
15739                 // the current value, updated on each dirty-check run
15740                 var newValue;
15741                 // a shallow copy of the newValue from the last dirty-check run,
15742                 // updated to match newValue during dirty-check run
15743                 var oldValue;
15744                 // a shallow copy of the newValue from when the last change happened
15745                 var veryOldValue;
15746                 // only track veryOldValue if the listener is asking for it
15747                 var trackVeryOldValue = (listener.length > 1);
15748                 var changeDetected = 0;
15749                 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15750                 var internalArray = [];
15751                 var internalObject = {};
15752                 var initRun = true;
15753                 var oldLength = 0;
15754
15755                 function $watchCollectionInterceptor(_value) {
15756                   newValue = _value;
15757                   var newLength, key, bothNaN, newItem, oldItem;
15758
15759                   // If the new value is undefined, then return undefined as the watch may be a one-time watch
15760                   if (isUndefined(newValue)) return;
15761
15762                   if (!isObject(newValue)) { // if primitive
15763                     if (oldValue !== newValue) {
15764                       oldValue = newValue;
15765                       changeDetected++;
15766                     }
15767                   } else if (isArrayLike(newValue)) {
15768                     if (oldValue !== internalArray) {
15769                       // we are transitioning from something which was not an array into array.
15770                       oldValue = internalArray;
15771                       oldLength = oldValue.length = 0;
15772                       changeDetected++;
15773                     }
15774
15775                     newLength = newValue.length;
15776
15777                     if (oldLength !== newLength) {
15778                       // if lengths do not match we need to trigger change notification
15779                       changeDetected++;
15780                       oldValue.length = oldLength = newLength;
15781                     }
15782                     // copy the items to oldValue and look for changes.
15783                     for (var i = 0; i < newLength; i++) {
15784                       oldItem = oldValue[i];
15785                       newItem = newValue[i];
15786
15787                       bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15788                       if (!bothNaN && (oldItem !== newItem)) {
15789                         changeDetected++;
15790                         oldValue[i] = newItem;
15791                       }
15792                     }
15793                   } else {
15794                     if (oldValue !== internalObject) {
15795                       // we are transitioning from something which was not an object into object.
15796                       oldValue = internalObject = {};
15797                       oldLength = 0;
15798                       changeDetected++;
15799                     }
15800                     // copy the items to oldValue and look for changes.
15801                     newLength = 0;
15802                     for (key in newValue) {
15803                       if (hasOwnProperty.call(newValue, key)) {
15804                         newLength++;
15805                         newItem = newValue[key];
15806                         oldItem = oldValue[key];
15807
15808                         if (key in oldValue) {
15809                           bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15810                           if (!bothNaN && (oldItem !== newItem)) {
15811                             changeDetected++;
15812                             oldValue[key] = newItem;
15813                           }
15814                         } else {
15815                           oldLength++;
15816                           oldValue[key] = newItem;
15817                           changeDetected++;
15818                         }
15819                       }
15820                     }
15821                     if (oldLength > newLength) {
15822                       // we used to have more keys, need to find them and destroy them.
15823                       changeDetected++;
15824                       for (key in oldValue) {
15825                         if (!hasOwnProperty.call(newValue, key)) {
15826                           oldLength--;
15827                           delete oldValue[key];
15828                         }
15829                       }
15830                     }
15831                   }
15832                   return changeDetected;
15833                 }
15834
15835                 function $watchCollectionAction() {
15836                   if (initRun) {
15837                     initRun = false;
15838                     listener(newValue, newValue, self);
15839                   } else {
15840                     listener(newValue, veryOldValue, self);
15841                   }
15842
15843                   // make a copy for the next time a collection is changed
15844                   if (trackVeryOldValue) {
15845                     if (!isObject(newValue)) {
15846                       //primitive
15847                       veryOldValue = newValue;
15848                     } else if (isArrayLike(newValue)) {
15849                       veryOldValue = new Array(newValue.length);
15850                       for (var i = 0; i < newValue.length; i++) {
15851                         veryOldValue[i] = newValue[i];
15852                       }
15853                     } else { // if object
15854                       veryOldValue = {};
15855                       for (var key in newValue) {
15856                         if (hasOwnProperty.call(newValue, key)) {
15857                           veryOldValue[key] = newValue[key];
15858                         }
15859                       }
15860                     }
15861                   }
15862                 }
15863
15864                 return this.$watch(changeDetector, $watchCollectionAction);
15865               },
15866
15867               /**
15868                * @ngdoc method
15869                * @name $rootScope.Scope#$digest
15870                * @kind function
15871                *
15872                * @description
15873                * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15874                * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15875                * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15876                * until no more listeners are firing. This means that it is possible to get into an infinite
15877                * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15878                * iterations exceeds 10.
15879                *
15880                * Usually, you don't call `$digest()` directly in
15881                * {@link ng.directive:ngController controllers} or in
15882                * {@link ng.$compileProvider#directive directives}.
15883                * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15884                * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15885                *
15886                * If you want to be notified whenever `$digest()` is called,
15887                * you can register a `watchExpression` function with
15888                * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15889                *
15890                * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15891                *
15892                * # Example
15893                * ```js
15894                    var scope = ...;
15895                    scope.name = 'misko';
15896                    scope.counter = 0;
15897
15898                    expect(scope.counter).toEqual(0);
15899                    scope.$watch('name', function(newValue, oldValue) {
15900                      scope.counter = scope.counter + 1;
15901                    });
15902                    expect(scope.counter).toEqual(0);
15903
15904                    scope.$digest();
15905                    // the listener is always called during the first $digest loop after it was registered
15906                    expect(scope.counter).toEqual(1);
15907
15908                    scope.$digest();
15909                    // but now it will not be called unless the value changes
15910                    expect(scope.counter).toEqual(1);
15911
15912                    scope.name = 'adam';
15913                    scope.$digest();
15914                    expect(scope.counter).toEqual(2);
15915                * ```
15916                *
15917                */
15918               $digest: function() {
15919                 var watch, value, last,
15920                     watchers,
15921                     length,
15922                     dirty, ttl = TTL,
15923                     next, current, target = this,
15924                     watchLog = [],
15925                     logIdx, logMsg, asyncTask;
15926
15927                 beginPhase('$digest');
15928                 // Check for changes to browser url that happened in sync before the call to $digest
15929                 $browser.$$checkUrlChange();
15930
15931                 if (this === $rootScope && applyAsyncId !== null) {
15932                   // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15933                   // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15934                   $browser.defer.cancel(applyAsyncId);
15935                   flushApplyAsync();
15936                 }
15937
15938                 lastDirtyWatch = null;
15939
15940                 do { // "while dirty" loop
15941                   dirty = false;
15942                   current = target;
15943
15944                   while (asyncQueue.length) {
15945                     try {
15946                       asyncTask = asyncQueue.shift();
15947                       asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15948                     } catch (e) {
15949                       $exceptionHandler(e);
15950                     }
15951                     lastDirtyWatch = null;
15952                   }
15953
15954                   traverseScopesLoop:
15955                   do { // "traverse the scopes" loop
15956                     if ((watchers = current.$$watchers)) {
15957                       // process our watches
15958                       length = watchers.length;
15959                       while (length--) {
15960                         try {
15961                           watch = watchers[length];
15962                           // Most common watches are on primitives, in which case we can short
15963                           // circuit it with === operator, only when === fails do we use .equals
15964                           if (watch) {
15965                             if ((value = watch.get(current)) !== (last = watch.last) &&
15966                                 !(watch.eq
15967                                     ? equals(value, last)
15968                                     : (typeof value === 'number' && typeof last === 'number'
15969                                        && isNaN(value) && isNaN(last)))) {
15970                               dirty = true;
15971                               lastDirtyWatch = watch;
15972                               watch.last = watch.eq ? copy(value, null) : value;
15973                               watch.fn(value, ((last === initWatchVal) ? value : last), current);
15974                               if (ttl < 5) {
15975                                 logIdx = 4 - ttl;
15976                                 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15977                                 watchLog[logIdx].push({
15978                                   msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15979                                   newVal: value,
15980                                   oldVal: last
15981                                 });
15982                               }
15983                             } else if (watch === lastDirtyWatch) {
15984                               // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15985                               // have already been tested.
15986                               dirty = false;
15987                               break traverseScopesLoop;
15988                             }
15989                           }
15990                         } catch (e) {
15991                           $exceptionHandler(e);
15992                         }
15993                       }
15994                     }
15995
15996                     // Insanity Warning: scope depth-first traversal
15997                     // yes, this code is a bit crazy, but it works and we have tests to prove it!
15998                     // this piece should be kept in sync with the traversal in $broadcast
15999                     if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16000                         (current !== target && current.$$nextSibling)))) {
16001                       while (current !== target && !(next = current.$$nextSibling)) {
16002                         current = current.$parent;
16003                       }
16004                     }
16005                   } while ((current = next));
16006
16007                   // `break traverseScopesLoop;` takes us to here
16008
16009                   if ((dirty || asyncQueue.length) && !(ttl--)) {
16010                     clearPhase();
16011                     throw $rootScopeMinErr('infdig',
16012                         '{0} $digest() iterations reached. Aborting!\n' +
16013                         'Watchers fired in the last 5 iterations: {1}',
16014                         TTL, watchLog);
16015                   }
16016
16017                 } while (dirty || asyncQueue.length);
16018
16019                 clearPhase();
16020
16021                 while (postDigestQueue.length) {
16022                   try {
16023                     postDigestQueue.shift()();
16024                   } catch (e) {
16025                     $exceptionHandler(e);
16026                   }
16027                 }
16028               },
16029
16030
16031               /**
16032                * @ngdoc event
16033                * @name $rootScope.Scope#$destroy
16034                * @eventType broadcast on scope being destroyed
16035                *
16036                * @description
16037                * Broadcasted when a scope and its children are being destroyed.
16038                *
16039                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16040                * clean up DOM bindings before an element is removed from the DOM.
16041                */
16042
16043               /**
16044                * @ngdoc method
16045                * @name $rootScope.Scope#$destroy
16046                * @kind function
16047                *
16048                * @description
16049                * Removes the current scope (and all of its children) from the parent scope. Removal implies
16050                * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16051                * propagate to the current scope and its children. Removal also implies that the current
16052                * scope is eligible for garbage collection.
16053                *
16054                * The `$destroy()` is usually used by directives such as
16055                * {@link ng.directive:ngRepeat ngRepeat} for managing the
16056                * unrolling of the loop.
16057                *
16058                * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16059                * Application code can register a `$destroy` event handler that will give it a chance to
16060                * perform any necessary cleanup.
16061                *
16062                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16063                * clean up DOM bindings before an element is removed from the DOM.
16064                */
16065               $destroy: function() {
16066                 // We can't destroy a scope that has been already destroyed.
16067                 if (this.$$destroyed) return;
16068                 var parent = this.$parent;
16069
16070                 this.$broadcast('$destroy');
16071                 this.$$destroyed = true;
16072
16073                 if (this === $rootScope) {
16074                   //Remove handlers attached to window when $rootScope is removed
16075                   $browser.$$applicationDestroyed();
16076                 }
16077
16078                 incrementWatchersCount(this, -this.$$watchersCount);
16079                 for (var eventName in this.$$listenerCount) {
16080                   decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16081                 }
16082
16083                 // sever all the references to parent scopes (after this cleanup, the current scope should
16084                 // not be retained by any of our references and should be eligible for garbage collection)
16085                 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16086                 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16087                 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16088                 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16089
16090                 // Disable listeners, watchers and apply/digest methods
16091                 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16092                 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16093                 this.$$listeners = {};
16094
16095                 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16096                 this.$$nextSibling = null;
16097                 cleanUpScope(this);
16098               },
16099
16100               /**
16101                * @ngdoc method
16102                * @name $rootScope.Scope#$eval
16103                * @kind function
16104                *
16105                * @description
16106                * Executes the `expression` on the current scope and returns the result. Any exceptions in
16107                * the expression are propagated (uncaught). This is useful when evaluating Angular
16108                * expressions.
16109                *
16110                * # Example
16111                * ```js
16112                    var scope = ng.$rootScope.Scope();
16113                    scope.a = 1;
16114                    scope.b = 2;
16115
16116                    expect(scope.$eval('a+b')).toEqual(3);
16117                    expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16118                * ```
16119                *
16120                * @param {(string|function())=} expression An angular expression to be executed.
16121                *
16122                *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16123                *    - `function(scope)`: execute the function with the current `scope` parameter.
16124                *
16125                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16126                * @returns {*} The result of evaluating the expression.
16127                */
16128               $eval: function(expr, locals) {
16129                 return $parse(expr)(this, locals);
16130               },
16131
16132               /**
16133                * @ngdoc method
16134                * @name $rootScope.Scope#$evalAsync
16135                * @kind function
16136                *
16137                * @description
16138                * Executes the expression on the current scope at a later point in time.
16139                *
16140                * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16141                * that:
16142                *
16143                *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16144                *     rendering).
16145                *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16146                *     `expression` execution.
16147                *
16148                * Any exceptions from the execution of the expression are forwarded to the
16149                * {@link ng.$exceptionHandler $exceptionHandler} service.
16150                *
16151                * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16152                * will be scheduled. However, it is encouraged to always call code that changes the model
16153                * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16154                *
16155                * @param {(string|function())=} expression An angular expression to be executed.
16156                *
16157                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16158                *    - `function(scope)`: execute the function with the current `scope` parameter.
16159                *
16160                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16161                */
16162               $evalAsync: function(expr, locals) {
16163                 // if we are outside of an $digest loop and this is the first time we are scheduling async
16164                 // task also schedule async auto-flush
16165                 if (!$rootScope.$$phase && !asyncQueue.length) {
16166                   $browser.defer(function() {
16167                     if (asyncQueue.length) {
16168                       $rootScope.$digest();
16169                     }
16170                   });
16171                 }
16172
16173                 asyncQueue.push({scope: this, expression: expr, locals: locals});
16174               },
16175
16176               $$postDigest: function(fn) {
16177                 postDigestQueue.push(fn);
16178               },
16179
16180               /**
16181                * @ngdoc method
16182                * @name $rootScope.Scope#$apply
16183                * @kind function
16184                *
16185                * @description
16186                * `$apply()` is used to execute an expression in angular from outside of the angular
16187                * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16188                * Because we are calling into the angular framework we need to perform proper scope life
16189                * cycle of {@link ng.$exceptionHandler exception handling},
16190                * {@link ng.$rootScope.Scope#$digest executing watches}.
16191                *
16192                * ## Life cycle
16193                *
16194                * # Pseudo-Code of `$apply()`
16195                * ```js
16196                    function $apply(expr) {
16197                      try {
16198                        return $eval(expr);
16199                      } catch (e) {
16200                        $exceptionHandler(e);
16201                      } finally {
16202                        $root.$digest();
16203                      }
16204                    }
16205                * ```
16206                *
16207                *
16208                * Scope's `$apply()` method transitions through the following stages:
16209                *
16210                * 1. The {@link guide/expression expression} is executed using the
16211                *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16212                * 2. Any exceptions from the execution of the expression are forwarded to the
16213                *    {@link ng.$exceptionHandler $exceptionHandler} service.
16214                * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16215                *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16216                *
16217                *
16218                * @param {(string|function())=} exp An angular expression to be executed.
16219                *
16220                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16221                *    - `function(scope)`: execute the function with current `scope` parameter.
16222                *
16223                * @returns {*} The result of evaluating the expression.
16224                */
16225               $apply: function(expr) {
16226                 try {
16227                   beginPhase('$apply');
16228                   try {
16229                     return this.$eval(expr);
16230                   } finally {
16231                     clearPhase();
16232                   }
16233                 } catch (e) {
16234                   $exceptionHandler(e);
16235                 } finally {
16236                   try {
16237                     $rootScope.$digest();
16238                   } catch (e) {
16239                     $exceptionHandler(e);
16240                     throw e;
16241                   }
16242                 }
16243               },
16244
16245               /**
16246                * @ngdoc method
16247                * @name $rootScope.Scope#$applyAsync
16248                * @kind function
16249                *
16250                * @description
16251                * Schedule the invocation of $apply to occur at a later time. The actual time difference
16252                * varies across browsers, but is typically around ~10 milliseconds.
16253                *
16254                * This can be used to queue up multiple expressions which need to be evaluated in the same
16255                * digest.
16256                *
16257                * @param {(string|function())=} exp An angular expression to be executed.
16258                *
16259                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16260                *    - `function(scope)`: execute the function with current `scope` parameter.
16261                */
16262               $applyAsync: function(expr) {
16263                 var scope = this;
16264                 expr && applyAsyncQueue.push($applyAsyncExpression);
16265                 scheduleApplyAsync();
16266
16267                 function $applyAsyncExpression() {
16268                   scope.$eval(expr);
16269                 }
16270               },
16271
16272               /**
16273                * @ngdoc method
16274                * @name $rootScope.Scope#$on
16275                * @kind function
16276                *
16277                * @description
16278                * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16279                * discussion of event life cycle.
16280                *
16281                * The event listener function format is: `function(event, args...)`. The `event` object
16282                * passed into the listener has the following attributes:
16283                *
16284                *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16285                *     `$broadcast`-ed.
16286                *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16287                *     event propagates through the scope hierarchy, this property is set to null.
16288                *   - `name` - `{string}`: name of the event.
16289                *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16290                *     further event propagation (available only for events that were `$emit`-ed).
16291                *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16292                *     to true.
16293                *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16294                *
16295                * @param {string} name Event name to listen on.
16296                * @param {function(event, ...args)} listener Function to call when the event is emitted.
16297                * @returns {function()} Returns a deregistration function for this listener.
16298                */
16299               $on: function(name, listener) {
16300                 var namedListeners = this.$$listeners[name];
16301                 if (!namedListeners) {
16302                   this.$$listeners[name] = namedListeners = [];
16303                 }
16304                 namedListeners.push(listener);
16305
16306                 var current = this;
16307                 do {
16308                   if (!current.$$listenerCount[name]) {
16309                     current.$$listenerCount[name] = 0;
16310                   }
16311                   current.$$listenerCount[name]++;
16312                 } while ((current = current.$parent));
16313
16314                 var self = this;
16315                 return function() {
16316                   var indexOfListener = namedListeners.indexOf(listener);
16317                   if (indexOfListener !== -1) {
16318                     namedListeners[indexOfListener] = null;
16319                     decrementListenerCount(self, 1, name);
16320                   }
16321                 };
16322               },
16323
16324
16325               /**
16326                * @ngdoc method
16327                * @name $rootScope.Scope#$emit
16328                * @kind function
16329                *
16330                * @description
16331                * Dispatches an event `name` upwards through the scope hierarchy notifying the
16332                * registered {@link ng.$rootScope.Scope#$on} listeners.
16333                *
16334                * The event life cycle starts at the scope on which `$emit` was called. All
16335                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16336                * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16337                * registered listeners along the way. The event will stop propagating if one of the listeners
16338                * cancels it.
16339                *
16340                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16341                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16342                *
16343                * @param {string} name Event name to emit.
16344                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16345                * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16346                */
16347               $emit: function(name, args) {
16348                 var empty = [],
16349                     namedListeners,
16350                     scope = this,
16351                     stopPropagation = false,
16352                     event = {
16353                       name: name,
16354                       targetScope: scope,
16355                       stopPropagation: function() {stopPropagation = true;},
16356                       preventDefault: function() {
16357                         event.defaultPrevented = true;
16358                       },
16359                       defaultPrevented: false
16360                     },
16361                     listenerArgs = concat([event], arguments, 1),
16362                     i, length;
16363
16364                 do {
16365                   namedListeners = scope.$$listeners[name] || empty;
16366                   event.currentScope = scope;
16367                   for (i = 0, length = namedListeners.length; i < length; i++) {
16368
16369                     // if listeners were deregistered, defragment the array
16370                     if (!namedListeners[i]) {
16371                       namedListeners.splice(i, 1);
16372                       i--;
16373                       length--;
16374                       continue;
16375                     }
16376                     try {
16377                       //allow all listeners attached to the current scope to run
16378                       namedListeners[i].apply(null, listenerArgs);
16379                     } catch (e) {
16380                       $exceptionHandler(e);
16381                     }
16382                   }
16383                   //if any listener on the current scope stops propagation, prevent bubbling
16384                   if (stopPropagation) {
16385                     event.currentScope = null;
16386                     return event;
16387                   }
16388                   //traverse upwards
16389                   scope = scope.$parent;
16390                 } while (scope);
16391
16392                 event.currentScope = null;
16393
16394                 return event;
16395               },
16396
16397
16398               /**
16399                * @ngdoc method
16400                * @name $rootScope.Scope#$broadcast
16401                * @kind function
16402                *
16403                * @description
16404                * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16405                * registered {@link ng.$rootScope.Scope#$on} listeners.
16406                *
16407                * The event life cycle starts at the scope on which `$broadcast` was called. All
16408                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16409                * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16410                * scope and calls all registered listeners along the way. The event cannot be canceled.
16411                *
16412                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16413                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16414                *
16415                * @param {string} name Event name to broadcast.
16416                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16417                * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16418                */
16419               $broadcast: function(name, args) {
16420                 var target = this,
16421                     current = target,
16422                     next = target,
16423                     event = {
16424                       name: name,
16425                       targetScope: target,
16426                       preventDefault: function() {
16427                         event.defaultPrevented = true;
16428                       },
16429                       defaultPrevented: false
16430                     };
16431
16432                 if (!target.$$listenerCount[name]) return event;
16433
16434                 var listenerArgs = concat([event], arguments, 1),
16435                     listeners, i, length;
16436
16437                 //down while you can, then up and next sibling or up and next sibling until back at root
16438                 while ((current = next)) {
16439                   event.currentScope = current;
16440                   listeners = current.$$listeners[name] || [];
16441                   for (i = 0, length = listeners.length; i < length; i++) {
16442                     // if listeners were deregistered, defragment the array
16443                     if (!listeners[i]) {
16444                       listeners.splice(i, 1);
16445                       i--;
16446                       length--;
16447                       continue;
16448                     }
16449
16450                     try {
16451                       listeners[i].apply(null, listenerArgs);
16452                     } catch (e) {
16453                       $exceptionHandler(e);
16454                     }
16455                   }
16456
16457                   // Insanity Warning: scope depth-first traversal
16458                   // yes, this code is a bit crazy, but it works and we have tests to prove it!
16459                   // this piece should be kept in sync with the traversal in $digest
16460                   // (though it differs due to having the extra check for $$listenerCount)
16461                   if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16462                       (current !== target && current.$$nextSibling)))) {
16463                     while (current !== target && !(next = current.$$nextSibling)) {
16464                       current = current.$parent;
16465                     }
16466                   }
16467                 }
16468
16469                 event.currentScope = null;
16470                 return event;
16471               }
16472             };
16473
16474             var $rootScope = new Scope();
16475
16476             //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16477             var asyncQueue = $rootScope.$$asyncQueue = [];
16478             var postDigestQueue = $rootScope.$$postDigestQueue = [];
16479             var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16480
16481             return $rootScope;
16482
16483
16484             function beginPhase(phase) {
16485               if ($rootScope.$$phase) {
16486                 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16487               }
16488
16489               $rootScope.$$phase = phase;
16490             }
16491
16492             function clearPhase() {
16493               $rootScope.$$phase = null;
16494             }
16495
16496             function incrementWatchersCount(current, count) {
16497               do {
16498                 current.$$watchersCount += count;
16499               } while ((current = current.$parent));
16500             }
16501
16502             function decrementListenerCount(current, count, name) {
16503               do {
16504                 current.$$listenerCount[name] -= count;
16505
16506                 if (current.$$listenerCount[name] === 0) {
16507                   delete current.$$listenerCount[name];
16508                 }
16509               } while ((current = current.$parent));
16510             }
16511
16512             /**
16513              * function used as an initial value for watchers.
16514              * because it's unique we can easily tell it apart from other values
16515              */
16516             function initWatchVal() {}
16517
16518             function flushApplyAsync() {
16519               while (applyAsyncQueue.length) {
16520                 try {
16521                   applyAsyncQueue.shift()();
16522                 } catch (e) {
16523                   $exceptionHandler(e);
16524                 }
16525               }
16526               applyAsyncId = null;
16527             }
16528
16529             function scheduleApplyAsync() {
16530               if (applyAsyncId === null) {
16531                 applyAsyncId = $browser.defer(function() {
16532                   $rootScope.$apply(flushApplyAsync);
16533                 });
16534               }
16535             }
16536           }];
16537         }
16538
16539         /**
16540          * @description
16541          * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16542          */
16543         function $$SanitizeUriProvider() {
16544           var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16545             imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16546
16547           /**
16548            * @description
16549            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16550            * urls during a[href] sanitization.
16551            *
16552            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16553            *
16554            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16555            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16556            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16557            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16558            *
16559            * @param {RegExp=} regexp New regexp to whitelist urls with.
16560            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16561            *    chaining otherwise.
16562            */
16563           this.aHrefSanitizationWhitelist = function(regexp) {
16564             if (isDefined(regexp)) {
16565               aHrefSanitizationWhitelist = regexp;
16566               return this;
16567             }
16568             return aHrefSanitizationWhitelist;
16569           };
16570
16571
16572           /**
16573            * @description
16574            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16575            * urls during img[src] sanitization.
16576            *
16577            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16578            *
16579            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16580            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16581            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16582            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16583            *
16584            * @param {RegExp=} regexp New regexp to whitelist urls with.
16585            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16586            *    chaining otherwise.
16587            */
16588           this.imgSrcSanitizationWhitelist = function(regexp) {
16589             if (isDefined(regexp)) {
16590               imgSrcSanitizationWhitelist = regexp;
16591               return this;
16592             }
16593             return imgSrcSanitizationWhitelist;
16594           };
16595
16596           this.$get = function() {
16597             return function sanitizeUri(uri, isImage) {
16598               var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16599               var normalizedVal;
16600               normalizedVal = urlResolve(uri).href;
16601               if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16602                 return 'unsafe:' + normalizedVal;
16603               }
16604               return uri;
16605             };
16606           };
16607         }
16608
16609         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16610          *     Any commits to this file should be reviewed with security in mind.  *
16611          *   Changes to this file can potentially create security vulnerabilities. *
16612          *          An approval from 2 Core members with history of modifying      *
16613          *                         this file is required.                          *
16614          *                                                                         *
16615          *  Does the change somehow allow for arbitrary javascript to be executed? *
16616          *    Or allows for someone to change the prototype of built-in objects?   *
16617          *     Or gives undesired access to variables likes document or window?    *
16618          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16619
16620         var $sceMinErr = minErr('$sce');
16621
16622         var SCE_CONTEXTS = {
16623           HTML: 'html',
16624           CSS: 'css',
16625           URL: 'url',
16626           // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16627           // url.  (e.g. ng-include, script src, templateUrl)
16628           RESOURCE_URL: 'resourceUrl',
16629           JS: 'js'
16630         };
16631
16632         // Helper functions follow.
16633
16634         function adjustMatcher(matcher) {
16635           if (matcher === 'self') {
16636             return matcher;
16637           } else if (isString(matcher)) {
16638             // Strings match exactly except for 2 wildcards - '*' and '**'.
16639             // '*' matches any character except those from the set ':/.?&'.
16640             // '**' matches any character (like .* in a RegExp).
16641             // More than 2 *'s raises an error as it's ill defined.
16642             if (matcher.indexOf('***') > -1) {
16643               throw $sceMinErr('iwcard',
16644                   'Illegal sequence *** in string matcher.  String: {0}', matcher);
16645             }
16646             matcher = escapeForRegexp(matcher).
16647                           replace('\\*\\*', '.*').
16648                           replace('\\*', '[^:/.?&;]*');
16649             return new RegExp('^' + matcher + '$');
16650           } else if (isRegExp(matcher)) {
16651             // The only other type of matcher allowed is a Regexp.
16652             // Match entire URL / disallow partial matches.
16653             // Flags are reset (i.e. no global, ignoreCase or multiline)
16654             return new RegExp('^' + matcher.source + '$');
16655           } else {
16656             throw $sceMinErr('imatcher',
16657                 'Matchers may only be "self", string patterns or RegExp objects');
16658           }
16659         }
16660
16661
16662         function adjustMatchers(matchers) {
16663           var adjustedMatchers = [];
16664           if (isDefined(matchers)) {
16665             forEach(matchers, function(matcher) {
16666               adjustedMatchers.push(adjustMatcher(matcher));
16667             });
16668           }
16669           return adjustedMatchers;
16670         }
16671
16672
16673         /**
16674          * @ngdoc service
16675          * @name $sceDelegate
16676          * @kind function
16677          *
16678          * @description
16679          *
16680          * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16681          * Contextual Escaping (SCE)} services to AngularJS.
16682          *
16683          * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16684          * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16685          * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16686          * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16687          * work because `$sce` delegates to `$sceDelegate` for these operations.
16688          *
16689          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16690          *
16691          * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16692          * can override it completely to change the behavior of `$sce`, the common case would
16693          * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16694          * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16695          * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16696          * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16697          * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16698          */
16699
16700         /**
16701          * @ngdoc provider
16702          * @name $sceDelegateProvider
16703          * @description
16704          *
16705          * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16706          * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16707          * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16708          * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16709          * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16710          *
16711          * For the general details about this service in Angular, read the main page for {@link ng.$sce
16712          * Strict Contextual Escaping (SCE)}.
16713          *
16714          * **Example**:  Consider the following case. <a name="example"></a>
16715          *
16716          * - your app is hosted at url `http://myapp.example.com/`
16717          * - but some of your templates are hosted on other domains you control such as
16718          *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16719          * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16720          *
16721          * Here is what a secure configuration for this scenario might look like:
16722          *
16723          * ```
16724          *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16725          *    $sceDelegateProvider.resourceUrlWhitelist([
16726          *      // Allow same origin resource loads.
16727          *      'self',
16728          *      // Allow loading from our assets domain.  Notice the difference between * and **.
16729          *      'http://srv*.assets.example.com/**'
16730          *    ]);
16731          *
16732          *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16733          *    $sceDelegateProvider.resourceUrlBlacklist([
16734          *      'http://myapp.example.com/clickThru**'
16735          *    ]);
16736          *  });
16737          * ```
16738          */
16739
16740         function $SceDelegateProvider() {
16741           this.SCE_CONTEXTS = SCE_CONTEXTS;
16742
16743           // Resource URLs can also be trusted by policy.
16744           var resourceUrlWhitelist = ['self'],
16745               resourceUrlBlacklist = [];
16746
16747           /**
16748            * @ngdoc method
16749            * @name $sceDelegateProvider#resourceUrlWhitelist
16750            * @kind function
16751            *
16752            * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16753            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16754            *     changes to the array are ignored.
16755            *
16756            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16757            *     allowed in this array.
16758            *
16759            *     Note: **an empty whitelist array will block all URLs**!
16760            *
16761            * @return {Array} the currently set whitelist array.
16762            *
16763            * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16764            * same origin resource requests.
16765            *
16766            * @description
16767            * Sets/Gets the whitelist of trusted resource URLs.
16768            */
16769           this.resourceUrlWhitelist = function(value) {
16770             if (arguments.length) {
16771               resourceUrlWhitelist = adjustMatchers(value);
16772             }
16773             return resourceUrlWhitelist;
16774           };
16775
16776           /**
16777            * @ngdoc method
16778            * @name $sceDelegateProvider#resourceUrlBlacklist
16779            * @kind function
16780            *
16781            * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16782            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16783            *     changes to the array are ignored.
16784            *
16785            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16786            *     allowed in this array.
16787            *
16788            *     The typical usage for the blacklist is to **block
16789            *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16790            *     these would otherwise be trusted but actually return content from the redirected domain.
16791            *
16792            *     Finally, **the blacklist overrides the whitelist** and has the final say.
16793            *
16794            * @return {Array} the currently set blacklist array.
16795            *
16796            * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16797            * is no blacklist.)
16798            *
16799            * @description
16800            * Sets/Gets the blacklist of trusted resource URLs.
16801            */
16802
16803           this.resourceUrlBlacklist = function(value) {
16804             if (arguments.length) {
16805               resourceUrlBlacklist = adjustMatchers(value);
16806             }
16807             return resourceUrlBlacklist;
16808           };
16809
16810           this.$get = ['$injector', function($injector) {
16811
16812             var htmlSanitizer = function htmlSanitizer(html) {
16813               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16814             };
16815
16816             if ($injector.has('$sanitize')) {
16817               htmlSanitizer = $injector.get('$sanitize');
16818             }
16819
16820
16821             function matchUrl(matcher, parsedUrl) {
16822               if (matcher === 'self') {
16823                 return urlIsSameOrigin(parsedUrl);
16824               } else {
16825                 // definitely a regex.  See adjustMatchers()
16826                 return !!matcher.exec(parsedUrl.href);
16827               }
16828             }
16829
16830             function isResourceUrlAllowedByPolicy(url) {
16831               var parsedUrl = urlResolve(url.toString());
16832               var i, n, allowed = false;
16833               // Ensure that at least one item from the whitelist allows this url.
16834               for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16835                 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16836                   allowed = true;
16837                   break;
16838                 }
16839               }
16840               if (allowed) {
16841                 // Ensure that no item from the blacklist blocked this url.
16842                 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16843                   if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16844                     allowed = false;
16845                     break;
16846                   }
16847                 }
16848               }
16849               return allowed;
16850             }
16851
16852             function generateHolderType(Base) {
16853               var holderType = function TrustedValueHolderType(trustedValue) {
16854                 this.$$unwrapTrustedValue = function() {
16855                   return trustedValue;
16856                 };
16857               };
16858               if (Base) {
16859                 holderType.prototype = new Base();
16860               }
16861               holderType.prototype.valueOf = function sceValueOf() {
16862                 return this.$$unwrapTrustedValue();
16863               };
16864               holderType.prototype.toString = function sceToString() {
16865                 return this.$$unwrapTrustedValue().toString();
16866               };
16867               return holderType;
16868             }
16869
16870             var trustedValueHolderBase = generateHolderType(),
16871                 byType = {};
16872
16873             byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16874             byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16875             byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16876             byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16877             byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16878
16879             /**
16880              * @ngdoc method
16881              * @name $sceDelegate#trustAs
16882              *
16883              * @description
16884              * Returns an object that is trusted by angular for use in specified strict
16885              * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16886              * attribute interpolation, any dom event binding attribute interpolation
16887              * such as for onclick,  etc.) that uses the provided value.
16888              * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16889              *
16890              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16891              *   resourceUrl, html, js and css.
16892              * @param {*} value The value that that should be considered trusted/safe.
16893              * @returns {*} A value that can be used to stand in for the provided `value` in places
16894              * where Angular expects a $sce.trustAs() return value.
16895              */
16896             function trustAs(type, trustedValue) {
16897               var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16898               if (!Constructor) {
16899                 throw $sceMinErr('icontext',
16900                     'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16901                     type, trustedValue);
16902               }
16903               if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16904                 return trustedValue;
16905               }
16906               // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16907               // mutable objects, we ensure here that the value passed in is actually a string.
16908               if (typeof trustedValue !== 'string') {
16909                 throw $sceMinErr('itype',
16910                     'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16911                     type);
16912               }
16913               return new Constructor(trustedValue);
16914             }
16915
16916             /**
16917              * @ngdoc method
16918              * @name $sceDelegate#valueOf
16919              *
16920              * @description
16921              * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16922              * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16923              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16924              *
16925              * If the passed parameter is not a value that had been returned by {@link
16926              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16927              *
16928              * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16929              *      call or anything else.
16930              * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16931              *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16932              *     `value` unchanged.
16933              */
16934             function valueOf(maybeTrusted) {
16935               if (maybeTrusted instanceof trustedValueHolderBase) {
16936                 return maybeTrusted.$$unwrapTrustedValue();
16937               } else {
16938                 return maybeTrusted;
16939               }
16940             }
16941
16942             /**
16943              * @ngdoc method
16944              * @name $sceDelegate#getTrusted
16945              *
16946              * @description
16947              * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16948              * returns the originally supplied value if the queried context type is a supertype of the
16949              * created type.  If this condition isn't satisfied, throws an exception.
16950              *
16951              * @param {string} type The kind of context in which this value is to be used.
16952              * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16953              *     `$sceDelegate.trustAs`} call.
16954              * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16955              *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16956              */
16957             function getTrusted(type, maybeTrusted) {
16958               if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16959                 return maybeTrusted;
16960               }
16961               var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16962               if (constructor && maybeTrusted instanceof constructor) {
16963                 return maybeTrusted.$$unwrapTrustedValue();
16964               }
16965               // If we get here, then we may only take one of two actions.
16966               // 1. sanitize the value for the requested type, or
16967               // 2. throw an exception.
16968               if (type === SCE_CONTEXTS.RESOURCE_URL) {
16969                 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16970                   return maybeTrusted;
16971                 } else {
16972                   throw $sceMinErr('insecurl',
16973                       'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16974                       maybeTrusted.toString());
16975                 }
16976               } else if (type === SCE_CONTEXTS.HTML) {
16977                 return htmlSanitizer(maybeTrusted);
16978               }
16979               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16980             }
16981
16982             return { trustAs: trustAs,
16983                      getTrusted: getTrusted,
16984                      valueOf: valueOf };
16985           }];
16986         }
16987
16988
16989         /**
16990          * @ngdoc provider
16991          * @name $sceProvider
16992          * @description
16993          *
16994          * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16995          * -   enable/disable Strict Contextual Escaping (SCE) in a module
16996          * -   override the default implementation with a custom delegate
16997          *
16998          * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16999          */
17000
17001         /* jshint maxlen: false*/
17002
17003         /**
17004          * @ngdoc service
17005          * @name $sce
17006          * @kind function
17007          *
17008          * @description
17009          *
17010          * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17011          *
17012          * # Strict Contextual Escaping
17013          *
17014          * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17015          * contexts to result in a value that is marked as safe to use for that context.  One example of
17016          * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17017          * to these contexts as privileged or SCE contexts.
17018          *
17019          * As of version 1.2, Angular ships with SCE enabled by default.
17020          *
17021          * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17022          * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17023          * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17024          * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17025          * to the top of your HTML document.
17026          *
17027          * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17028          * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17029          *
17030          * Here's an example of a binding in a privileged context:
17031          *
17032          * ```
17033          * <input ng-model="userHtml" aria-label="User input">
17034          * <div ng-bind-html="userHtml"></div>
17035          * ```
17036          *
17037          * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17038          * disabled, this application allows the user to render arbitrary HTML into the DIV.
17039          * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17040          * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17041          * security vulnerabilities.)
17042          *
17043          * For the case of HTML, you might use a library, either on the client side, or on the server side,
17044          * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17045          *
17046          * How would you ensure that every place that used these types of bindings was bound to a value that
17047          * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17048          * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17049          * properties/fields and forgot to update the binding to the sanitized value?
17050          *
17051          * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17052          * determine that something explicitly says it's safe to use a value for binding in that
17053          * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17054          * for those values that you can easily tell are safe - because they were received from your server,
17055          * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17056          * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17057          * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17058          *
17059          * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17060          * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17061          * obtain values that will be accepted by SCE / privileged contexts.
17062          *
17063          *
17064          * ## How does it work?
17065          *
17066          * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17067          * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17068          * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17069          * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17070          *
17071          * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17072          * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17073          * simplified):
17074          *
17075          * ```
17076          * var ngBindHtmlDirective = ['$sce', function($sce) {
17077          *   return function(scope, element, attr) {
17078          *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17079          *       element.html(value || '');
17080          *     });
17081          *   };
17082          * }];
17083          * ```
17084          *
17085          * ## Impact on loading templates
17086          *
17087          * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17088          * `templateUrl`'s specified by {@link guide/directive directives}.
17089          *
17090          * By default, Angular only loads templates from the same domain and protocol as the application
17091          * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17092          * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17093          * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17094          * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17095          *
17096          * *Please note*:
17097          * The browser's
17098          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17099          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17100          * policy apply in addition to this and may further restrict whether the template is successfully
17101          * loaded.  This means that without the right CORS policy, loading templates from a different domain
17102          * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17103          * browsers.
17104          *
17105          * ## This feels like too much overhead
17106          *
17107          * It's important to remember that SCE only applies to interpolation expressions.
17108          *
17109          * If your expressions are constant literals, they're automatically trusted and you don't need to
17110          * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17111          * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17112          *
17113          * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17114          * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17115          *
17116          * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17117          * templates in `ng-include` from your application's domain without having to even know about SCE.
17118          * It blocks loading templates from other domains or loading templates over http from an https
17119          * served document.  You can change these by setting your own custom {@link
17120          * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17121          * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17122          *
17123          * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17124          * application that's secure and can be audited to verify that with much more ease than bolting
17125          * security onto an application later.
17126          *
17127          * <a name="contexts"></a>
17128          * ## What trusted context types are supported?
17129          *
17130          * | Context             | Notes          |
17131          * |---------------------|----------------|
17132          * | `$sce.HTML`         | For HTML that's safe to source into the application.  The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
17133          * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17134          * | `$sce.URL`          | For URLs that are safe to follow as links.  Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
17135          * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application.  Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.)  <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
17136          * | `$sce.JS`           | For JavaScript that is safe to execute in your application's context.  Currently unused.  Feel free to use it in your own directives. |
17137          *
17138          * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17139          *
17140          *  Each element in these arrays must be one of the following:
17141          *
17142          *  - **'self'**
17143          *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17144          *      domain** as the application document using the **same protocol**.
17145          *  - **String** (except the special value `'self'`)
17146          *    - The string is matched against the full *normalized / absolute URL* of the resource
17147          *      being tested (substring matches are not good enough.)
17148          *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17149          *      match themselves.
17150          *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17151          *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17152          *      in a whitelist.
17153          *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17154          *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17155          *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17156          *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17157          *      http://foo.example.com/templates/**).
17158          *  - **RegExp** (*see caveat below*)
17159          *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17160          *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17161          *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17162          *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17163          *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17164          *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17165          *      is highly recommended to use the string patterns and only fall back to regular expressions
17166          *      as a last resort.
17167          *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17168          *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17169          *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17170          *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17171          *    - If you are generating your JavaScript from some other templating engine (not
17172          *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17173          *      remember to escape your regular expression (and be aware that you might need more than
17174          *      one level of escaping depending on your templating engine and the way you interpolated
17175          *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17176          *      enough before coding your own.  E.g. Ruby has
17177          *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17178          *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17179          *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17180          *      Closure library's [goog.string.regExpEscape(s)](
17181          *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17182          *
17183          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17184          *
17185          * ## Show me an example using SCE.
17186          *
17187          * <example module="mySceApp" deps="angular-sanitize.js">
17188          * <file name="index.html">
17189          *   <div ng-controller="AppController as myCtrl">
17190          *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17191          *     <b>User comments</b><br>
17192          *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17193          *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17194          *     exploit.
17195          *     <div class="well">
17196          *       <div ng-repeat="userComment in myCtrl.userComments">
17197          *         <b>{{userComment.name}}</b>:
17198          *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17199          *         <br>
17200          *       </div>
17201          *     </div>
17202          *   </div>
17203          * </file>
17204          *
17205          * <file name="script.js">
17206          *   angular.module('mySceApp', ['ngSanitize'])
17207          *     .controller('AppController', ['$http', '$templateCache', '$sce',
17208          *       function($http, $templateCache, $sce) {
17209          *         var self = this;
17210          *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17211          *           self.userComments = userComments;
17212          *         });
17213          *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17214          *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17215          *             'sanitization.&quot;">Hover over this text.</span>');
17216          *       }]);
17217          * </file>
17218          *
17219          * <file name="test_data.json">
17220          * [
17221          *   { "name": "Alice",
17222          *     "htmlComment":
17223          *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17224          *   },
17225          *   { "name": "Bob",
17226          *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17227          *   }
17228          * ]
17229          * </file>
17230          *
17231          * <file name="protractor.js" type="protractor">
17232          *   describe('SCE doc demo', function() {
17233          *     it('should sanitize untrusted values', function() {
17234          *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17235          *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17236          *     });
17237          *
17238          *     it('should NOT sanitize explicitly trusted values', function() {
17239          *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17240          *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17241          *           'sanitization.&quot;">Hover over this text.</span>');
17242          *     });
17243          *   });
17244          * </file>
17245          * </example>
17246          *
17247          *
17248          *
17249          * ## Can I disable SCE completely?
17250          *
17251          * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17252          * for little coding overhead.  It will be much harder to take an SCE disabled application and
17253          * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17254          * for cases where you have a lot of existing code that was written before SCE was introduced and
17255          * you're migrating them a module at a time.
17256          *
17257          * That said, here's how you can completely disable SCE:
17258          *
17259          * ```
17260          * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17261          *   // Completely disable SCE.  For demonstration purposes only!
17262          *   // Do not use in new projects.
17263          *   $sceProvider.enabled(false);
17264          * });
17265          * ```
17266          *
17267          */
17268         /* jshint maxlen: 100 */
17269
17270         function $SceProvider() {
17271           var enabled = true;
17272
17273           /**
17274            * @ngdoc method
17275            * @name $sceProvider#enabled
17276            * @kind function
17277            *
17278            * @param {boolean=} value If provided, then enables/disables SCE.
17279            * @return {boolean} true if SCE is enabled, false otherwise.
17280            *
17281            * @description
17282            * Enables/disables SCE and returns the current value.
17283            */
17284           this.enabled = function(value) {
17285             if (arguments.length) {
17286               enabled = !!value;
17287             }
17288             return enabled;
17289           };
17290
17291
17292           /* Design notes on the default implementation for SCE.
17293            *
17294            * The API contract for the SCE delegate
17295            * -------------------------------------
17296            * The SCE delegate object must provide the following 3 methods:
17297            *
17298            * - trustAs(contextEnum, value)
17299            *     This method is used to tell the SCE service that the provided value is OK to use in the
17300            *     contexts specified by contextEnum.  It must return an object that will be accepted by
17301            *     getTrusted() for a compatible contextEnum and return this value.
17302            *
17303            * - valueOf(value)
17304            *     For values that were not produced by trustAs(), return them as is.  For values that were
17305            *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17306            *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17307            *     such a value.
17308            *
17309            * - getTrusted(contextEnum, value)
17310            *     This function should return the a value that is safe to use in the context specified by
17311            *     contextEnum or throw and exception otherwise.
17312            *
17313            * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17314            * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17315            * instance, an implementation could maintain a registry of all trusted objects by context.  In
17316            * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17317            * return the same object passed in if it was found in the registry under a compatible context or
17318            * throw an exception otherwise.  An implementation might only wrap values some of the time based
17319            * on some criteria.  getTrusted() might return a value and not throw an exception for special
17320            * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17321            *
17322            *
17323            * A note on the inheritance model for SCE contexts
17324            * ------------------------------------------------
17325            * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17326            * is purely an implementation details.
17327            *
17328            * The contract is simply this:
17329            *
17330            *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17331            *     will also succeed.
17332            *
17333            * Inheritance happens to capture this in a natural way.  In some future, we
17334            * may not use inheritance anymore.  That is OK because no code outside of
17335            * sce.js and sceSpecs.js would need to be aware of this detail.
17336            */
17337
17338           this.$get = ['$parse', '$sceDelegate', function(
17339                         $parse,   $sceDelegate) {
17340             // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17341             // the "expression(javascript expression)" syntax which is insecure.
17342             if (enabled && msie < 8) {
17343               throw $sceMinErr('iequirks',
17344                 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17345                 'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17346                 'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17347             }
17348
17349             var sce = shallowCopy(SCE_CONTEXTS);
17350
17351             /**
17352              * @ngdoc method
17353              * @name $sce#isEnabled
17354              * @kind function
17355              *
17356              * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17357              * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17358              *
17359              * @description
17360              * Returns a boolean indicating if SCE is enabled.
17361              */
17362             sce.isEnabled = function() {
17363               return enabled;
17364             };
17365             sce.trustAs = $sceDelegate.trustAs;
17366             sce.getTrusted = $sceDelegate.getTrusted;
17367             sce.valueOf = $sceDelegate.valueOf;
17368
17369             if (!enabled) {
17370               sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17371               sce.valueOf = identity;
17372             }
17373
17374             /**
17375              * @ngdoc method
17376              * @name $sce#parseAs
17377              *
17378              * @description
17379              * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17380              * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17381              * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17382              * *result*)}
17383              *
17384              * @param {string} type The kind of SCE context in which this result will be used.
17385              * @param {string} expression String expression to compile.
17386              * @returns {function(context, locals)} a function which represents the compiled expression:
17387              *
17388              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17389              *      are evaluated against (typically a scope object).
17390              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17391              *      `context`.
17392              */
17393             sce.parseAs = function sceParseAs(type, expr) {
17394               var parsed = $parse(expr);
17395               if (parsed.literal && parsed.constant) {
17396                 return parsed;
17397               } else {
17398                 return $parse(expr, function(value) {
17399                   return sce.getTrusted(type, value);
17400                 });
17401               }
17402             };
17403
17404             /**
17405              * @ngdoc method
17406              * @name $sce#trustAs
17407              *
17408              * @description
17409              * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17410              * returns an object that is trusted by angular for use in specified strict contextual
17411              * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17412              * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17413              * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17414              * escaping.
17415              *
17416              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17417              *   resourceUrl, html, js and css.
17418              * @param {*} value The value that that should be considered trusted/safe.
17419              * @returns {*} A value that can be used to stand in for the provided `value` in places
17420              * where Angular expects a $sce.trustAs() return value.
17421              */
17422
17423             /**
17424              * @ngdoc method
17425              * @name $sce#trustAsHtml
17426              *
17427              * @description
17428              * Shorthand method.  `$sce.trustAsHtml(value)` →
17429              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17430              *
17431              * @param {*} value The value to trustAs.
17432              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17433              *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17434              *     only accept expressions that are either literal constants or are the
17435              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17436              */
17437
17438             /**
17439              * @ngdoc method
17440              * @name $sce#trustAsUrl
17441              *
17442              * @description
17443              * Shorthand method.  `$sce.trustAsUrl(value)` →
17444              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17445              *
17446              * @param {*} value The value to trustAs.
17447              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17448              *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17449              *     only accept expressions that are either literal constants or are the
17450              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17451              */
17452
17453             /**
17454              * @ngdoc method
17455              * @name $sce#trustAsResourceUrl
17456              *
17457              * @description
17458              * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17459              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17460              *
17461              * @param {*} value The value to trustAs.
17462              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17463              *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17464              *     only accept expressions that are either literal constants or are the return
17465              *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17466              */
17467
17468             /**
17469              * @ngdoc method
17470              * @name $sce#trustAsJs
17471              *
17472              * @description
17473              * Shorthand method.  `$sce.trustAsJs(value)` →
17474              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17475              *
17476              * @param {*} value The value to trustAs.
17477              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17478              *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17479              *     only accept expressions that are either literal constants or are the
17480              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17481              */
17482
17483             /**
17484              * @ngdoc method
17485              * @name $sce#getTrusted
17486              *
17487              * @description
17488              * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17489              * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17490              * originally supplied value if the queried context type is a supertype of the created type.
17491              * If this condition isn't satisfied, throws an exception.
17492              *
17493              * @param {string} type The kind of context in which this value is to be used.
17494              * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17495              *                         call.
17496              * @returns {*} The value the was originally provided to
17497              *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17498              *              Otherwise, throws an exception.
17499              */
17500
17501             /**
17502              * @ngdoc method
17503              * @name $sce#getTrustedHtml
17504              *
17505              * @description
17506              * Shorthand method.  `$sce.getTrustedHtml(value)` →
17507              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17508              *
17509              * @param {*} value The value to pass to `$sce.getTrusted`.
17510              * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17511              */
17512
17513             /**
17514              * @ngdoc method
17515              * @name $sce#getTrustedCss
17516              *
17517              * @description
17518              * Shorthand method.  `$sce.getTrustedCss(value)` →
17519              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17520              *
17521              * @param {*} value The value to pass to `$sce.getTrusted`.
17522              * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17523              */
17524
17525             /**
17526              * @ngdoc method
17527              * @name $sce#getTrustedUrl
17528              *
17529              * @description
17530              * Shorthand method.  `$sce.getTrustedUrl(value)` →
17531              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17532              *
17533              * @param {*} value The value to pass to `$sce.getTrusted`.
17534              * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17535              */
17536
17537             /**
17538              * @ngdoc method
17539              * @name $sce#getTrustedResourceUrl
17540              *
17541              * @description
17542              * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17543              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17544              *
17545              * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17546              * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17547              */
17548
17549             /**
17550              * @ngdoc method
17551              * @name $sce#getTrustedJs
17552              *
17553              * @description
17554              * Shorthand method.  `$sce.getTrustedJs(value)` →
17555              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17556              *
17557              * @param {*} value The value to pass to `$sce.getTrusted`.
17558              * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17559              */
17560
17561             /**
17562              * @ngdoc method
17563              * @name $sce#parseAsHtml
17564              *
17565              * @description
17566              * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17567              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17568              *
17569              * @param {string} expression String expression to compile.
17570              * @returns {function(context, locals)} a function which represents the compiled expression:
17571              *
17572              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17573              *      are evaluated against (typically a scope object).
17574              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17575              *      `context`.
17576              */
17577
17578             /**
17579              * @ngdoc method
17580              * @name $sce#parseAsCss
17581              *
17582              * @description
17583              * Shorthand method.  `$sce.parseAsCss(value)` →
17584              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17585              *
17586              * @param {string} expression String expression to compile.
17587              * @returns {function(context, locals)} a function which represents the compiled expression:
17588              *
17589              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17590              *      are evaluated against (typically a scope object).
17591              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17592              *      `context`.
17593              */
17594
17595             /**
17596              * @ngdoc method
17597              * @name $sce#parseAsUrl
17598              *
17599              * @description
17600              * Shorthand method.  `$sce.parseAsUrl(value)` →
17601              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17602              *
17603              * @param {string} expression String expression to compile.
17604              * @returns {function(context, locals)} a function which represents the compiled expression:
17605              *
17606              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17607              *      are evaluated against (typically a scope object).
17608              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17609              *      `context`.
17610              */
17611
17612             /**
17613              * @ngdoc method
17614              * @name $sce#parseAsResourceUrl
17615              *
17616              * @description
17617              * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17618              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17619              *
17620              * @param {string} expression String expression to compile.
17621              * @returns {function(context, locals)} a function which represents the compiled expression:
17622              *
17623              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17624              *      are evaluated against (typically a scope object).
17625              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17626              *      `context`.
17627              */
17628
17629             /**
17630              * @ngdoc method
17631              * @name $sce#parseAsJs
17632              *
17633              * @description
17634              * Shorthand method.  `$sce.parseAsJs(value)` →
17635              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17636              *
17637              * @param {string} expression String expression to compile.
17638              * @returns {function(context, locals)} a function which represents the compiled expression:
17639              *
17640              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17641              *      are evaluated against (typically a scope object).
17642              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17643              *      `context`.
17644              */
17645
17646             // Shorthand delegations.
17647             var parse = sce.parseAs,
17648                 getTrusted = sce.getTrusted,
17649                 trustAs = sce.trustAs;
17650
17651             forEach(SCE_CONTEXTS, function(enumValue, name) {
17652               var lName = lowercase(name);
17653               sce[camelCase("parse_as_" + lName)] = function(expr) {
17654                 return parse(enumValue, expr);
17655               };
17656               sce[camelCase("get_trusted_" + lName)] = function(value) {
17657                 return getTrusted(enumValue, value);
17658               };
17659               sce[camelCase("trust_as_" + lName)] = function(value) {
17660                 return trustAs(enumValue, value);
17661               };
17662             });
17663
17664             return sce;
17665           }];
17666         }
17667
17668         /**
17669          * !!! This is an undocumented "private" service !!!
17670          *
17671          * @name $sniffer
17672          * @requires $window
17673          * @requires $document
17674          *
17675          * @property {boolean} history Does the browser support html5 history api ?
17676          * @property {boolean} transitions Does the browser support CSS transition events ?
17677          * @property {boolean} animations Does the browser support CSS animation events ?
17678          *
17679          * @description
17680          * This is very simple implementation of testing browser's features.
17681          */
17682         function $SnifferProvider() {
17683           this.$get = ['$window', '$document', function($window, $document) {
17684             var eventSupport = {},
17685                 android =
17686                   toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17687                 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17688                 document = $document[0] || {},
17689                 vendorPrefix,
17690                 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17691                 bodyStyle = document.body && document.body.style,
17692                 transitions = false,
17693                 animations = false,
17694                 match;
17695
17696             if (bodyStyle) {
17697               for (var prop in bodyStyle) {
17698                 if (match = vendorRegex.exec(prop)) {
17699                   vendorPrefix = match[0];
17700                   vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17701                   break;
17702                 }
17703               }
17704
17705               if (!vendorPrefix) {
17706                 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17707               }
17708
17709               transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17710               animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17711
17712               if (android && (!transitions ||  !animations)) {
17713                 transitions = isString(bodyStyle.webkitTransition);
17714                 animations = isString(bodyStyle.webkitAnimation);
17715               }
17716             }
17717
17718
17719             return {
17720               // Android has history.pushState, but it does not update location correctly
17721               // so let's not use the history API at all.
17722               // http://code.google.com/p/android/issues/detail?id=17471
17723               // https://github.com/angular/angular.js/issues/904
17724
17725               // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17726               // so let's not use the history API also
17727               // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17728               // jshint -W018
17729               history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17730               // jshint +W018
17731               hasEvent: function(event) {
17732                 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17733                 // it. In particular the event is not fired when backspace or delete key are pressed or
17734                 // when cut operation is performed.
17735                 // IE10+ implements 'input' event but it erroneously fires under various situations,
17736                 // e.g. when placeholder changes, or a form is focused.
17737                 if (event === 'input' && msie <= 11) return false;
17738
17739                 if (isUndefined(eventSupport[event])) {
17740                   var divElm = document.createElement('div');
17741                   eventSupport[event] = 'on' + event in divElm;
17742                 }
17743
17744                 return eventSupport[event];
17745               },
17746               csp: csp(),
17747               vendorPrefix: vendorPrefix,
17748               transitions: transitions,
17749               animations: animations,
17750               android: android
17751             };
17752           }];
17753         }
17754
17755         var $compileMinErr = minErr('$compile');
17756
17757         /**
17758          * @ngdoc service
17759          * @name $templateRequest
17760          *
17761          * @description
17762          * The `$templateRequest` service runs security checks then downloads the provided template using
17763          * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17764          * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17765          * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17766          * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17767          * when `tpl` is of type string and `$templateCache` has the matching entry.
17768          *
17769          * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17770          * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17771          *
17772          * @return {Promise} a promise for the HTTP response data of the given URL.
17773          *
17774          * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17775          */
17776         function $TemplateRequestProvider() {
17777           this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17778             function handleRequestFn(tpl, ignoreRequestError) {
17779               handleRequestFn.totalPendingRequests++;
17780
17781               // We consider the template cache holds only trusted templates, so
17782               // there's no need to go through whitelisting again for keys that already
17783               // are included in there. This also makes Angular accept any script
17784               // directive, no matter its name. However, we still need to unwrap trusted
17785               // types.
17786               if (!isString(tpl) || !$templateCache.get(tpl)) {
17787                 tpl = $sce.getTrustedResourceUrl(tpl);
17788               }
17789
17790               var transformResponse = $http.defaults && $http.defaults.transformResponse;
17791
17792               if (isArray(transformResponse)) {
17793                 transformResponse = transformResponse.filter(function(transformer) {
17794                   return transformer !== defaultHttpResponseTransform;
17795                 });
17796               } else if (transformResponse === defaultHttpResponseTransform) {
17797                 transformResponse = null;
17798               }
17799
17800               var httpOptions = {
17801                 cache: $templateCache,
17802                 transformResponse: transformResponse
17803               };
17804
17805               return $http.get(tpl, httpOptions)
17806                 ['finally'](function() {
17807                   handleRequestFn.totalPendingRequests--;
17808                 })
17809                 .then(function(response) {
17810                   $templateCache.put(tpl, response.data);
17811                   return response.data;
17812                 }, handleError);
17813
17814               function handleError(resp) {
17815                 if (!ignoreRequestError) {
17816                   throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17817                     tpl, resp.status, resp.statusText);
17818                 }
17819                 return $q.reject(resp);
17820               }
17821             }
17822
17823             handleRequestFn.totalPendingRequests = 0;
17824
17825             return handleRequestFn;
17826           }];
17827         }
17828
17829         function $$TestabilityProvider() {
17830           this.$get = ['$rootScope', '$browser', '$location',
17831                function($rootScope,   $browser,   $location) {
17832
17833             /**
17834              * @name $testability
17835              *
17836              * @description
17837              * The private $$testability service provides a collection of methods for use when debugging
17838              * or by automated test and debugging tools.
17839              */
17840             var testability = {};
17841
17842             /**
17843              * @name $$testability#findBindings
17844              *
17845              * @description
17846              * Returns an array of elements that are bound (via ng-bind or {{}})
17847              * to expressions matching the input.
17848              *
17849              * @param {Element} element The element root to search from.
17850              * @param {string} expression The binding expression to match.
17851              * @param {boolean} opt_exactMatch If true, only returns exact matches
17852              *     for the expression. Filters and whitespace are ignored.
17853              */
17854             testability.findBindings = function(element, expression, opt_exactMatch) {
17855               var bindings = element.getElementsByClassName('ng-binding');
17856               var matches = [];
17857               forEach(bindings, function(binding) {
17858                 var dataBinding = angular.element(binding).data('$binding');
17859                 if (dataBinding) {
17860                   forEach(dataBinding, function(bindingName) {
17861                     if (opt_exactMatch) {
17862                       var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17863                       if (matcher.test(bindingName)) {
17864                         matches.push(binding);
17865                       }
17866                     } else {
17867                       if (bindingName.indexOf(expression) != -1) {
17868                         matches.push(binding);
17869                       }
17870                     }
17871                   });
17872                 }
17873               });
17874               return matches;
17875             };
17876
17877             /**
17878              * @name $$testability#findModels
17879              *
17880              * @description
17881              * Returns an array of elements that are two-way found via ng-model to
17882              * expressions matching the input.
17883              *
17884              * @param {Element} element The element root to search from.
17885              * @param {string} expression The model expression to match.
17886              * @param {boolean} opt_exactMatch If true, only returns exact matches
17887              *     for the expression.
17888              */
17889             testability.findModels = function(element, expression, opt_exactMatch) {
17890               var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17891               for (var p = 0; p < prefixes.length; ++p) {
17892                 var attributeEquals = opt_exactMatch ? '=' : '*=';
17893                 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17894                 var elements = element.querySelectorAll(selector);
17895                 if (elements.length) {
17896                   return elements;
17897                 }
17898               }
17899             };
17900
17901             /**
17902              * @name $$testability#getLocation
17903              *
17904              * @description
17905              * Shortcut for getting the location in a browser agnostic way. Returns
17906              *     the path, search, and hash. (e.g. /path?a=b#hash)
17907              */
17908             testability.getLocation = function() {
17909               return $location.url();
17910             };
17911
17912             /**
17913              * @name $$testability#setLocation
17914              *
17915              * @description
17916              * Shortcut for navigating to a location without doing a full page reload.
17917              *
17918              * @param {string} url The location url (path, search and hash,
17919              *     e.g. /path?a=b#hash) to go to.
17920              */
17921             testability.setLocation = function(url) {
17922               if (url !== $location.url()) {
17923                 $location.url(url);
17924                 $rootScope.$digest();
17925               }
17926             };
17927
17928             /**
17929              * @name $$testability#whenStable
17930              *
17931              * @description
17932              * Calls the callback when $timeout and $http requests are completed.
17933              *
17934              * @param {function} callback
17935              */
17936             testability.whenStable = function(callback) {
17937               $browser.notifyWhenNoOutstandingRequests(callback);
17938             };
17939
17940             return testability;
17941           }];
17942         }
17943
17944         function $TimeoutProvider() {
17945           this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17946                function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17947
17948             var deferreds = {};
17949
17950
17951              /**
17952               * @ngdoc service
17953               * @name $timeout
17954               *
17955               * @description
17956               * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17957               * block and delegates any exceptions to
17958               * {@link ng.$exceptionHandler $exceptionHandler} service.
17959               *
17960               * The return value of calling `$timeout` is a promise, which will be resolved when
17961               * the delay has passed and the timeout function, if provided, is executed.
17962               *
17963               * To cancel a timeout request, call `$timeout.cancel(promise)`.
17964               *
17965               * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17966               * synchronously flush the queue of deferred functions.
17967               *
17968               * If you only want a promise that will be resolved after some specified delay
17969               * then you can call `$timeout` without the `fn` function.
17970               *
17971               * @param {function()=} fn A function, whose execution should be delayed.
17972               * @param {number=} [delay=0] Delay in milliseconds.
17973               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17974               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17975               * @param {...*=} Pass additional parameters to the executed function.
17976               * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17977               *   promise will be resolved with is the return value of the `fn` function.
17978               *
17979               */
17980             function timeout(fn, delay, invokeApply) {
17981               if (!isFunction(fn)) {
17982                 invokeApply = delay;
17983                 delay = fn;
17984                 fn = noop;
17985               }
17986
17987               var args = sliceArgs(arguments, 3),
17988                   skipApply = (isDefined(invokeApply) && !invokeApply),
17989                   deferred = (skipApply ? $$q : $q).defer(),
17990                   promise = deferred.promise,
17991                   timeoutId;
17992
17993               timeoutId = $browser.defer(function() {
17994                 try {
17995                   deferred.resolve(fn.apply(null, args));
17996                 } catch (e) {
17997                   deferred.reject(e);
17998                   $exceptionHandler(e);
17999                 }
18000                 finally {
18001                   delete deferreds[promise.$$timeoutId];
18002                 }
18003
18004                 if (!skipApply) $rootScope.$apply();
18005               }, delay);
18006
18007               promise.$$timeoutId = timeoutId;
18008               deferreds[timeoutId] = deferred;
18009
18010               return promise;
18011             }
18012
18013
18014              /**
18015               * @ngdoc method
18016               * @name $timeout#cancel
18017               *
18018               * @description
18019               * Cancels a task associated with the `promise`. As a result of this, the promise will be
18020               * resolved with a rejection.
18021               *
18022               * @param {Promise=} promise Promise returned by the `$timeout` function.
18023               * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18024               *   canceled.
18025               */
18026             timeout.cancel = function(promise) {
18027               if (promise && promise.$$timeoutId in deferreds) {
18028                 deferreds[promise.$$timeoutId].reject('canceled');
18029                 delete deferreds[promise.$$timeoutId];
18030                 return $browser.defer.cancel(promise.$$timeoutId);
18031               }
18032               return false;
18033             };
18034
18035             return timeout;
18036           }];
18037         }
18038
18039         // NOTE:  The usage of window and document instead of $window and $document here is
18040         // deliberate.  This service depends on the specific behavior of anchor nodes created by the
18041         // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18042         // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18043         // doesn't know about mocked locations and resolves URLs to the real document - which is
18044         // exactly the behavior needed here.  There is little value is mocking these out for this
18045         // service.
18046         var urlParsingNode = document.createElement("a");
18047         var originUrl = urlResolve(window.location.href);
18048
18049
18050         /**
18051          *
18052          * Implementation Notes for non-IE browsers
18053          * ----------------------------------------
18054          * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18055          * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18056          * URL will be resolved into an absolute URL in the context of the application document.
18057          * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18058          * properties are all populated to reflect the normalized URL.  This approach has wide
18059          * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18060          * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18061          *
18062          * Implementation Notes for IE
18063          * ---------------------------
18064          * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18065          * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18066          * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18067          * work around that by performing the parsing in a 2nd step by taking a previously normalized
18068          * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18069          * properties such as protocol, hostname, port, etc.
18070          *
18071          * References:
18072          *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18073          *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18074          *   http://url.spec.whatwg.org/#urlutils
18075          *   https://github.com/angular/angular.js/pull/2902
18076          *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18077          *
18078          * @kind function
18079          * @param {string} url The URL to be parsed.
18080          * @description Normalizes and parses a URL.
18081          * @returns {object} Returns the normalized URL as a dictionary.
18082          *
18083          *   | member name   | Description    |
18084          *   |---------------|----------------|
18085          *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18086          *   | protocol      | The protocol including the trailing colon                              |
18087          *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18088          *   | search        | The search params, minus the question mark                             |
18089          *   | hash          | The hash string, minus the hash symbol
18090          *   | hostname      | The hostname
18091          *   | port          | The port, without ":"
18092          *   | pathname      | The pathname, beginning with "/"
18093          *
18094          */
18095         function urlResolve(url) {
18096           var href = url;
18097
18098           if (msie) {
18099             // Normalize before parse.  Refer Implementation Notes on why this is
18100             // done in two steps on IE.
18101             urlParsingNode.setAttribute("href", href);
18102             href = urlParsingNode.href;
18103           }
18104
18105           urlParsingNode.setAttribute('href', href);
18106
18107           // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18108           return {
18109             href: urlParsingNode.href,
18110             protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18111             host: urlParsingNode.host,
18112             search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18113             hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18114             hostname: urlParsingNode.hostname,
18115             port: urlParsingNode.port,
18116             pathname: (urlParsingNode.pathname.charAt(0) === '/')
18117               ? urlParsingNode.pathname
18118               : '/' + urlParsingNode.pathname
18119           };
18120         }
18121
18122         /**
18123          * Parse a request URL and determine whether this is a same-origin request as the application document.
18124          *
18125          * @param {string|object} requestUrl The url of the request as a string that will be resolved
18126          * or a parsed URL object.
18127          * @returns {boolean} Whether the request is for the same origin as the application document.
18128          */
18129         function urlIsSameOrigin(requestUrl) {
18130           var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18131           return (parsed.protocol === originUrl.protocol &&
18132                   parsed.host === originUrl.host);
18133         }
18134
18135         /**
18136          * @ngdoc service
18137          * @name $window
18138          *
18139          * @description
18140          * A reference to the browser's `window` object. While `window`
18141          * is globally available in JavaScript, it causes testability problems, because
18142          * it is a global variable. In angular we always refer to it through the
18143          * `$window` service, so it may be overridden, removed or mocked for testing.
18144          *
18145          * Expressions, like the one defined for the `ngClick` directive in the example
18146          * below, are evaluated with respect to the current scope.  Therefore, there is
18147          * no risk of inadvertently coding in a dependency on a global value in such an
18148          * expression.
18149          *
18150          * @example
18151            <example module="windowExample">
18152              <file name="index.html">
18153                <script>
18154                  angular.module('windowExample', [])
18155                    .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18156                      $scope.greeting = 'Hello, World!';
18157                      $scope.doGreeting = function(greeting) {
18158                        $window.alert(greeting);
18159                      };
18160                    }]);
18161                </script>
18162                <div ng-controller="ExampleController">
18163                  <input type="text" ng-model="greeting" aria-label="greeting" />
18164                  <button ng-click="doGreeting(greeting)">ALERT</button>
18165                </div>
18166              </file>
18167              <file name="protractor.js" type="protractor">
18168               it('should display the greeting in the input box', function() {
18169                element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18170                // If we click the button it will block the test runner
18171                // element(':button').click();
18172               });
18173              </file>
18174            </example>
18175          */
18176         function $WindowProvider() {
18177           this.$get = valueFn(window);
18178         }
18179
18180         /**
18181          * @name $$cookieReader
18182          * @requires $document
18183          *
18184          * @description
18185          * This is a private service for reading cookies used by $http and ngCookies
18186          *
18187          * @return {Object} a key/value map of the current cookies
18188          */
18189         function $$CookieReader($document) {
18190           var rawDocument = $document[0] || {};
18191           var lastCookies = {};
18192           var lastCookieString = '';
18193
18194           function safeDecodeURIComponent(str) {
18195             try {
18196               return decodeURIComponent(str);
18197             } catch (e) {
18198               return str;
18199             }
18200           }
18201
18202           return function() {
18203             var cookieArray, cookie, i, index, name;
18204             var currentCookieString = rawDocument.cookie || '';
18205
18206             if (currentCookieString !== lastCookieString) {
18207               lastCookieString = currentCookieString;
18208               cookieArray = lastCookieString.split('; ');
18209               lastCookies = {};
18210
18211               for (i = 0; i < cookieArray.length; i++) {
18212                 cookie = cookieArray[i];
18213                 index = cookie.indexOf('=');
18214                 if (index > 0) { //ignore nameless cookies
18215                   name = safeDecodeURIComponent(cookie.substring(0, index));
18216                   // the first value that is seen for a cookie is the most
18217                   // specific one.  values for the same cookie name that
18218                   // follow are for less specific paths.
18219                   if (isUndefined(lastCookies[name])) {
18220                     lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18221                   }
18222                 }
18223               }
18224             }
18225             return lastCookies;
18226           };
18227         }
18228
18229         $$CookieReader.$inject = ['$document'];
18230
18231         function $$CookieReaderProvider() {
18232           this.$get = $$CookieReader;
18233         }
18234
18235         /* global currencyFilter: true,
18236          dateFilter: true,
18237          filterFilter: true,
18238          jsonFilter: true,
18239          limitToFilter: true,
18240          lowercaseFilter: true,
18241          numberFilter: true,
18242          orderByFilter: true,
18243          uppercaseFilter: true,
18244          */
18245
18246         /**
18247          * @ngdoc provider
18248          * @name $filterProvider
18249          * @description
18250          *
18251          * Filters are just functions which transform input to an output. However filters need to be
18252          * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18253          * annotated with dependencies and is responsible for creating a filter function.
18254          *
18255          * <div class="alert alert-warning">
18256          * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18257          * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18258          * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18259          * (`myapp_subsection_filterx`).
18260          * </div>
18261          *
18262          * ```js
18263          *   // Filter registration
18264          *   function MyModule($provide, $filterProvider) {
18265          *     // create a service to demonstrate injection (not always needed)
18266          *     $provide.value('greet', function(name){
18267          *       return 'Hello ' + name + '!';
18268          *     });
18269          *
18270          *     // register a filter factory which uses the
18271          *     // greet service to demonstrate DI.
18272          *     $filterProvider.register('greet', function(greet){
18273          *       // return the filter function which uses the greet service
18274          *       // to generate salutation
18275          *       return function(text) {
18276          *         // filters need to be forgiving so check input validity
18277          *         return text && greet(text) || text;
18278          *       };
18279          *     });
18280          *   }
18281          * ```
18282          *
18283          * The filter function is registered with the `$injector` under the filter name suffix with
18284          * `Filter`.
18285          *
18286          * ```js
18287          *   it('should be the same instance', inject(
18288          *     function($filterProvider) {
18289          *       $filterProvider.register('reverse', function(){
18290          *         return ...;
18291          *       });
18292          *     },
18293          *     function($filter, reverseFilter) {
18294          *       expect($filter('reverse')).toBe(reverseFilter);
18295          *     });
18296          * ```
18297          *
18298          *
18299          * For more information about how angular filters work, and how to create your own filters, see
18300          * {@link guide/filter Filters} in the Angular Developer Guide.
18301          */
18302
18303         /**
18304          * @ngdoc service
18305          * @name $filter
18306          * @kind function
18307          * @description
18308          * Filters are used for formatting data displayed to the user.
18309          *
18310          * The general syntax in templates is as follows:
18311          *
18312          *         {{ expression [| filter_name[:parameter_value] ... ] }}
18313          *
18314          * @param {String} name Name of the filter function to retrieve
18315          * @return {Function} the filter function
18316          * @example
18317            <example name="$filter" module="filterExample">
18318              <file name="index.html">
18319                <div ng-controller="MainCtrl">
18320                 <h3>{{ originalText }}</h3>
18321                 <h3>{{ filteredText }}</h3>
18322                </div>
18323              </file>
18324
18325              <file name="script.js">
18326               angular.module('filterExample', [])
18327               .controller('MainCtrl', function($scope, $filter) {
18328                 $scope.originalText = 'hello';
18329                 $scope.filteredText = $filter('uppercase')($scope.originalText);
18330               });
18331              </file>
18332            </example>
18333           */
18334         $FilterProvider.$inject = ['$provide'];
18335         function $FilterProvider($provide) {
18336           var suffix = 'Filter';
18337
18338           /**
18339            * @ngdoc method
18340            * @name $filterProvider#register
18341            * @param {string|Object} name Name of the filter function, or an object map of filters where
18342            *    the keys are the filter names and the values are the filter factories.
18343            *
18344            *    <div class="alert alert-warning">
18345            *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18346            *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18347            *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18348            *    (`myapp_subsection_filterx`).
18349            *    </div>
18350             * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18351            * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18352            *    of the registered filter instances.
18353            */
18354           function register(name, factory) {
18355             if (isObject(name)) {
18356               var filters = {};
18357               forEach(name, function(filter, key) {
18358                 filters[key] = register(key, filter);
18359               });
18360               return filters;
18361             } else {
18362               return $provide.factory(name + suffix, factory);
18363             }
18364           }
18365           this.register = register;
18366
18367           this.$get = ['$injector', function($injector) {
18368             return function(name) {
18369               return $injector.get(name + suffix);
18370             };
18371           }];
18372
18373           ////////////////////////////////////////
18374
18375           /* global
18376             currencyFilter: false,
18377             dateFilter: false,
18378             filterFilter: false,
18379             jsonFilter: false,
18380             limitToFilter: false,
18381             lowercaseFilter: false,
18382             numberFilter: false,
18383             orderByFilter: false,
18384             uppercaseFilter: false,
18385           */
18386
18387           register('currency', currencyFilter);
18388           register('date', dateFilter);
18389           register('filter', filterFilter);
18390           register('json', jsonFilter);
18391           register('limitTo', limitToFilter);
18392           register('lowercase', lowercaseFilter);
18393           register('number', numberFilter);
18394           register('orderBy', orderByFilter);
18395           register('uppercase', uppercaseFilter);
18396         }
18397
18398         /**
18399          * @ngdoc filter
18400          * @name filter
18401          * @kind function
18402          *
18403          * @description
18404          * Selects a subset of items from `array` and returns it as a new array.
18405          *
18406          * @param {Array} array The source array.
18407          * @param {string|Object|function()} expression The predicate to be used for selecting items from
18408          *   `array`.
18409          *
18410          *   Can be one of:
18411          *
18412          *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18413          *     objects with string properties in `array` that match this string will be returned. This also
18414          *     applies to nested object properties.
18415          *     The predicate can be negated by prefixing the string with `!`.
18416          *
18417          *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18418          *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18419          *     which have property `name` containing "M" and property `phone` containing "1". A special
18420          *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18421          *     property of the object or its nested object properties. That's equivalent to the simple
18422          *     substring match with a `string` as described above. The predicate can be negated by prefixing
18423          *     the string with `!`.
18424          *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18425          *     not containing "M".
18426          *
18427          *     Note that a named property will match properties on the same level only, while the special
18428          *     `$` property will match properties on the same level or deeper. E.g. an array item like
18429          *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18430          *     **will** be matched by `{$: 'John'}`.
18431          *
18432          *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18433          *     The function is called for each element of the array, with the element, its index, and
18434          *     the entire array itself as arguments.
18435          *
18436          *     The final result is an array of those elements that the predicate returned true for.
18437          *
18438          * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18439          *     determining if the expected value (from the filter expression) and actual value (from
18440          *     the object in the array) should be considered a match.
18441          *
18442          *   Can be one of:
18443          *
18444          *   - `function(actual, expected)`:
18445          *     The function will be given the object value and the predicate value to compare and
18446          *     should return true if both values should be considered equal.
18447          *
18448          *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18449          *     This is essentially strict comparison of expected and actual.
18450          *
18451          *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18452          *     insensitive way.
18453          *
18454          *     Primitive values are converted to strings. Objects are not compared against primitives,
18455          *     unless they have a custom `toString` method (e.g. `Date` objects).
18456          *
18457          * @example
18458            <example>
18459              <file name="index.html">
18460                <div ng-init="friends = [{name:'John', phone:'555-1276'},
18461                                         {name:'Mary', phone:'800-BIG-MARY'},
18462                                         {name:'Mike', phone:'555-4321'},
18463                                         {name:'Adam', phone:'555-5678'},
18464                                         {name:'Julie', phone:'555-8765'},
18465                                         {name:'Juliette', phone:'555-5678'}]"></div>
18466
18467                <label>Search: <input ng-model="searchText"></label>
18468                <table id="searchTextResults">
18469                  <tr><th>Name</th><th>Phone</th></tr>
18470                  <tr ng-repeat="friend in friends | filter:searchText">
18471                    <td>{{friend.name}}</td>
18472                    <td>{{friend.phone}}</td>
18473                  </tr>
18474                </table>
18475                <hr>
18476                <label>Any: <input ng-model="search.$"></label> <br>
18477                <label>Name only <input ng-model="search.name"></label><br>
18478                <label>Phone only <input ng-model="search.phone"></label><br>
18479                <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18480                <table id="searchObjResults">
18481                  <tr><th>Name</th><th>Phone</th></tr>
18482                  <tr ng-repeat="friendObj in friends | filter:search:strict">
18483                    <td>{{friendObj.name}}</td>
18484                    <td>{{friendObj.phone}}</td>
18485                  </tr>
18486                </table>
18487              </file>
18488              <file name="protractor.js" type="protractor">
18489                var expectFriendNames = function(expectedNames, key) {
18490                  element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18491                    arr.forEach(function(wd, i) {
18492                      expect(wd.getText()).toMatch(expectedNames[i]);
18493                    });
18494                  });
18495                };
18496
18497                it('should search across all fields when filtering with a string', function() {
18498                  var searchText = element(by.model('searchText'));
18499                  searchText.clear();
18500                  searchText.sendKeys('m');
18501                  expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18502
18503                  searchText.clear();
18504                  searchText.sendKeys('76');
18505                  expectFriendNames(['John', 'Julie'], 'friend');
18506                });
18507
18508                it('should search in specific fields when filtering with a predicate object', function() {
18509                  var searchAny = element(by.model('search.$'));
18510                  searchAny.clear();
18511                  searchAny.sendKeys('i');
18512                  expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18513                });
18514                it('should use a equal comparison when comparator is true', function() {
18515                  var searchName = element(by.model('search.name'));
18516                  var strict = element(by.model('strict'));
18517                  searchName.clear();
18518                  searchName.sendKeys('Julie');
18519                  strict.click();
18520                  expectFriendNames(['Julie'], 'friendObj');
18521                });
18522              </file>
18523            </example>
18524          */
18525         function filterFilter() {
18526           return function(array, expression, comparator) {
18527             if (!isArrayLike(array)) {
18528               if (array == null) {
18529                 return array;
18530               } else {
18531                 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18532               }
18533             }
18534
18535             var expressionType = getTypeForFilter(expression);
18536             var predicateFn;
18537             var matchAgainstAnyProp;
18538
18539             switch (expressionType) {
18540               case 'function':
18541                 predicateFn = expression;
18542                 break;
18543               case 'boolean':
18544               case 'null':
18545               case 'number':
18546               case 'string':
18547                 matchAgainstAnyProp = true;
18548                 //jshint -W086
18549               case 'object':
18550                 //jshint +W086
18551                 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18552                 break;
18553               default:
18554                 return array;
18555             }
18556
18557             return Array.prototype.filter.call(array, predicateFn);
18558           };
18559         }
18560
18561         // Helper functions for `filterFilter`
18562         function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18563           var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18564           var predicateFn;
18565
18566           if (comparator === true) {
18567             comparator = equals;
18568           } else if (!isFunction(comparator)) {
18569             comparator = function(actual, expected) {
18570               if (isUndefined(actual)) {
18571                 // No substring matching against `undefined`
18572                 return false;
18573               }
18574               if ((actual === null) || (expected === null)) {
18575                 // No substring matching against `null`; only match against `null`
18576                 return actual === expected;
18577               }
18578               if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18579                 // Should not compare primitives against objects, unless they have custom `toString` method
18580                 return false;
18581               }
18582
18583               actual = lowercase('' + actual);
18584               expected = lowercase('' + expected);
18585               return actual.indexOf(expected) !== -1;
18586             };
18587           }
18588
18589           predicateFn = function(item) {
18590             if (shouldMatchPrimitives && !isObject(item)) {
18591               return deepCompare(item, expression.$, comparator, false);
18592             }
18593             return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18594           };
18595
18596           return predicateFn;
18597         }
18598
18599         function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18600           var actualType = getTypeForFilter(actual);
18601           var expectedType = getTypeForFilter(expected);
18602
18603           if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18604             return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18605           } else if (isArray(actual)) {
18606             // In case `actual` is an array, consider it a match
18607             // if ANY of it's items matches `expected`
18608             return actual.some(function(item) {
18609               return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18610             });
18611           }
18612
18613           switch (actualType) {
18614             case 'object':
18615               var key;
18616               if (matchAgainstAnyProp) {
18617                 for (key in actual) {
18618                   if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18619                     return true;
18620                   }
18621                 }
18622                 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18623               } else if (expectedType === 'object') {
18624                 for (key in expected) {
18625                   var expectedVal = expected[key];
18626                   if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18627                     continue;
18628                   }
18629
18630                   var matchAnyProperty = key === '$';
18631                   var actualVal = matchAnyProperty ? actual : actual[key];
18632                   if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18633                     return false;
18634                   }
18635                 }
18636                 return true;
18637               } else {
18638                 return comparator(actual, expected);
18639               }
18640               break;
18641             case 'function':
18642               return false;
18643             default:
18644               return comparator(actual, expected);
18645           }
18646         }
18647
18648         // Used for easily differentiating between `null` and actual `object`
18649         function getTypeForFilter(val) {
18650           return (val === null) ? 'null' : typeof val;
18651         }
18652
18653         /**
18654          * @ngdoc filter
18655          * @name currency
18656          * @kind function
18657          *
18658          * @description
18659          * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18660          * symbol for current locale is used.
18661          *
18662          * @param {number} amount Input to filter.
18663          * @param {string=} symbol Currency symbol or identifier to be displayed.
18664          * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18665          * @returns {string} Formatted number.
18666          *
18667          *
18668          * @example
18669            <example module="currencyExample">
18670              <file name="index.html">
18671                <script>
18672                  angular.module('currencyExample', [])
18673                    .controller('ExampleController', ['$scope', function($scope) {
18674                      $scope.amount = 1234.56;
18675                    }]);
18676                </script>
18677                <div ng-controller="ExampleController">
18678                  <input type="number" ng-model="amount" aria-label="amount"> <br>
18679                  default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18680                  custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18681                  no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18682                </div>
18683              </file>
18684              <file name="protractor.js" type="protractor">
18685                it('should init with 1234.56', function() {
18686                  expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18687                  expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18688                  expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18689                });
18690                it('should update', function() {
18691                  if (browser.params.browser == 'safari') {
18692                    // Safari does not understand the minus key. See
18693                    // https://github.com/angular/protractor/issues/481
18694                    return;
18695                  }
18696                  element(by.model('amount')).clear();
18697                  element(by.model('amount')).sendKeys('-1234');
18698                  expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18699                  expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18700                  expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18701                });
18702              </file>
18703            </example>
18704          */
18705         currencyFilter.$inject = ['$locale'];
18706         function currencyFilter($locale) {
18707           var formats = $locale.NUMBER_FORMATS;
18708           return function(amount, currencySymbol, fractionSize) {
18709             if (isUndefined(currencySymbol)) {
18710               currencySymbol = formats.CURRENCY_SYM;
18711             }
18712
18713             if (isUndefined(fractionSize)) {
18714               fractionSize = formats.PATTERNS[1].maxFrac;
18715             }
18716
18717             // if null or undefined pass it through
18718             return (amount == null)
18719                 ? amount
18720                 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18721                     replace(/\u00A4/g, currencySymbol);
18722           };
18723         }
18724
18725         /**
18726          * @ngdoc filter
18727          * @name number
18728          * @kind function
18729          *
18730          * @description
18731          * Formats a number as text.
18732          *
18733          * If the input is null or undefined, it will just be returned.
18734          * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18735          * If the input is not a number an empty string is returned.
18736          *
18737          *
18738          * @param {number|string} number Number to format.
18739          * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18740          * If this is not provided then the fraction size is computed from the current locale's number
18741          * formatting pattern. In the case of the default locale, it will be 3.
18742          * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18743          *
18744          * @example
18745            <example module="numberFilterExample">
18746              <file name="index.html">
18747                <script>
18748                  angular.module('numberFilterExample', [])
18749                    .controller('ExampleController', ['$scope', function($scope) {
18750                      $scope.val = 1234.56789;
18751                    }]);
18752                </script>
18753                <div ng-controller="ExampleController">
18754                  <label>Enter number: <input ng-model='val'></label><br>
18755                  Default formatting: <span id='number-default'>{{val | number}}</span><br>
18756                  No fractions: <span>{{val | number:0}}</span><br>
18757                  Negative number: <span>{{-val | number:4}}</span>
18758                </div>
18759              </file>
18760              <file name="protractor.js" type="protractor">
18761                it('should format numbers', function() {
18762                  expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18763                  expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18764                  expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18765                });
18766
18767                it('should update', function() {
18768                  element(by.model('val')).clear();
18769                  element(by.model('val')).sendKeys('3374.333');
18770                  expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18771                  expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18772                  expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18773               });
18774              </file>
18775            </example>
18776          */
18777
18778
18779         numberFilter.$inject = ['$locale'];
18780         function numberFilter($locale) {
18781           var formats = $locale.NUMBER_FORMATS;
18782           return function(number, fractionSize) {
18783
18784             // if null or undefined pass it through
18785             return (number == null)
18786                 ? number
18787                 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18788                                fractionSize);
18789           };
18790         }
18791
18792         var DECIMAL_SEP = '.';
18793         function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18794           if (isObject(number)) return '';
18795
18796           var isNegative = number < 0;
18797           number = Math.abs(number);
18798
18799           var isInfinity = number === Infinity;
18800           if (!isInfinity && !isFinite(number)) return '';
18801
18802           var numStr = number + '',
18803               formatedText = '',
18804               hasExponent = false,
18805               parts = [];
18806
18807           if (isInfinity) formatedText = '\u221e';
18808
18809           if (!isInfinity && numStr.indexOf('e') !== -1) {
18810             var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18811             if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18812               number = 0;
18813             } else {
18814               formatedText = numStr;
18815               hasExponent = true;
18816             }
18817           }
18818
18819           if (!isInfinity && !hasExponent) {
18820             var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18821
18822             // determine fractionSize if it is not specified
18823             if (isUndefined(fractionSize)) {
18824               fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18825             }
18826
18827             // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18828             // inspired by:
18829             // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18830             number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18831
18832             var fraction = ('' + number).split(DECIMAL_SEP);
18833             var whole = fraction[0];
18834             fraction = fraction[1] || '';
18835
18836             var i, pos = 0,
18837                 lgroup = pattern.lgSize,
18838                 group = pattern.gSize;
18839
18840             if (whole.length >= (lgroup + group)) {
18841               pos = whole.length - lgroup;
18842               for (i = 0; i < pos; i++) {
18843                 if ((pos - i) % group === 0 && i !== 0) {
18844                   formatedText += groupSep;
18845                 }
18846                 formatedText += whole.charAt(i);
18847               }
18848             }
18849
18850             for (i = pos; i < whole.length; i++) {
18851               if ((whole.length - i) % lgroup === 0 && i !== 0) {
18852                 formatedText += groupSep;
18853               }
18854               formatedText += whole.charAt(i);
18855             }
18856
18857             // format fraction part.
18858             while (fraction.length < fractionSize) {
18859               fraction += '0';
18860             }
18861
18862             if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18863           } else {
18864             if (fractionSize > 0 && number < 1) {
18865               formatedText = number.toFixed(fractionSize);
18866               number = parseFloat(formatedText);
18867               formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18868             }
18869           }
18870
18871           if (number === 0) {
18872             isNegative = false;
18873           }
18874
18875           parts.push(isNegative ? pattern.negPre : pattern.posPre,
18876                      formatedText,
18877                      isNegative ? pattern.negSuf : pattern.posSuf);
18878           return parts.join('');
18879         }
18880
18881         function padNumber(num, digits, trim) {
18882           var neg = '';
18883           if (num < 0) {
18884             neg =  '-';
18885             num = -num;
18886           }
18887           num = '' + num;
18888           while (num.length < digits) num = '0' + num;
18889           if (trim) {
18890             num = num.substr(num.length - digits);
18891           }
18892           return neg + num;
18893         }
18894
18895
18896         function dateGetter(name, size, offset, trim) {
18897           offset = offset || 0;
18898           return function(date) {
18899             var value = date['get' + name]();
18900             if (offset > 0 || value > -offset) {
18901               value += offset;
18902             }
18903             if (value === 0 && offset == -12) value = 12;
18904             return padNumber(value, size, trim);
18905           };
18906         }
18907
18908         function dateStrGetter(name, shortForm) {
18909           return function(date, formats) {
18910             var value = date['get' + name]();
18911             var get = uppercase(shortForm ? ('SHORT' + name) : name);
18912
18913             return formats[get][value];
18914           };
18915         }
18916
18917         function timeZoneGetter(date, formats, offset) {
18918           var zone = -1 * offset;
18919           var paddedZone = (zone >= 0) ? "+" : "";
18920
18921           paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18922                         padNumber(Math.abs(zone % 60), 2);
18923
18924           return paddedZone;
18925         }
18926
18927         function getFirstThursdayOfYear(year) {
18928             // 0 = index of January
18929             var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18930             // 4 = index of Thursday (+1 to account for 1st = 5)
18931             // 11 = index of *next* Thursday (+1 account for 1st = 12)
18932             return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18933         }
18934
18935         function getThursdayThisWeek(datetime) {
18936             return new Date(datetime.getFullYear(), datetime.getMonth(),
18937               // 4 = index of Thursday
18938               datetime.getDate() + (4 - datetime.getDay()));
18939         }
18940
18941         function weekGetter(size) {
18942            return function(date) {
18943               var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18944                  thisThurs = getThursdayThisWeek(date);
18945
18946               var diff = +thisThurs - +firstThurs,
18947                  result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18948
18949               return padNumber(result, size);
18950            };
18951         }
18952
18953         function ampmGetter(date, formats) {
18954           return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18955         }
18956
18957         function eraGetter(date, formats) {
18958           return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18959         }
18960
18961         function longEraGetter(date, formats) {
18962           return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18963         }
18964
18965         var DATE_FORMATS = {
18966           yyyy: dateGetter('FullYear', 4),
18967             yy: dateGetter('FullYear', 2, 0, true),
18968              y: dateGetter('FullYear', 1),
18969           MMMM: dateStrGetter('Month'),
18970            MMM: dateStrGetter('Month', true),
18971             MM: dateGetter('Month', 2, 1),
18972              M: dateGetter('Month', 1, 1),
18973             dd: dateGetter('Date', 2),
18974              d: dateGetter('Date', 1),
18975             HH: dateGetter('Hours', 2),
18976              H: dateGetter('Hours', 1),
18977             hh: dateGetter('Hours', 2, -12),
18978              h: dateGetter('Hours', 1, -12),
18979             mm: dateGetter('Minutes', 2),
18980              m: dateGetter('Minutes', 1),
18981             ss: dateGetter('Seconds', 2),
18982              s: dateGetter('Seconds', 1),
18983              // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18984              // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18985            sss: dateGetter('Milliseconds', 3),
18986           EEEE: dateStrGetter('Day'),
18987            EEE: dateStrGetter('Day', true),
18988              a: ampmGetter,
18989              Z: timeZoneGetter,
18990             ww: weekGetter(2),
18991              w: weekGetter(1),
18992              G: eraGetter,
18993              GG: eraGetter,
18994              GGG: eraGetter,
18995              GGGG: longEraGetter
18996         };
18997
18998         var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18999             NUMBER_STRING = /^\-?\d+$/;
19000
19001         /**
19002          * @ngdoc filter
19003          * @name date
19004          * @kind function
19005          *
19006          * @description
19007          *   Formats `date` to a string based on the requested `format`.
19008          *
19009          *   `format` string can be composed of the following elements:
19010          *
19011          *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19012          *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19013          *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19014          *   * `'MMMM'`: Month in year (January-December)
19015          *   * `'MMM'`: Month in year (Jan-Dec)
19016          *   * `'MM'`: Month in year, padded (01-12)
19017          *   * `'M'`: Month in year (1-12)
19018          *   * `'dd'`: Day in month, padded (01-31)
19019          *   * `'d'`: Day in month (1-31)
19020          *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19021          *   * `'EEE'`: Day in Week, (Sun-Sat)
19022          *   * `'HH'`: Hour in day, padded (00-23)
19023          *   * `'H'`: Hour in day (0-23)
19024          *   * `'hh'`: Hour in AM/PM, padded (01-12)
19025          *   * `'h'`: Hour in AM/PM, (1-12)
19026          *   * `'mm'`: Minute in hour, padded (00-59)
19027          *   * `'m'`: Minute in hour (0-59)
19028          *   * `'ss'`: Second in minute, padded (00-59)
19029          *   * `'s'`: Second in minute (0-59)
19030          *   * `'sss'`: Millisecond in second, padded (000-999)
19031          *   * `'a'`: AM/PM marker
19032          *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19033          *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19034          *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19035          *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19036          *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19037          *
19038          *   `format` string can also be one of the following predefined
19039          *   {@link guide/i18n localizable formats}:
19040          *
19041          *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19042          *     (e.g. Sep 3, 2010 12:05:08 PM)
19043          *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19044          *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19045          *     (e.g. Friday, September 3, 2010)
19046          *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19047          *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19048          *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19049          *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19050          *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19051          *
19052          *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19053          *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19054          *   (e.g. `"h 'o''clock'"`).
19055          *
19056          * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19057          *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19058          *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19059          *    specified in the string input, the time is considered to be in the local timezone.
19060          * @param {string=} format Formatting rules (see Description). If not specified,
19061          *    `mediumDate` is used.
19062          * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19063          *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19064          *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19065          *    If not specified, the timezone of the browser will be used.
19066          * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19067          *
19068          * @example
19069            <example>
19070              <file name="index.html">
19071                <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19072                    <span>{{1288323623006 | date:'medium'}}</span><br>
19073                <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19074                   <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19075                <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19076                   <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19077                <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19078                   <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19079              </file>
19080              <file name="protractor.js" type="protractor">
19081                it('should format date', function() {
19082                  expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19083                     toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19084                  expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19085                     toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19086                  expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19087                     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19088                  expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19089                     toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19090                });
19091              </file>
19092            </example>
19093          */
19094         dateFilter.$inject = ['$locale'];
19095         function dateFilter($locale) {
19096
19097
19098           var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19099                              // 1        2       3         4          5          6          7          8  9     10      11
19100           function jsonStringToDate(string) {
19101             var match;
19102             if (match = string.match(R_ISO8601_STR)) {
19103               var date = new Date(0),
19104                   tzHour = 0,
19105                   tzMin  = 0,
19106                   dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19107                   timeSetter = match[8] ? date.setUTCHours : date.setHours;
19108
19109               if (match[9]) {
19110                 tzHour = toInt(match[9] + match[10]);
19111                 tzMin = toInt(match[9] + match[11]);
19112               }
19113               dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19114               var h = toInt(match[4] || 0) - tzHour;
19115               var m = toInt(match[5] || 0) - tzMin;
19116               var s = toInt(match[6] || 0);
19117               var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19118               timeSetter.call(date, h, m, s, ms);
19119               return date;
19120             }
19121             return string;
19122           }
19123
19124
19125           return function(date, format, timezone) {
19126             var text = '',
19127                 parts = [],
19128                 fn, match;
19129
19130             format = format || 'mediumDate';
19131             format = $locale.DATETIME_FORMATS[format] || format;
19132             if (isString(date)) {
19133               date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19134             }
19135
19136             if (isNumber(date)) {
19137               date = new Date(date);
19138             }
19139
19140             if (!isDate(date) || !isFinite(date.getTime())) {
19141               return date;
19142             }
19143
19144             while (format) {
19145               match = DATE_FORMATS_SPLIT.exec(format);
19146               if (match) {
19147                 parts = concat(parts, match, 1);
19148                 format = parts.pop();
19149               } else {
19150                 parts.push(format);
19151                 format = null;
19152               }
19153             }
19154
19155             var dateTimezoneOffset = date.getTimezoneOffset();
19156             if (timezone) {
19157               dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19158               date = convertTimezoneToLocal(date, timezone, true);
19159             }
19160             forEach(parts, function(value) {
19161               fn = DATE_FORMATS[value];
19162               text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19163                          : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19164             });
19165
19166             return text;
19167           };
19168         }
19169
19170
19171         /**
19172          * @ngdoc filter
19173          * @name json
19174          * @kind function
19175          *
19176          * @description
19177          *   Allows you to convert a JavaScript object into JSON string.
19178          *
19179          *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19180          *   the binding is automatically converted to JSON.
19181          *
19182          * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19183          * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19184          * @returns {string} JSON string.
19185          *
19186          *
19187          * @example
19188            <example>
19189              <file name="index.html">
19190                <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19191                <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19192              </file>
19193              <file name="protractor.js" type="protractor">
19194                it('should jsonify filtered objects', function() {
19195                  expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19196                  expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19197                });
19198              </file>
19199            </example>
19200          *
19201          */
19202         function jsonFilter() {
19203           return function(object, spacing) {
19204             if (isUndefined(spacing)) {
19205                 spacing = 2;
19206             }
19207             return toJson(object, spacing);
19208           };
19209         }
19210
19211
19212         /**
19213          * @ngdoc filter
19214          * @name lowercase
19215          * @kind function
19216          * @description
19217          * Converts string to lowercase.
19218          * @see angular.lowercase
19219          */
19220         var lowercaseFilter = valueFn(lowercase);
19221
19222
19223         /**
19224          * @ngdoc filter
19225          * @name uppercase
19226          * @kind function
19227          * @description
19228          * Converts string to uppercase.
19229          * @see angular.uppercase
19230          */
19231         var uppercaseFilter = valueFn(uppercase);
19232
19233         /**
19234          * @ngdoc filter
19235          * @name limitTo
19236          * @kind function
19237          *
19238          * @description
19239          * Creates a new array or string containing only a specified number of elements. The elements
19240          * are taken from either the beginning or the end of the source array, string or number, as specified by
19241          * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19242          * converted to a string.
19243          *
19244          * @param {Array|string|number} input Source array, string or number to be limited.
19245          * @param {string|number} limit The length of the returned array or string. If the `limit` number
19246          *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19247          *     If the number is negative, `limit` number  of items from the end of the source array/string
19248          *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19249          *     the input will be returned unchanged.
19250          * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19251          *     indicates an offset from the end of `input`. Defaults to `0`.
19252          * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19253          *     had less than `limit` elements.
19254          *
19255          * @example
19256            <example module="limitToExample">
19257              <file name="index.html">
19258                <script>
19259                  angular.module('limitToExample', [])
19260                    .controller('ExampleController', ['$scope', function($scope) {
19261                      $scope.numbers = [1,2,3,4,5,6,7,8,9];
19262                      $scope.letters = "abcdefghi";
19263                      $scope.longNumber = 2345432342;
19264                      $scope.numLimit = 3;
19265                      $scope.letterLimit = 3;
19266                      $scope.longNumberLimit = 3;
19267                    }]);
19268                </script>
19269                <div ng-controller="ExampleController">
19270                  <label>
19271                     Limit {{numbers}} to:
19272                     <input type="number" step="1" ng-model="numLimit">
19273                  </label>
19274                  <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19275                  <label>
19276                     Limit {{letters}} to:
19277                     <input type="number" step="1" ng-model="letterLimit">
19278                  </label>
19279                  <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19280                  <label>
19281                     Limit {{longNumber}} to:
19282                     <input type="number" step="1" ng-model="longNumberLimit">
19283                  </label>
19284                  <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19285                </div>
19286              </file>
19287              <file name="protractor.js" type="protractor">
19288                var numLimitInput = element(by.model('numLimit'));
19289                var letterLimitInput = element(by.model('letterLimit'));
19290                var longNumberLimitInput = element(by.model('longNumberLimit'));
19291                var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19292                var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19293                var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19294
19295                it('should limit the number array to first three items', function() {
19296                  expect(numLimitInput.getAttribute('value')).toBe('3');
19297                  expect(letterLimitInput.getAttribute('value')).toBe('3');
19298                  expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19299                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19300                  expect(limitedLetters.getText()).toEqual('Output letters: abc');
19301                  expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19302                });
19303
19304                // There is a bug in safari and protractor that doesn't like the minus key
19305                // it('should update the output when -3 is entered', function() {
19306                //   numLimitInput.clear();
19307                //   numLimitInput.sendKeys('-3');
19308                //   letterLimitInput.clear();
19309                //   letterLimitInput.sendKeys('-3');
19310                //   longNumberLimitInput.clear();
19311                //   longNumberLimitInput.sendKeys('-3');
19312                //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19313                //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19314                //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19315                // });
19316
19317                it('should not exceed the maximum size of input array', function() {
19318                  numLimitInput.clear();
19319                  numLimitInput.sendKeys('100');
19320                  letterLimitInput.clear();
19321                  letterLimitInput.sendKeys('100');
19322                  longNumberLimitInput.clear();
19323                  longNumberLimitInput.sendKeys('100');
19324                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19325                  expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19326                  expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19327                });
19328              </file>
19329            </example>
19330         */
19331         function limitToFilter() {
19332           return function(input, limit, begin) {
19333             if (Math.abs(Number(limit)) === Infinity) {
19334               limit = Number(limit);
19335             } else {
19336               limit = toInt(limit);
19337             }
19338             if (isNaN(limit)) return input;
19339
19340             if (isNumber(input)) input = input.toString();
19341             if (!isArray(input) && !isString(input)) return input;
19342
19343             begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19344             begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19345
19346             if (limit >= 0) {
19347               return input.slice(begin, begin + limit);
19348             } else {
19349               if (begin === 0) {
19350                 return input.slice(limit, input.length);
19351               } else {
19352                 return input.slice(Math.max(0, begin + limit), begin);
19353               }
19354             }
19355           };
19356         }
19357
19358         /**
19359          * @ngdoc filter
19360          * @name orderBy
19361          * @kind function
19362          *
19363          * @description
19364          * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19365          * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19366          * as expected, make sure they are actually being saved as numbers and not strings.
19367          *
19368          * @param {Array} array The array to sort.
19369          * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19370          *    used by the comparator to determine the order of elements.
19371          *
19372          *    Can be one of:
19373          *
19374          *    - `function`: Getter function. The result of this function will be sorted using the
19375          *      `<`, `===`, `>` operator.
19376          *    - `string`: An Angular expression. The result of this expression is used to compare elements
19377          *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19378          *      3 first characters of a property called `name`). The result of a constant expression
19379          *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19380          *      to sort object by the value of their `special name` property). An expression can be
19381          *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19382          *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19383          *      element itself is used to compare where sorting.
19384          *    - `Array`: An array of function or string predicates. The first predicate in the array
19385          *      is used for sorting, but when two items are equivalent, the next predicate is used.
19386          *
19387          *    If the predicate is missing or empty then it defaults to `'+'`.
19388          *
19389          * @param {boolean=} reverse Reverse the order of the array.
19390          * @returns {Array} Sorted copy of the source array.
19391          *
19392          *
19393          * @example
19394          * The example below demonstrates a simple ngRepeat, where the data is sorted
19395          * by age in descending order (predicate is set to `'-age'`).
19396          * `reverse` is not set, which means it defaults to `false`.
19397            <example module="orderByExample">
19398              <file name="index.html">
19399                <script>
19400                  angular.module('orderByExample', [])
19401                    .controller('ExampleController', ['$scope', function($scope) {
19402                      $scope.friends =
19403                          [{name:'John', phone:'555-1212', age:10},
19404                           {name:'Mary', phone:'555-9876', age:19},
19405                           {name:'Mike', phone:'555-4321', age:21},
19406                           {name:'Adam', phone:'555-5678', age:35},
19407                           {name:'Julie', phone:'555-8765', age:29}];
19408                    }]);
19409                </script>
19410                <div ng-controller="ExampleController">
19411                  <table class="friend">
19412                    <tr>
19413                      <th>Name</th>
19414                      <th>Phone Number</th>
19415                      <th>Age</th>
19416                    </tr>
19417                    <tr ng-repeat="friend in friends | orderBy:'-age'">
19418                      <td>{{friend.name}}</td>
19419                      <td>{{friend.phone}}</td>
19420                      <td>{{friend.age}}</td>
19421                    </tr>
19422                  </table>
19423                </div>
19424              </file>
19425            </example>
19426          *
19427          * The predicate and reverse parameters can be controlled dynamically through scope properties,
19428          * as shown in the next example.
19429          * @example
19430            <example module="orderByExample">
19431              <file name="index.html">
19432                <script>
19433                  angular.module('orderByExample', [])
19434                    .controller('ExampleController', ['$scope', function($scope) {
19435                      $scope.friends =
19436                          [{name:'John', phone:'555-1212', age:10},
19437                           {name:'Mary', phone:'555-9876', age:19},
19438                           {name:'Mike', phone:'555-4321', age:21},
19439                           {name:'Adam', phone:'555-5678', age:35},
19440                           {name:'Julie', phone:'555-8765', age:29}];
19441                      $scope.predicate = 'age';
19442                      $scope.reverse = true;
19443                      $scope.order = function(predicate) {
19444                        $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19445                        $scope.predicate = predicate;
19446                      };
19447                    }]);
19448                </script>
19449                <style type="text/css">
19450                  .sortorder:after {
19451                    content: '\25b2';
19452                  }
19453                  .sortorder.reverse:after {
19454                    content: '\25bc';
19455                  }
19456                </style>
19457                <div ng-controller="ExampleController">
19458                  <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19459                  <hr/>
19460                  [ <a href="" ng-click="predicate=''">unsorted</a> ]
19461                  <table class="friend">
19462                    <tr>
19463                      <th>
19464                        <a href="" ng-click="order('name')">Name</a>
19465                        <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19466                      </th>
19467                      <th>
19468                        <a href="" ng-click="order('phone')">Phone Number</a>
19469                        <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19470                      </th>
19471                      <th>
19472                        <a href="" ng-click="order('age')">Age</a>
19473                        <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19474                      </th>
19475                    </tr>
19476                    <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19477                      <td>{{friend.name}}</td>
19478                      <td>{{friend.phone}}</td>
19479                      <td>{{friend.age}}</td>
19480                    </tr>
19481                  </table>
19482                </div>
19483              </file>
19484            </example>
19485          *
19486          * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19487          * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19488          * desired parameters.
19489          *
19490          * Example:
19491          *
19492          * @example
19493           <example module="orderByExample">
19494             <file name="index.html">
19495               <div ng-controller="ExampleController">
19496                 <table class="friend">
19497                   <tr>
19498                     <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19499                       (<a href="" ng-click="order('-name',false)">^</a>)</th>
19500                     <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19501                     <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19502                   </tr>
19503                   <tr ng-repeat="friend in friends">
19504                     <td>{{friend.name}}</td>
19505                     <td>{{friend.phone}}</td>
19506                     <td>{{friend.age}}</td>
19507                   </tr>
19508                 </table>
19509               </div>
19510             </file>
19511
19512             <file name="script.js">
19513               angular.module('orderByExample', [])
19514                 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19515                   var orderBy = $filter('orderBy');
19516                   $scope.friends = [
19517                     { name: 'John',    phone: '555-1212',    age: 10 },
19518                     { name: 'Mary',    phone: '555-9876',    age: 19 },
19519                     { name: 'Mike',    phone: '555-4321',    age: 21 },
19520                     { name: 'Adam',    phone: '555-5678',    age: 35 },
19521                     { name: 'Julie',   phone: '555-8765',    age: 29 }
19522                   ];
19523                   $scope.order = function(predicate, reverse) {
19524                     $scope.friends = orderBy($scope.friends, predicate, reverse);
19525                   };
19526                   $scope.order('-age',false);
19527                 }]);
19528             </file>
19529         </example>
19530          */
19531         orderByFilter.$inject = ['$parse'];
19532         function orderByFilter($parse) {
19533           return function(array, sortPredicate, reverseOrder) {
19534
19535             if (!(isArrayLike(array))) return array;
19536
19537             if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19538             if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19539
19540             var predicates = processPredicates(sortPredicate, reverseOrder);
19541             // Add a predicate at the end that evaluates to the element index. This makes the
19542             // sort stable as it works as a tie-breaker when all the input predicates cannot
19543             // distinguish between two elements.
19544             predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19545
19546             // The next three lines are a version of a Swartzian Transform idiom from Perl
19547             // (sometimes called the Decorate-Sort-Undecorate idiom)
19548             // See https://en.wikipedia.org/wiki/Schwartzian_transform
19549             var compareValues = Array.prototype.map.call(array, getComparisonObject);
19550             compareValues.sort(doComparison);
19551             array = compareValues.map(function(item) { return item.value; });
19552
19553             return array;
19554
19555             function getComparisonObject(value, index) {
19556               return {
19557                 value: value,
19558                 predicateValues: predicates.map(function(predicate) {
19559                   return getPredicateValue(predicate.get(value), index);
19560                 })
19561               };
19562             }
19563
19564             function doComparison(v1, v2) {
19565               var result = 0;
19566               for (var index=0, length = predicates.length; index < length; ++index) {
19567                 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19568                 if (result) break;
19569               }
19570               return result;
19571             }
19572           };
19573
19574           function processPredicates(sortPredicate, reverseOrder) {
19575             reverseOrder = reverseOrder ? -1 : 1;
19576             return sortPredicate.map(function(predicate) {
19577               var descending = 1, get = identity;
19578
19579               if (isFunction(predicate)) {
19580                 get = predicate;
19581               } else if (isString(predicate)) {
19582                 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19583                   descending = predicate.charAt(0) == '-' ? -1 : 1;
19584                   predicate = predicate.substring(1);
19585                 }
19586                 if (predicate !== '') {
19587                   get = $parse(predicate);
19588                   if (get.constant) {
19589                     var key = get();
19590                     get = function(value) { return value[key]; };
19591                   }
19592                 }
19593               }
19594               return { get: get, descending: descending * reverseOrder };
19595             });
19596           }
19597
19598           function isPrimitive(value) {
19599             switch (typeof value) {
19600               case 'number': /* falls through */
19601               case 'boolean': /* falls through */
19602               case 'string':
19603                 return true;
19604               default:
19605                 return false;
19606             }
19607           }
19608
19609           function objectValue(value, index) {
19610             // If `valueOf` is a valid function use that
19611             if (typeof value.valueOf === 'function') {
19612               value = value.valueOf();
19613               if (isPrimitive(value)) return value;
19614             }
19615             // If `toString` is a valid function and not the one from `Object.prototype` use that
19616             if (hasCustomToString(value)) {
19617               value = value.toString();
19618               if (isPrimitive(value)) return value;
19619             }
19620             // We have a basic object so we use the position of the object in the collection
19621             return index;
19622           }
19623
19624           function getPredicateValue(value, index) {
19625             var type = typeof value;
19626             if (value === null) {
19627               type = 'string';
19628               value = 'null';
19629             } else if (type === 'string') {
19630               value = value.toLowerCase();
19631             } else if (type === 'object') {
19632               value = objectValue(value, index);
19633             }
19634             return { value: value, type: type };
19635           }
19636
19637           function compare(v1, v2) {
19638             var result = 0;
19639             if (v1.type === v2.type) {
19640               if (v1.value !== v2.value) {
19641                 result = v1.value < v2.value ? -1 : 1;
19642               }
19643             } else {
19644               result = v1.type < v2.type ? -1 : 1;
19645             }
19646             return result;
19647           }
19648         }
19649
19650         function ngDirective(directive) {
19651           if (isFunction(directive)) {
19652             directive = {
19653               link: directive
19654             };
19655           }
19656           directive.restrict = directive.restrict || 'AC';
19657           return valueFn(directive);
19658         }
19659
19660         /**
19661          * @ngdoc directive
19662          * @name a
19663          * @restrict E
19664          *
19665          * @description
19666          * Modifies the default behavior of the html A tag so that the default action is prevented when
19667          * the href attribute is empty.
19668          *
19669          * This change permits the easy creation of action links with the `ngClick` directive
19670          * without changing the location or causing page reloads, e.g.:
19671          * `<a href="" ng-click="list.addItem()">Add Item</a>`
19672          */
19673         var htmlAnchorDirective = valueFn({
19674           restrict: 'E',
19675           compile: function(element, attr) {
19676             if (!attr.href && !attr.xlinkHref) {
19677               return function(scope, element) {
19678                 // If the linked element is not an anchor tag anymore, do nothing
19679                 if (element[0].nodeName.toLowerCase() !== 'a') return;
19680
19681                 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19682                 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19683                            'xlink:href' : 'href';
19684                 element.on('click', function(event) {
19685                   // if we have no href url, then don't navigate anywhere.
19686                   if (!element.attr(href)) {
19687                     event.preventDefault();
19688                   }
19689                 });
19690               };
19691             }
19692           }
19693         });
19694
19695         /**
19696          * @ngdoc directive
19697          * @name ngHref
19698          * @restrict A
19699          * @priority 99
19700          *
19701          * @description
19702          * Using Angular markup like `{{hash}}` in an href attribute will
19703          * make the link go to the wrong URL if the user clicks it before
19704          * Angular has a chance to replace the `{{hash}}` markup with its
19705          * value. Until Angular replaces the markup the link will be broken
19706          * and will most likely return a 404 error. The `ngHref` directive
19707          * solves this problem.
19708          *
19709          * The wrong way to write it:
19710          * ```html
19711          * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19712          * ```
19713          *
19714          * The correct way to write it:
19715          * ```html
19716          * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19717          * ```
19718          *
19719          * @element A
19720          * @param {template} ngHref any string which can contain `{{}}` markup.
19721          *
19722          * @example
19723          * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19724          * in links and their different behaviors:
19725             <example>
19726               <file name="index.html">
19727                 <input ng-model="value" /><br />
19728                 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19729                 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19730                 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19731                 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19732                 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19733                 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19734               </file>
19735               <file name="protractor.js" type="protractor">
19736                 it('should execute ng-click but not reload when href without value', function() {
19737                   element(by.id('link-1')).click();
19738                   expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19739                   expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19740                 });
19741
19742                 it('should execute ng-click but not reload when href empty string', function() {
19743                   element(by.id('link-2')).click();
19744                   expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19745                   expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19746                 });
19747
19748                 it('should execute ng-click and change url when ng-href specified', function() {
19749                   expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19750
19751                   element(by.id('link-3')).click();
19752
19753                   // At this point, we navigate away from an Angular page, so we need
19754                   // to use browser.driver to get the base webdriver.
19755
19756                   browser.wait(function() {
19757                     return browser.driver.getCurrentUrl().then(function(url) {
19758                       return url.match(/\/123$/);
19759                     });
19760                   }, 5000, 'page should navigate to /123');
19761                 });
19762
19763                 it('should execute ng-click but not reload when href empty string and name specified', function() {
19764                   element(by.id('link-4')).click();
19765                   expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19766                   expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19767                 });
19768
19769                 it('should execute ng-click but not reload when no href but name specified', function() {
19770                   element(by.id('link-5')).click();
19771                   expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19772                   expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19773                 });
19774
19775                 it('should only change url when only ng-href', function() {
19776                   element(by.model('value')).clear();
19777                   element(by.model('value')).sendKeys('6');
19778                   expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19779
19780                   element(by.id('link-6')).click();
19781
19782                   // At this point, we navigate away from an Angular page, so we need
19783                   // to use browser.driver to get the base webdriver.
19784                   browser.wait(function() {
19785                     return browser.driver.getCurrentUrl().then(function(url) {
19786                       return url.match(/\/6$/);
19787                     });
19788                   }, 5000, 'page should navigate to /6');
19789                 });
19790               </file>
19791             </example>
19792          */
19793
19794         /**
19795          * @ngdoc directive
19796          * @name ngSrc
19797          * @restrict A
19798          * @priority 99
19799          *
19800          * @description
19801          * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19802          * work right: The browser will fetch from the URL with the literal
19803          * text `{{hash}}` until Angular replaces the expression inside
19804          * `{{hash}}`. The `ngSrc` directive solves this problem.
19805          *
19806          * The buggy way to write it:
19807          * ```html
19808          * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19809          * ```
19810          *
19811          * The correct way to write it:
19812          * ```html
19813          * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19814          * ```
19815          *
19816          * @element IMG
19817          * @param {template} ngSrc any string which can contain `{{}}` markup.
19818          */
19819
19820         /**
19821          * @ngdoc directive
19822          * @name ngSrcset
19823          * @restrict A
19824          * @priority 99
19825          *
19826          * @description
19827          * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19828          * work right: The browser will fetch from the URL with the literal
19829          * text `{{hash}}` until Angular replaces the expression inside
19830          * `{{hash}}`. The `ngSrcset` directive solves this problem.
19831          *
19832          * The buggy way to write it:
19833          * ```html
19834          * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19835          * ```
19836          *
19837          * The correct way to write it:
19838          * ```html
19839          * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19840          * ```
19841          *
19842          * @element IMG
19843          * @param {template} ngSrcset any string which can contain `{{}}` markup.
19844          */
19845
19846         /**
19847          * @ngdoc directive
19848          * @name ngDisabled
19849          * @restrict A
19850          * @priority 100
19851          *
19852          * @description
19853          *
19854          * This directive sets the `disabled` attribute on the element if the
19855          * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19856          *
19857          * A special directive is necessary because we cannot use interpolation inside the `disabled`
19858          * attribute.  The following example would make the button enabled on Chrome/Firefox
19859          * but not on older IEs:
19860          *
19861          * ```html
19862          * <!-- See below for an example of ng-disabled being used correctly -->
19863          * <div ng-init="isDisabled = false">
19864          *  <button disabled="{{isDisabled}}">Disabled</button>
19865          * </div>
19866          * ```
19867          *
19868          * This is because the HTML specification does not require browsers to preserve the values of
19869          * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19870          * If we put an Angular interpolation expression into such an attribute then the
19871          * binding information would be lost when the browser removes the attribute.
19872          *
19873          * @example
19874             <example>
19875               <file name="index.html">
19876                 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19877                 <button ng-model="button" ng-disabled="checked">Button</button>
19878               </file>
19879               <file name="protractor.js" type="protractor">
19880                 it('should toggle button', function() {
19881                   expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19882                   element(by.model('checked')).click();
19883                   expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19884                 });
19885               </file>
19886             </example>
19887          *
19888          * @element INPUT
19889          * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19890          *     then the `disabled` attribute will be set on the element
19891          */
19892
19893
19894         /**
19895          * @ngdoc directive
19896          * @name ngChecked
19897          * @restrict A
19898          * @priority 100
19899          *
19900          * @description
19901          * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19902          *
19903          * Note that this directive should not be used together with {@link ngModel `ngModel`},
19904          * as this can lead to unexpected behavior.
19905          *
19906          * ### Why do we need `ngChecked`?
19907          *
19908          * The HTML specification does not require browsers to preserve the values of boolean attributes
19909          * such as checked. (Their presence means true and their absence means false.)
19910          * If we put an Angular interpolation expression into such an attribute then the
19911          * binding information would be lost when the browser removes the attribute.
19912          * The `ngChecked` directive solves this problem for the `checked` attribute.
19913          * This complementary directive is not removed by the browser and so provides
19914          * a permanent reliable place to store the binding information.
19915          * @example
19916             <example>
19917               <file name="index.html">
19918                 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19919                 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19920               </file>
19921               <file name="protractor.js" type="protractor">
19922                 it('should check both checkBoxes', function() {
19923                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19924                   element(by.model('master')).click();
19925                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19926                 });
19927               </file>
19928             </example>
19929          *
19930          * @element INPUT
19931          * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19932          *     then the `checked` attribute will be set on the element
19933          */
19934
19935
19936         /**
19937          * @ngdoc directive
19938          * @name ngReadonly
19939          * @restrict A
19940          * @priority 100
19941          *
19942          * @description
19943          * The HTML specification does not require browsers to preserve the values of boolean attributes
19944          * such as readonly. (Their presence means true and their absence means false.)
19945          * If we put an Angular interpolation expression into such an attribute then the
19946          * binding information would be lost when the browser removes the attribute.
19947          * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19948          * This complementary directive is not removed by the browser and so provides
19949          * a permanent reliable place to store the binding information.
19950          * @example
19951             <example>
19952               <file name="index.html">
19953                 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19954                 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19955               </file>
19956               <file name="protractor.js" type="protractor">
19957                 it('should toggle readonly attr', function() {
19958                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19959                   element(by.model('checked')).click();
19960                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19961                 });
19962               </file>
19963             </example>
19964          *
19965          * @element INPUT
19966          * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19967          *     then special attribute "readonly" will be set on the element
19968          */
19969
19970
19971         /**
19972          * @ngdoc directive
19973          * @name ngSelected
19974          * @restrict A
19975          * @priority 100
19976          *
19977          * @description
19978          * The HTML specification does not require browsers to preserve the values of boolean attributes
19979          * such as selected. (Their presence means true and their absence means false.)
19980          * If we put an Angular interpolation expression into such an attribute then the
19981          * binding information would be lost when the browser removes the attribute.
19982          * The `ngSelected` directive solves this problem for the `selected` attribute.
19983          * This complementary directive is not removed by the browser and so provides
19984          * a permanent reliable place to store the binding information.
19985          *
19986          * @example
19987             <example>
19988               <file name="index.html">
19989                 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19990                 <select aria-label="ngSelected demo">
19991                   <option>Hello!</option>
19992                   <option id="greet" ng-selected="selected">Greetings!</option>
19993                 </select>
19994               </file>
19995               <file name="protractor.js" type="protractor">
19996                 it('should select Greetings!', function() {
19997                   expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19998                   element(by.model('selected')).click();
19999                   expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
20000                 });
20001               </file>
20002             </example>
20003          *
20004          * @element OPTION
20005          * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20006          *     then special attribute "selected" will be set on the element
20007          */
20008
20009         /**
20010          * @ngdoc directive
20011          * @name ngOpen
20012          * @restrict A
20013          * @priority 100
20014          *
20015          * @description
20016          * The HTML specification does not require browsers to preserve the values of boolean attributes
20017          * such as open. (Their presence means true and their absence means false.)
20018          * If we put an Angular interpolation expression into such an attribute then the
20019          * binding information would be lost when the browser removes the attribute.
20020          * The `ngOpen` directive solves this problem for the `open` attribute.
20021          * This complementary directive is not removed by the browser and so provides
20022          * a permanent reliable place to store the binding information.
20023          * @example
20024              <example>
20025                <file name="index.html">
20026                  <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20027                  <details id="details" ng-open="open">
20028                     <summary>Show/Hide me</summary>
20029                  </details>
20030                </file>
20031                <file name="protractor.js" type="protractor">
20032                  it('should toggle open', function() {
20033                    expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20034                    element(by.model('open')).click();
20035                    expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20036                  });
20037                </file>
20038              </example>
20039          *
20040          * @element DETAILS
20041          * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20042          *     then special attribute "open" will be set on the element
20043          */
20044
20045         var ngAttributeAliasDirectives = {};
20046
20047         // boolean attrs are evaluated
20048         forEach(BOOLEAN_ATTR, function(propName, attrName) {
20049           // binding to multiple is not supported
20050           if (propName == "multiple") return;
20051
20052           function defaultLinkFn(scope, element, attr) {
20053             scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20054               attr.$set(attrName, !!value);
20055             });
20056           }
20057
20058           var normalized = directiveNormalize('ng-' + attrName);
20059           var linkFn = defaultLinkFn;
20060
20061           if (propName === 'checked') {
20062             linkFn = function(scope, element, attr) {
20063               // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20064               if (attr.ngModel !== attr[normalized]) {
20065                 defaultLinkFn(scope, element, attr);
20066               }
20067             };
20068           }
20069
20070           ngAttributeAliasDirectives[normalized] = function() {
20071             return {
20072               restrict: 'A',
20073               priority: 100,
20074               link: linkFn
20075             };
20076           };
20077         });
20078
20079         // aliased input attrs are evaluated
20080         forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20081           ngAttributeAliasDirectives[ngAttr] = function() {
20082             return {
20083               priority: 100,
20084               link: function(scope, element, attr) {
20085                 //special case ngPattern when a literal regular expression value
20086                 //is used as the expression (this way we don't have to watch anything).
20087                 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20088                   var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20089                   if (match) {
20090                     attr.$set("ngPattern", new RegExp(match[1], match[2]));
20091                     return;
20092                   }
20093                 }
20094
20095                 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20096                   attr.$set(ngAttr, value);
20097                 });
20098               }
20099             };
20100           };
20101         });
20102
20103         // ng-src, ng-srcset, ng-href are interpolated
20104         forEach(['src', 'srcset', 'href'], function(attrName) {
20105           var normalized = directiveNormalize('ng-' + attrName);
20106           ngAttributeAliasDirectives[normalized] = function() {
20107             return {
20108               priority: 99, // it needs to run after the attributes are interpolated
20109               link: function(scope, element, attr) {
20110                 var propName = attrName,
20111                     name = attrName;
20112
20113                 if (attrName === 'href' &&
20114                     toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20115                   name = 'xlinkHref';
20116                   attr.$attr[name] = 'xlink:href';
20117                   propName = null;
20118                 }
20119
20120                 attr.$observe(normalized, function(value) {
20121                   if (!value) {
20122                     if (attrName === 'href') {
20123                       attr.$set(name, null);
20124                     }
20125                     return;
20126                   }
20127
20128                   attr.$set(name, value);
20129
20130                   // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20131                   // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20132                   // to set the property as well to achieve the desired effect.
20133                   // we use attr[attrName] value since $set can sanitize the url.
20134                   if (msie && propName) element.prop(propName, attr[name]);
20135                 });
20136               }
20137             };
20138           };
20139         });
20140
20141         /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20142          */
20143         var nullFormCtrl = {
20144           $addControl: noop,
20145           $$renameControl: nullFormRenameControl,
20146           $removeControl: noop,
20147           $setValidity: noop,
20148           $setDirty: noop,
20149           $setPristine: noop,
20150           $setSubmitted: noop
20151         },
20152         SUBMITTED_CLASS = 'ng-submitted';
20153
20154         function nullFormRenameControl(control, name) {
20155           control.$name = name;
20156         }
20157
20158         /**
20159          * @ngdoc type
20160          * @name form.FormController
20161          *
20162          * @property {boolean} $pristine True if user has not interacted with the form yet.
20163          * @property {boolean} $dirty True if user has already interacted with the form.
20164          * @property {boolean} $valid True if all of the containing forms and controls are valid.
20165          * @property {boolean} $invalid True if at least one containing control or form is invalid.
20166          * @property {boolean} $pending True if at least one containing control or form is pending.
20167          * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20168          *
20169          * @property {Object} $error Is an object hash, containing references to controls or
20170          *  forms with failing validators, where:
20171          *
20172          *  - keys are validation tokens (error names),
20173          *  - values are arrays of controls or forms that have a failing validator for given error name.
20174          *
20175          *  Built-in validation tokens:
20176          *
20177          *  - `email`
20178          *  - `max`
20179          *  - `maxlength`
20180          *  - `min`
20181          *  - `minlength`
20182          *  - `number`
20183          *  - `pattern`
20184          *  - `required`
20185          *  - `url`
20186          *  - `date`
20187          *  - `datetimelocal`
20188          *  - `time`
20189          *  - `week`
20190          *  - `month`
20191          *
20192          * @description
20193          * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20194          * such as being valid/invalid or dirty/pristine.
20195          *
20196          * Each {@link ng.directive:form form} directive creates an instance
20197          * of `FormController`.
20198          *
20199          */
20200         //asks for $scope to fool the BC controller module
20201         FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20202         function FormController(element, attrs, $scope, $animate, $interpolate) {
20203           var form = this,
20204               controls = [];
20205
20206           // init state
20207           form.$error = {};
20208           form.$$success = {};
20209           form.$pending = undefined;
20210           form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20211           form.$dirty = false;
20212           form.$pristine = true;
20213           form.$valid = true;
20214           form.$invalid = false;
20215           form.$submitted = false;
20216           form.$$parentForm = nullFormCtrl;
20217
20218           /**
20219            * @ngdoc method
20220            * @name form.FormController#$rollbackViewValue
20221            *
20222            * @description
20223            * Rollback all form controls pending updates to the `$modelValue`.
20224            *
20225            * Updates may be pending by a debounced event or because the input is waiting for a some future
20226            * event defined in `ng-model-options`. This method is typically needed by the reset button of
20227            * a form that uses `ng-model-options` to pend updates.
20228            */
20229           form.$rollbackViewValue = function() {
20230             forEach(controls, function(control) {
20231               control.$rollbackViewValue();
20232             });
20233           };
20234
20235           /**
20236            * @ngdoc method
20237            * @name form.FormController#$commitViewValue
20238            *
20239            * @description
20240            * Commit all form controls pending updates to the `$modelValue`.
20241            *
20242            * Updates may be pending by a debounced event or because the input is waiting for a some future
20243            * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20244            * usually handles calling this in response to input events.
20245            */
20246           form.$commitViewValue = function() {
20247             forEach(controls, function(control) {
20248               control.$commitViewValue();
20249             });
20250           };
20251
20252           /**
20253            * @ngdoc method
20254            * @name form.FormController#$addControl
20255            * @param {object} control control object, either a {@link form.FormController} or an
20256            * {@link ngModel.NgModelController}
20257            *
20258            * @description
20259            * Register a control with the form. Input elements using ngModelController do this automatically
20260            * when they are linked.
20261            *
20262            * Note that the current state of the control will not be reflected on the new parent form. This
20263            * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20264            * state.
20265            *
20266            * However, if the method is used programmatically, for example by adding dynamically created controls,
20267            * or controls that have been previously removed without destroying their corresponding DOM element,
20268            * it's the developers responsiblity to make sure the current state propagates to the parent form.
20269            *
20270            * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20271            * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20272            */
20273           form.$addControl = function(control) {
20274             // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20275             // and not added to the scope.  Now we throw an error.
20276             assertNotHasOwnProperty(control.$name, 'input');
20277             controls.push(control);
20278
20279             if (control.$name) {
20280               form[control.$name] = control;
20281             }
20282
20283             control.$$parentForm = form;
20284           };
20285
20286           // Private API: rename a form control
20287           form.$$renameControl = function(control, newName) {
20288             var oldName = control.$name;
20289
20290             if (form[oldName] === control) {
20291               delete form[oldName];
20292             }
20293             form[newName] = control;
20294             control.$name = newName;
20295           };
20296
20297           /**
20298            * @ngdoc method
20299            * @name form.FormController#$removeControl
20300            * @param {object} control control object, either a {@link form.FormController} or an
20301            * {@link ngModel.NgModelController}
20302            *
20303            * @description
20304            * Deregister a control from the form.
20305            *
20306            * Input elements using ngModelController do this automatically when they are destroyed.
20307            *
20308            * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20309            * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20310            * different from case to case. For example, removing the only `$dirty` control from a form may or
20311            * may not mean that the form is still `$dirty`.
20312            */
20313           form.$removeControl = function(control) {
20314             if (control.$name && form[control.$name] === control) {
20315               delete form[control.$name];
20316             }
20317             forEach(form.$pending, function(value, name) {
20318               form.$setValidity(name, null, control);
20319             });
20320             forEach(form.$error, function(value, name) {
20321               form.$setValidity(name, null, control);
20322             });
20323             forEach(form.$$success, function(value, name) {
20324               form.$setValidity(name, null, control);
20325             });
20326
20327             arrayRemove(controls, control);
20328             control.$$parentForm = nullFormCtrl;
20329           };
20330
20331
20332           /**
20333            * @ngdoc method
20334            * @name form.FormController#$setValidity
20335            *
20336            * @description
20337            * Sets the validity of a form control.
20338            *
20339            * This method will also propagate to parent forms.
20340            */
20341           addSetValidityMethod({
20342             ctrl: this,
20343             $element: element,
20344             set: function(object, property, controller) {
20345               var list = object[property];
20346               if (!list) {
20347                 object[property] = [controller];
20348               } else {
20349                 var index = list.indexOf(controller);
20350                 if (index === -1) {
20351                   list.push(controller);
20352                 }
20353               }
20354             },
20355             unset: function(object, property, controller) {
20356               var list = object[property];
20357               if (!list) {
20358                 return;
20359               }
20360               arrayRemove(list, controller);
20361               if (list.length === 0) {
20362                 delete object[property];
20363               }
20364             },
20365             $animate: $animate
20366           });
20367
20368           /**
20369            * @ngdoc method
20370            * @name form.FormController#$setDirty
20371            *
20372            * @description
20373            * Sets the form to a dirty state.
20374            *
20375            * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20376            * state (ng-dirty class). This method will also propagate to parent forms.
20377            */
20378           form.$setDirty = function() {
20379             $animate.removeClass(element, PRISTINE_CLASS);
20380             $animate.addClass(element, DIRTY_CLASS);
20381             form.$dirty = true;
20382             form.$pristine = false;
20383             form.$$parentForm.$setDirty();
20384           };
20385
20386           /**
20387            * @ngdoc method
20388            * @name form.FormController#$setPristine
20389            *
20390            * @description
20391            * Sets the form to its pristine state.
20392            *
20393            * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20394            * state (ng-pristine class). This method will also propagate to all the controls contained
20395            * in this form.
20396            *
20397            * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20398            * saving or resetting it.
20399            */
20400           form.$setPristine = function() {
20401             $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20402             form.$dirty = false;
20403             form.$pristine = true;
20404             form.$submitted = false;
20405             forEach(controls, function(control) {
20406               control.$setPristine();
20407             });
20408           };
20409
20410           /**
20411            * @ngdoc method
20412            * @name form.FormController#$setUntouched
20413            *
20414            * @description
20415            * Sets the form to its untouched state.
20416            *
20417            * This method can be called to remove the 'ng-touched' class and set the form controls to their
20418            * untouched state (ng-untouched class).
20419            *
20420            * Setting a form controls back to their untouched state is often useful when setting the form
20421            * back to its pristine state.
20422            */
20423           form.$setUntouched = function() {
20424             forEach(controls, function(control) {
20425               control.$setUntouched();
20426             });
20427           };
20428
20429           /**
20430            * @ngdoc method
20431            * @name form.FormController#$setSubmitted
20432            *
20433            * @description
20434            * Sets the form to its submitted state.
20435            */
20436           form.$setSubmitted = function() {
20437             $animate.addClass(element, SUBMITTED_CLASS);
20438             form.$submitted = true;
20439             form.$$parentForm.$setSubmitted();
20440           };
20441         }
20442
20443         /**
20444          * @ngdoc directive
20445          * @name ngForm
20446          * @restrict EAC
20447          *
20448          * @description
20449          * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20450          * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20451          * sub-group of controls needs to be determined.
20452          *
20453          * Note: the purpose of `ngForm` is to group controls,
20454          * but not to be a replacement for the `<form>` tag with all of its capabilities
20455          * (e.g. posting to the server, ...).
20456          *
20457          * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20458          *                       related scope, under this name.
20459          *
20460          */
20461
20462          /**
20463          * @ngdoc directive
20464          * @name form
20465          * @restrict E
20466          *
20467          * @description
20468          * Directive that instantiates
20469          * {@link form.FormController FormController}.
20470          *
20471          * If the `name` attribute is specified, the form controller is published onto the current scope under
20472          * this name.
20473          *
20474          * # Alias: {@link ng.directive:ngForm `ngForm`}
20475          *
20476          * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20477          * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20478          * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20479          * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20480          * using Angular validation directives in forms that are dynamically generated using the
20481          * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20482          * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20483          * `ngForm` directive and nest these in an outer `form` element.
20484          *
20485          *
20486          * # CSS classes
20487          *  - `ng-valid` is set if the form is valid.
20488          *  - `ng-invalid` is set if the form is invalid.
20489          *  - `ng-pending` is set if the form is pending.
20490          *  - `ng-pristine` is set if the form is pristine.
20491          *  - `ng-dirty` is set if the form is dirty.
20492          *  - `ng-submitted` is set if the form was submitted.
20493          *
20494          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20495          *
20496          *
20497          * # Submitting a form and preventing the default action
20498          *
20499          * Since the role of forms in client-side Angular applications is different than in classical
20500          * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20501          * page reload that sends the data to the server. Instead some javascript logic should be triggered
20502          * to handle the form submission in an application-specific way.
20503          *
20504          * For this reason, Angular prevents the default action (form submission to the server) unless the
20505          * `<form>` element has an `action` attribute specified.
20506          *
20507          * You can use one of the following two ways to specify what javascript method should be called when
20508          * a form is submitted:
20509          *
20510          * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20511          * - {@link ng.directive:ngClick ngClick} directive on the first
20512           *  button or input field of type submit (input[type=submit])
20513          *
20514          * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20515          * or {@link ng.directive:ngClick ngClick} directives.
20516          * This is because of the following form submission rules in the HTML specification:
20517          *
20518          * - If a form has only one input field then hitting enter in this field triggers form submit
20519          * (`ngSubmit`)
20520          * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20521          * doesn't trigger submit
20522          * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20523          * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20524          * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20525          *
20526          * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20527          * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20528          * to have access to the updated model.
20529          *
20530          * ## Animation Hooks
20531          *
20532          * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20533          * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20534          * other validations that are performed within the form. Animations in ngForm are similar to how
20535          * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20536          * as JS animations.
20537          *
20538          * The following example shows a simple way to utilize CSS transitions to style a form element
20539          * that has been rendered as invalid after it has been validated:
20540          *
20541          * <pre>
20542          * //be sure to include ngAnimate as a module to hook into more
20543          * //advanced animations
20544          * .my-form {
20545          *   transition:0.5s linear all;
20546          *   background: white;
20547          * }
20548          * .my-form.ng-invalid {
20549          *   background: red;
20550          *   color:white;
20551          * }
20552          * </pre>
20553          *
20554          * @example
20555             <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20556               <file name="index.html">
20557                <script>
20558                  angular.module('formExample', [])
20559                    .controller('FormController', ['$scope', function($scope) {
20560                      $scope.userType = 'guest';
20561                    }]);
20562                </script>
20563                <style>
20564                 .my-form {
20565                   transition:all linear 0.5s;
20566                   background: transparent;
20567                 }
20568                 .my-form.ng-invalid {
20569                   background: red;
20570                 }
20571                </style>
20572                <form name="myForm" ng-controller="FormController" class="my-form">
20573                  userType: <input name="input" ng-model="userType" required>
20574                  <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20575                  <code>userType = {{userType}}</code><br>
20576                  <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20577                  <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20578                  <code>myForm.$valid = {{myForm.$valid}}</code><br>
20579                  <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20580                 </form>
20581               </file>
20582               <file name="protractor.js" type="protractor">
20583                 it('should initialize to model', function() {
20584                   var userType = element(by.binding('userType'));
20585                   var valid = element(by.binding('myForm.input.$valid'));
20586
20587                   expect(userType.getText()).toContain('guest');
20588                   expect(valid.getText()).toContain('true');
20589                 });
20590
20591                 it('should be invalid if empty', function() {
20592                   var userType = element(by.binding('userType'));
20593                   var valid = element(by.binding('myForm.input.$valid'));
20594                   var userInput = element(by.model('userType'));
20595
20596                   userInput.clear();
20597                   userInput.sendKeys('');
20598
20599                   expect(userType.getText()).toEqual('userType =');
20600                   expect(valid.getText()).toContain('false');
20601                 });
20602               </file>
20603             </example>
20604          *
20605          * @param {string=} name Name of the form. If specified, the form controller will be published into
20606          *                       related scope, under this name.
20607          */
20608         var formDirectiveFactory = function(isNgForm) {
20609           return ['$timeout', '$parse', function($timeout, $parse) {
20610             var formDirective = {
20611               name: 'form',
20612               restrict: isNgForm ? 'EAC' : 'E',
20613               require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20614               controller: FormController,
20615               compile: function ngFormCompile(formElement, attr) {
20616                 // Setup initial state of the control
20617                 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20618
20619                 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20620
20621                 return {
20622                   pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20623                     var controller = ctrls[0];
20624
20625                     // if `action` attr is not present on the form, prevent the default action (submission)
20626                     if (!('action' in attr)) {
20627                       // we can't use jq events because if a form is destroyed during submission the default
20628                       // action is not prevented. see #1238
20629                       //
20630                       // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20631                       // page reload if the form was destroyed by submission of the form via a click handler
20632                       // on a button in the form. Looks like an IE9 specific bug.
20633                       var handleFormSubmission = function(event) {
20634                         scope.$apply(function() {
20635                           controller.$commitViewValue();
20636                           controller.$setSubmitted();
20637                         });
20638
20639                         event.preventDefault();
20640                       };
20641
20642                       addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20643
20644                       // unregister the preventDefault listener so that we don't not leak memory but in a
20645                       // way that will achieve the prevention of the default action.
20646                       formElement.on('$destroy', function() {
20647                         $timeout(function() {
20648                           removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20649                         }, 0, false);
20650                       });
20651                     }
20652
20653                     var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20654                     parentFormCtrl.$addControl(controller);
20655
20656                     var setter = nameAttr ? getSetter(controller.$name) : noop;
20657
20658                     if (nameAttr) {
20659                       setter(scope, controller);
20660                       attr.$observe(nameAttr, function(newValue) {
20661                         if (controller.$name === newValue) return;
20662                         setter(scope, undefined);
20663                         controller.$$parentForm.$$renameControl(controller, newValue);
20664                         setter = getSetter(controller.$name);
20665                         setter(scope, controller);
20666                       });
20667                     }
20668                     formElement.on('$destroy', function() {
20669                       controller.$$parentForm.$removeControl(controller);
20670                       setter(scope, undefined);
20671                       extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20672                     });
20673                   }
20674                 };
20675               }
20676             };
20677
20678             return formDirective;
20679
20680             function getSetter(expression) {
20681               if (expression === '') {
20682                 //create an assignable expression, so forms with an empty name can be renamed later
20683                 return $parse('this[""]').assign;
20684               }
20685               return $parse(expression).assign || noop;
20686             }
20687           }];
20688         };
20689
20690         var formDirective = formDirectiveFactory();
20691         var ngFormDirective = formDirectiveFactory(true);
20692
20693         /* global VALID_CLASS: false,
20694           INVALID_CLASS: false,
20695           PRISTINE_CLASS: false,
20696           DIRTY_CLASS: false,
20697           UNTOUCHED_CLASS: false,
20698           TOUCHED_CLASS: false,
20699           ngModelMinErr: false,
20700         */
20701
20702         // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20703         var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
20704         // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20705         var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20706         var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20707         var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20708         var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20709         var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20710         var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20711         var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20712         var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20713
20714         var inputType = {
20715
20716           /**
20717            * @ngdoc input
20718            * @name input[text]
20719            *
20720            * @description
20721            * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20722            *
20723            *
20724            * @param {string} ngModel Assignable angular expression to data-bind to.
20725            * @param {string=} name Property name of the form under which the control is published.
20726            * @param {string=} required Adds `required` validation error key if the value is not entered.
20727            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20728            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20729            *    `required` when you want to data-bind to the `required` attribute.
20730            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20731            *    minlength.
20732            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20733            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20734            *    any length.
20735            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20736            *    that contains the regular expression body that will be converted to a regular expression
20737            *    as in the ngPattern directive.
20738            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20739            *    a RegExp found by evaluating the Angular expression given in the attribute value.
20740            *    If the expression evaluates to a RegExp object, then this is used directly.
20741            *    If the expression evaluates to a string, then it will be converted to a RegExp
20742            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20743            *    `new RegExp('^abc$')`.<br />
20744            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20745            *    start at the index of the last search's match, thus not taking the whole input value into
20746            *    account.
20747            * @param {string=} ngChange Angular expression to be executed when input changes due to user
20748            *    interaction with the input element.
20749            * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20750            *    This parameter is ignored for input[type=password] controls, which will never trim the
20751            *    input.
20752            *
20753            * @example
20754               <example name="text-input-directive" module="textInputExample">
20755                 <file name="index.html">
20756                  <script>
20757                    angular.module('textInputExample', [])
20758                      .controller('ExampleController', ['$scope', function($scope) {
20759                        $scope.example = {
20760                          text: 'guest',
20761                          word: /^\s*\w*\s*$/
20762                        };
20763                      }]);
20764                  </script>
20765                  <form name="myForm" ng-controller="ExampleController">
20766                    <label>Single word:
20767                      <input type="text" name="input" ng-model="example.text"
20768                             ng-pattern="example.word" required ng-trim="false">
20769                    </label>
20770                    <div role="alert">
20771                      <span class="error" ng-show="myForm.input.$error.required">
20772                        Required!</span>
20773                      <span class="error" ng-show="myForm.input.$error.pattern">
20774                        Single word only!</span>
20775                    </div>
20776                    <tt>text = {{example.text}}</tt><br/>
20777                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20778                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20779                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20780                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20781                   </form>
20782                 </file>
20783                 <file name="protractor.js" type="protractor">
20784                   var text = element(by.binding('example.text'));
20785                   var valid = element(by.binding('myForm.input.$valid'));
20786                   var input = element(by.model('example.text'));
20787
20788                   it('should initialize to model', function() {
20789                     expect(text.getText()).toContain('guest');
20790                     expect(valid.getText()).toContain('true');
20791                   });
20792
20793                   it('should be invalid if empty', function() {
20794                     input.clear();
20795                     input.sendKeys('');
20796
20797                     expect(text.getText()).toEqual('text =');
20798                     expect(valid.getText()).toContain('false');
20799                   });
20800
20801                   it('should be invalid if multi word', function() {
20802                     input.clear();
20803                     input.sendKeys('hello world');
20804
20805                     expect(valid.getText()).toContain('false');
20806                   });
20807                 </file>
20808               </example>
20809            */
20810           'text': textInputType,
20811
20812             /**
20813              * @ngdoc input
20814              * @name input[date]
20815              *
20816              * @description
20817              * Input with date validation and transformation. In browsers that do not yet support
20818              * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20819              * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20820              * modern browsers do not yet support this input type, it is important to provide cues to users on the
20821              * expected input format via a placeholder or label.
20822              *
20823              * The model must always be a Date object, otherwise Angular will throw an error.
20824              * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20825              *
20826              * The timezone to be used to read/write the `Date` instance in the model can be defined using
20827              * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20828              *
20829              * @param {string} ngModel Assignable angular expression to data-bind to.
20830              * @param {string=} name Property name of the form under which the control is published.
20831              * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20832              *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20833              *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20834              *   constraint validation.
20835              * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20836              *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20837              *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20838              *   constraint validation.
20839              * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20840              *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20841              * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20842              *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20843              * @param {string=} required Sets `required` validation error key if the value is not entered.
20844              * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20845              *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20846              *    `required` when you want to data-bind to the `required` attribute.
20847              * @param {string=} ngChange Angular expression to be executed when input changes due to user
20848              *    interaction with the input element.
20849              *
20850              * @example
20851              <example name="date-input-directive" module="dateInputExample">
20852              <file name="index.html">
20853                <script>
20854                   angular.module('dateInputExample', [])
20855                     .controller('DateController', ['$scope', function($scope) {
20856                       $scope.example = {
20857                         value: new Date(2013, 9, 22)
20858                       };
20859                     }]);
20860                </script>
20861                <form name="myForm" ng-controller="DateController as dateCtrl">
20862                   <label for="exampleInput">Pick a date in 2013:</label>
20863                   <input type="date" id="exampleInput" name="input" ng-model="example.value"
20864                       placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20865                   <div role="alert">
20866                     <span class="error" ng-show="myForm.input.$error.required">
20867                         Required!</span>
20868                     <span class="error" ng-show="myForm.input.$error.date">
20869                         Not a valid date!</span>
20870                    </div>
20871                    <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20872                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20873                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20874                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20875                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20876                </form>
20877              </file>
20878              <file name="protractor.js" type="protractor">
20879                 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20880                 var valid = element(by.binding('myForm.input.$valid'));
20881                 var input = element(by.model('example.value'));
20882
20883                 // currently protractor/webdriver does not support
20884                 // sending keys to all known HTML5 input controls
20885                 // for various browsers (see https://github.com/angular/protractor/issues/562).
20886                 function setInput(val) {
20887                   // set the value of the element and force validation.
20888                   var scr = "var ipt = document.getElementById('exampleInput'); " +
20889                   "ipt.value = '" + val + "';" +
20890                   "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20891                   browser.executeScript(scr);
20892                 }
20893
20894                 it('should initialize to model', function() {
20895                   expect(value.getText()).toContain('2013-10-22');
20896                   expect(valid.getText()).toContain('myForm.input.$valid = true');
20897                 });
20898
20899                 it('should be invalid if empty', function() {
20900                   setInput('');
20901                   expect(value.getText()).toEqual('value =');
20902                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20903                 });
20904
20905                 it('should be invalid if over max', function() {
20906                   setInput('2015-01-01');
20907                   expect(value.getText()).toContain('');
20908                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20909                 });
20910              </file>
20911              </example>
20912              */
20913           'date': createDateInputType('date', DATE_REGEXP,
20914                  createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20915                  'yyyy-MM-dd'),
20916
20917            /**
20918             * @ngdoc input
20919             * @name input[datetime-local]
20920             *
20921             * @description
20922             * Input with datetime validation and transformation. In browsers that do not yet support
20923             * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20924             * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20925             *
20926             * The model must always be a Date object, otherwise Angular will throw an error.
20927             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20928             *
20929             * The timezone to be used to read/write the `Date` instance in the model can be defined using
20930             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20931             *
20932             * @param {string} ngModel Assignable angular expression to data-bind to.
20933             * @param {string=} name Property name of the form under which the control is published.
20934             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20935             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20936             *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20937             *   Note that `min` will also add native HTML5 constraint validation.
20938             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20939             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20940             *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20941             *   Note that `max` will also add native HTML5 constraint validation.
20942             * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20943             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20944             * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20945             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20946             * @param {string=} required Sets `required` validation error key if the value is not entered.
20947             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20948             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20949             *    `required` when you want to data-bind to the `required` attribute.
20950             * @param {string=} ngChange Angular expression to be executed when input changes due to user
20951             *    interaction with the input element.
20952             *
20953             * @example
20954             <example name="datetimelocal-input-directive" module="dateExample">
20955             <file name="index.html">
20956               <script>
20957                 angular.module('dateExample', [])
20958                   .controller('DateController', ['$scope', function($scope) {
20959                     $scope.example = {
20960                       value: new Date(2010, 11, 28, 14, 57)
20961                     };
20962                   }]);
20963               </script>
20964               <form name="myForm" ng-controller="DateController as dateCtrl">
20965                 <label for="exampleInput">Pick a date between in 2013:</label>
20966                 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20967                     placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20968                 <div role="alert">
20969                   <span class="error" ng-show="myForm.input.$error.required">
20970                       Required!</span>
20971                   <span class="error" ng-show="myForm.input.$error.datetimelocal">
20972                       Not a valid date!</span>
20973                 </div>
20974                 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20975                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20976                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20977                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20978                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20979               </form>
20980             </file>
20981             <file name="protractor.js" type="protractor">
20982               var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20983               var valid = element(by.binding('myForm.input.$valid'));
20984               var input = element(by.model('example.value'));
20985
20986               // currently protractor/webdriver does not support
20987               // sending keys to all known HTML5 input controls
20988               // for various browsers (https://github.com/angular/protractor/issues/562).
20989               function setInput(val) {
20990                 // set the value of the element and force validation.
20991                 var scr = "var ipt = document.getElementById('exampleInput'); " +
20992                 "ipt.value = '" + val + "';" +
20993                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20994                 browser.executeScript(scr);
20995               }
20996
20997               it('should initialize to model', function() {
20998                 expect(value.getText()).toContain('2010-12-28T14:57:00');
20999                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21000               });
21001
21002               it('should be invalid if empty', function() {
21003                 setInput('');
21004                 expect(value.getText()).toEqual('value =');
21005                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21006               });
21007
21008               it('should be invalid if over max', function() {
21009                 setInput('2015-01-01T23:59:00');
21010                 expect(value.getText()).toContain('');
21011                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21012               });
21013             </file>
21014             </example>
21015             */
21016           'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21017               createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21018               'yyyy-MM-ddTHH:mm:ss.sss'),
21019
21020           /**
21021            * @ngdoc input
21022            * @name input[time]
21023            *
21024            * @description
21025            * Input with time validation and transformation. In browsers that do not yet support
21026            * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21027            * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21028            * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21029            *
21030            * The model must always be a Date object, otherwise Angular will throw an error.
21031            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21032            *
21033            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21034            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21035            *
21036            * @param {string} ngModel Assignable angular expression to data-bind to.
21037            * @param {string=} name Property name of the form under which the control is published.
21038            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21039            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21040            *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21041            *   native HTML5 constraint validation.
21042            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21043            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21044            *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21045            *   native HTML5 constraint validation.
21046            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21047            *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21048            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21049            *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21050            * @param {string=} required Sets `required` validation error key if the value is not entered.
21051            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21052            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21053            *    `required` when you want to data-bind to the `required` attribute.
21054            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21055            *    interaction with the input element.
21056            *
21057            * @example
21058            <example name="time-input-directive" module="timeExample">
21059            <file name="index.html">
21060              <script>
21061               angular.module('timeExample', [])
21062                 .controller('DateController', ['$scope', function($scope) {
21063                   $scope.example = {
21064                     value: new Date(1970, 0, 1, 14, 57, 0)
21065                   };
21066                 }]);
21067              </script>
21068              <form name="myForm" ng-controller="DateController as dateCtrl">
21069                 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21070                 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21071                     placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21072                 <div role="alert">
21073                   <span class="error" ng-show="myForm.input.$error.required">
21074                       Required!</span>
21075                   <span class="error" ng-show="myForm.input.$error.time">
21076                       Not a valid date!</span>
21077                 </div>
21078                 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21079                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21080                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21081                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21082                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21083              </form>
21084            </file>
21085            <file name="protractor.js" type="protractor">
21086               var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21087               var valid = element(by.binding('myForm.input.$valid'));
21088               var input = element(by.model('example.value'));
21089
21090               // currently protractor/webdriver does not support
21091               // sending keys to all known HTML5 input controls
21092               // for various browsers (https://github.com/angular/protractor/issues/562).
21093               function setInput(val) {
21094                 // set the value of the element and force validation.
21095                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21096                 "ipt.value = '" + val + "';" +
21097                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21098                 browser.executeScript(scr);
21099               }
21100
21101               it('should initialize to model', function() {
21102                 expect(value.getText()).toContain('14:57:00');
21103                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21104               });
21105
21106               it('should be invalid if empty', function() {
21107                 setInput('');
21108                 expect(value.getText()).toEqual('value =');
21109                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21110               });
21111
21112               it('should be invalid if over max', function() {
21113                 setInput('23:59:00');
21114                 expect(value.getText()).toContain('');
21115                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21116               });
21117            </file>
21118            </example>
21119            */
21120           'time': createDateInputType('time', TIME_REGEXP,
21121               createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21122              'HH:mm:ss.sss'),
21123
21124            /**
21125             * @ngdoc input
21126             * @name input[week]
21127             *
21128             * @description
21129             * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21130             * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21131             * week format (yyyy-W##), for example: `2013-W02`.
21132             *
21133             * The model must always be a Date object, otherwise Angular will throw an error.
21134             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21135             *
21136             * The timezone to be used to read/write the `Date` instance in the model can be defined using
21137             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21138             *
21139             * @param {string} ngModel Assignable angular expression to data-bind to.
21140             * @param {string=} name Property name of the form under which the control is published.
21141             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21142             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21143             *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21144             *   native HTML5 constraint validation.
21145             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21146             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21147             *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21148             *   native HTML5 constraint validation.
21149             * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21150             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21151             * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21152             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21153             * @param {string=} required Sets `required` validation error key if the value is not entered.
21154             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21155             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21156             *    `required` when you want to data-bind to the `required` attribute.
21157             * @param {string=} ngChange Angular expression to be executed when input changes due to user
21158             *    interaction with the input element.
21159             *
21160             * @example
21161             <example name="week-input-directive" module="weekExample">
21162             <file name="index.html">
21163               <script>
21164               angular.module('weekExample', [])
21165                 .controller('DateController', ['$scope', function($scope) {
21166                   $scope.example = {
21167                     value: new Date(2013, 0, 3)
21168                   };
21169                 }]);
21170               </script>
21171               <form name="myForm" ng-controller="DateController as dateCtrl">
21172                 <label>Pick a date between in 2013:
21173                   <input id="exampleInput" type="week" name="input" ng-model="example.value"
21174                          placeholder="YYYY-W##" min="2012-W32"
21175                          max="2013-W52" required />
21176                 </label>
21177                 <div role="alert">
21178                   <span class="error" ng-show="myForm.input.$error.required">
21179                       Required!</span>
21180                   <span class="error" ng-show="myForm.input.$error.week">
21181                       Not a valid date!</span>
21182                 </div>
21183                 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21184                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21185                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21186                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21187                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21188               </form>
21189             </file>
21190             <file name="protractor.js" type="protractor">
21191               var value = element(by.binding('example.value | date: "yyyy-Www"'));
21192               var valid = element(by.binding('myForm.input.$valid'));
21193               var input = element(by.model('example.value'));
21194
21195               // currently protractor/webdriver does not support
21196               // sending keys to all known HTML5 input controls
21197               // for various browsers (https://github.com/angular/protractor/issues/562).
21198               function setInput(val) {
21199                 // set the value of the element and force validation.
21200                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21201                 "ipt.value = '" + val + "';" +
21202                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21203                 browser.executeScript(scr);
21204               }
21205
21206               it('should initialize to model', function() {
21207                 expect(value.getText()).toContain('2013-W01');
21208                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21209               });
21210
21211               it('should be invalid if empty', function() {
21212                 setInput('');
21213                 expect(value.getText()).toEqual('value =');
21214                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21215               });
21216
21217               it('should be invalid if over max', function() {
21218                 setInput('2015-W01');
21219                 expect(value.getText()).toContain('');
21220                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21221               });
21222             </file>
21223             </example>
21224             */
21225           'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21226
21227           /**
21228            * @ngdoc input
21229            * @name input[month]
21230            *
21231            * @description
21232            * Input with month validation and transformation. In browsers that do not yet support
21233            * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21234            * month format (yyyy-MM), for example: `2009-01`.
21235            *
21236            * The model must always be a Date object, otherwise Angular will throw an error.
21237            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21238            * If the model is not set to the first of the month, the next view to model update will set it
21239            * to the first of the month.
21240            *
21241            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21242            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21243            *
21244            * @param {string} ngModel Assignable angular expression to data-bind to.
21245            * @param {string=} name Property name of the form under which the control is published.
21246            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21247            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21248            *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21249            *   native HTML5 constraint validation.
21250            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21251            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21252            *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21253            *   native HTML5 constraint validation.
21254            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21255            *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21256            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21257            *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21258
21259            * @param {string=} required Sets `required` validation error key if the value is not entered.
21260            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21261            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21262            *    `required` when you want to data-bind to the `required` attribute.
21263            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21264            *    interaction with the input element.
21265            *
21266            * @example
21267            <example name="month-input-directive" module="monthExample">
21268            <file name="index.html">
21269              <script>
21270               angular.module('monthExample', [])
21271                 .controller('DateController', ['$scope', function($scope) {
21272                   $scope.example = {
21273                     value: new Date(2013, 9, 1)
21274                   };
21275                 }]);
21276              </script>
21277              <form name="myForm" ng-controller="DateController as dateCtrl">
21278                <label for="exampleInput">Pick a month in 2013:</label>
21279                <input id="exampleInput" type="month" name="input" ng-model="example.value"
21280                   placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21281                <div role="alert">
21282                  <span class="error" ng-show="myForm.input.$error.required">
21283                     Required!</span>
21284                  <span class="error" ng-show="myForm.input.$error.month">
21285                     Not a valid month!</span>
21286                </div>
21287                <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21288                <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21289                <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21290                <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21291                <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21292              </form>
21293            </file>
21294            <file name="protractor.js" type="protractor">
21295               var value = element(by.binding('example.value | date: "yyyy-MM"'));
21296               var valid = element(by.binding('myForm.input.$valid'));
21297               var input = element(by.model('example.value'));
21298
21299               // currently protractor/webdriver does not support
21300               // sending keys to all known HTML5 input controls
21301               // for various browsers (https://github.com/angular/protractor/issues/562).
21302               function setInput(val) {
21303                 // set the value of the element and force validation.
21304                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21305                 "ipt.value = '" + val + "';" +
21306                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21307                 browser.executeScript(scr);
21308               }
21309
21310               it('should initialize to model', function() {
21311                 expect(value.getText()).toContain('2013-10');
21312                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21313               });
21314
21315               it('should be invalid if empty', function() {
21316                 setInput('');
21317                 expect(value.getText()).toEqual('value =');
21318                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21319               });
21320
21321               it('should be invalid if over max', function() {
21322                 setInput('2015-01');
21323                 expect(value.getText()).toContain('');
21324                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21325               });
21326            </file>
21327            </example>
21328            */
21329           'month': createDateInputType('month', MONTH_REGEXP,
21330              createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21331              'yyyy-MM'),
21332
21333           /**
21334            * @ngdoc input
21335            * @name input[number]
21336            *
21337            * @description
21338            * Text input with number validation and transformation. Sets the `number` validation
21339            * error if not a valid number.
21340            *
21341            * <div class="alert alert-warning">
21342            * The model must always be of type `number` otherwise Angular will throw an error.
21343            * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21344            * error docs for more information and an example of how to convert your model if necessary.
21345            * </div>
21346            *
21347            * ## Issues with HTML5 constraint validation
21348            *
21349            * In browsers that follow the
21350            * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21351            * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21352            * If a non-number is entered in the input, the browser will report the value as an empty string,
21353            * which means the view / model values in `ngModel` and subsequently the scope value
21354            * will also be an empty string.
21355            *
21356            *
21357            * @param {string} ngModel Assignable angular expression to data-bind to.
21358            * @param {string=} name Property name of the form under which the control is published.
21359            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21360            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21361            * @param {string=} required Sets `required` validation error key if the value is not entered.
21362            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21363            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21364            *    `required` when you want to data-bind to the `required` attribute.
21365            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21366            *    minlength.
21367            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21368            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21369            *    any length.
21370            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21371            *    that contains the regular expression body that will be converted to a regular expression
21372            *    as in the ngPattern directive.
21373            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21374            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21375            *    If the expression evaluates to a RegExp object, then this is used directly.
21376            *    If the expression evaluates to a string, then it will be converted to a RegExp
21377            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21378            *    `new RegExp('^abc$')`.<br />
21379            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21380            *    start at the index of the last search's match, thus not taking the whole input value into
21381            *    account.
21382            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21383            *    interaction with the input element.
21384            *
21385            * @example
21386               <example name="number-input-directive" module="numberExample">
21387                 <file name="index.html">
21388                  <script>
21389                    angular.module('numberExample', [])
21390                      .controller('ExampleController', ['$scope', function($scope) {
21391                        $scope.example = {
21392                          value: 12
21393                        };
21394                      }]);
21395                  </script>
21396                  <form name="myForm" ng-controller="ExampleController">
21397                    <label>Number:
21398                      <input type="number" name="input" ng-model="example.value"
21399                             min="0" max="99" required>
21400                   </label>
21401                    <div role="alert">
21402                      <span class="error" ng-show="myForm.input.$error.required">
21403                        Required!</span>
21404                      <span class="error" ng-show="myForm.input.$error.number">
21405                        Not valid number!</span>
21406                    </div>
21407                    <tt>value = {{example.value}}</tt><br/>
21408                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21409                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21410                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21411                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21412                   </form>
21413                 </file>
21414                 <file name="protractor.js" type="protractor">
21415                   var value = element(by.binding('example.value'));
21416                   var valid = element(by.binding('myForm.input.$valid'));
21417                   var input = element(by.model('example.value'));
21418
21419                   it('should initialize to model', function() {
21420                     expect(value.getText()).toContain('12');
21421                     expect(valid.getText()).toContain('true');
21422                   });
21423
21424                   it('should be invalid if empty', function() {
21425                     input.clear();
21426                     input.sendKeys('');
21427                     expect(value.getText()).toEqual('value =');
21428                     expect(valid.getText()).toContain('false');
21429                   });
21430
21431                   it('should be invalid if over max', function() {
21432                     input.clear();
21433                     input.sendKeys('123');
21434                     expect(value.getText()).toEqual('value =');
21435                     expect(valid.getText()).toContain('false');
21436                   });
21437                 </file>
21438               </example>
21439            */
21440           'number': numberInputType,
21441
21442
21443           /**
21444            * @ngdoc input
21445            * @name input[url]
21446            *
21447            * @description
21448            * Text input with URL validation. Sets the `url` validation error key if the content is not a
21449            * valid URL.
21450            *
21451            * <div class="alert alert-warning">
21452            * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21453            * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21454            * the built-in validators (see the {@link guide/forms Forms guide})
21455            * </div>
21456            *
21457            * @param {string} ngModel Assignable angular expression to data-bind to.
21458            * @param {string=} name Property name of the form under which the control is published.
21459            * @param {string=} required Sets `required` validation error key if the value is not entered.
21460            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21461            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21462            *    `required` when you want to data-bind to the `required` attribute.
21463            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21464            *    minlength.
21465            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21466            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21467            *    any length.
21468            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21469            *    that contains the regular expression body that will be converted to a regular expression
21470            *    as in the ngPattern directive.
21471            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21472            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21473            *    If the expression evaluates to a RegExp object, then this is used directly.
21474            *    If the expression evaluates to a string, then it will be converted to a RegExp
21475            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21476            *    `new RegExp('^abc$')`.<br />
21477            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21478            *    start at the index of the last search's match, thus not taking the whole input value into
21479            *    account.
21480            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21481            *    interaction with the input element.
21482            *
21483            * @example
21484               <example name="url-input-directive" module="urlExample">
21485                 <file name="index.html">
21486                  <script>
21487                    angular.module('urlExample', [])
21488                      .controller('ExampleController', ['$scope', function($scope) {
21489                        $scope.url = {
21490                          text: 'http://google.com'
21491                        };
21492                      }]);
21493                  </script>
21494                  <form name="myForm" ng-controller="ExampleController">
21495                    <label>URL:
21496                      <input type="url" name="input" ng-model="url.text" required>
21497                    <label>
21498                    <div role="alert">
21499                      <span class="error" ng-show="myForm.input.$error.required">
21500                        Required!</span>
21501                      <span class="error" ng-show="myForm.input.$error.url">
21502                        Not valid url!</span>
21503                    </div>
21504                    <tt>text = {{url.text}}</tt><br/>
21505                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21506                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21507                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21508                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21509                    <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21510                   </form>
21511                 </file>
21512                 <file name="protractor.js" type="protractor">
21513                   var text = element(by.binding('url.text'));
21514                   var valid = element(by.binding('myForm.input.$valid'));
21515                   var input = element(by.model('url.text'));
21516
21517                   it('should initialize to model', function() {
21518                     expect(text.getText()).toContain('http://google.com');
21519                     expect(valid.getText()).toContain('true');
21520                   });
21521
21522                   it('should be invalid if empty', function() {
21523                     input.clear();
21524                     input.sendKeys('');
21525
21526                     expect(text.getText()).toEqual('text =');
21527                     expect(valid.getText()).toContain('false');
21528                   });
21529
21530                   it('should be invalid if not url', function() {
21531                     input.clear();
21532                     input.sendKeys('box');
21533
21534                     expect(valid.getText()).toContain('false');
21535                   });
21536                 </file>
21537               </example>
21538            */
21539           'url': urlInputType,
21540
21541
21542           /**
21543            * @ngdoc input
21544            * @name input[email]
21545            *
21546            * @description
21547            * Text input with email validation. Sets the `email` validation error key if not a valid email
21548            * address.
21549            *
21550            * <div class="alert alert-warning">
21551            * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21552            * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21553            * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21554            * </div>
21555            *
21556            * @param {string} ngModel Assignable angular expression to data-bind to.
21557            * @param {string=} name Property name of the form under which the control is published.
21558            * @param {string=} required Sets `required` validation error key if the value is not entered.
21559            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21560            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21561            *    `required` when you want to data-bind to the `required` attribute.
21562            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21563            *    minlength.
21564            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21565            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21566            *    any length.
21567            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21568            *    that contains the regular expression body that will be converted to a regular expression
21569            *    as in the ngPattern directive.
21570            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21571            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21572            *    If the expression evaluates to a RegExp object, then this is used directly.
21573            *    If the expression evaluates to a string, then it will be converted to a RegExp
21574            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21575            *    `new RegExp('^abc$')`.<br />
21576            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21577            *    start at the index of the last search's match, thus not taking the whole input value into
21578            *    account.
21579            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21580            *    interaction with the input element.
21581            *
21582            * @example
21583               <example name="email-input-directive" module="emailExample">
21584                 <file name="index.html">
21585                  <script>
21586                    angular.module('emailExample', [])
21587                      .controller('ExampleController', ['$scope', function($scope) {
21588                        $scope.email = {
21589                          text: 'me@example.com'
21590                        };
21591                      }]);
21592                  </script>
21593                    <form name="myForm" ng-controller="ExampleController">
21594                      <label>Email:
21595                        <input type="email" name="input" ng-model="email.text" required>
21596                      </label>
21597                      <div role="alert">
21598                        <span class="error" ng-show="myForm.input.$error.required">
21599                          Required!</span>
21600                        <span class="error" ng-show="myForm.input.$error.email">
21601                          Not valid email!</span>
21602                      </div>
21603                      <tt>text = {{email.text}}</tt><br/>
21604                      <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21605                      <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21606                      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21607                      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21608                      <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21609                    </form>
21610                  </file>
21611                 <file name="protractor.js" type="protractor">
21612                   var text = element(by.binding('email.text'));
21613                   var valid = element(by.binding('myForm.input.$valid'));
21614                   var input = element(by.model('email.text'));
21615
21616                   it('should initialize to model', function() {
21617                     expect(text.getText()).toContain('me@example.com');
21618                     expect(valid.getText()).toContain('true');
21619                   });
21620
21621                   it('should be invalid if empty', function() {
21622                     input.clear();
21623                     input.sendKeys('');
21624                     expect(text.getText()).toEqual('text =');
21625                     expect(valid.getText()).toContain('false');
21626                   });
21627
21628                   it('should be invalid if not email', function() {
21629                     input.clear();
21630                     input.sendKeys('xxx');
21631
21632                     expect(valid.getText()).toContain('false');
21633                   });
21634                 </file>
21635               </example>
21636            */
21637           'email': emailInputType,
21638
21639
21640           /**
21641            * @ngdoc input
21642            * @name input[radio]
21643            *
21644            * @description
21645            * HTML radio button.
21646            *
21647            * @param {string} ngModel Assignable angular expression to data-bind to.
21648            * @param {string} value The value to which the `ngModel` expression should be set when selected.
21649            *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21650            *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21651            * @param {string=} name Property name of the form under which the control is published.
21652            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21653            *    interaction with the input element.
21654            * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21655            *    is selected. Should be used instead of the `value` attribute if you need
21656            *    a non-string `ngModel` (`boolean`, `array`, ...).
21657            *
21658            * @example
21659               <example name="radio-input-directive" module="radioExample">
21660                 <file name="index.html">
21661                  <script>
21662                    angular.module('radioExample', [])
21663                      .controller('ExampleController', ['$scope', function($scope) {
21664                        $scope.color = {
21665                          name: 'blue'
21666                        };
21667                        $scope.specialValue = {
21668                          "id": "12345",
21669                          "value": "green"
21670                        };
21671                      }]);
21672                  </script>
21673                  <form name="myForm" ng-controller="ExampleController">
21674                    <label>
21675                      <input type="radio" ng-model="color.name" value="red">
21676                      Red
21677                    </label><br/>
21678                    <label>
21679                      <input type="radio" ng-model="color.name" ng-value="specialValue">
21680                      Green
21681                    </label><br/>
21682                    <label>
21683                      <input type="radio" ng-model="color.name" value="blue">
21684                      Blue
21685                    </label><br/>
21686                    <tt>color = {{color.name | json}}</tt><br/>
21687                   </form>
21688                   Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21689                 </file>
21690                 <file name="protractor.js" type="protractor">
21691                   it('should change state', function() {
21692                     var color = element(by.binding('color.name'));
21693
21694                     expect(color.getText()).toContain('blue');
21695
21696                     element.all(by.model('color.name')).get(0).click();
21697
21698                     expect(color.getText()).toContain('red');
21699                   });
21700                 </file>
21701               </example>
21702            */
21703           'radio': radioInputType,
21704
21705
21706           /**
21707            * @ngdoc input
21708            * @name input[checkbox]
21709            *
21710            * @description
21711            * HTML checkbox.
21712            *
21713            * @param {string} ngModel Assignable angular expression to data-bind to.
21714            * @param {string=} name Property name of the form under which the control is published.
21715            * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21716            * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21717            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21718            *    interaction with the input element.
21719            *
21720            * @example
21721               <example name="checkbox-input-directive" module="checkboxExample">
21722                 <file name="index.html">
21723                  <script>
21724                    angular.module('checkboxExample', [])
21725                      .controller('ExampleController', ['$scope', function($scope) {
21726                        $scope.checkboxModel = {
21727                         value1 : true,
21728                         value2 : 'YES'
21729                       };
21730                      }]);
21731                  </script>
21732                  <form name="myForm" ng-controller="ExampleController">
21733                    <label>Value1:
21734                      <input type="checkbox" ng-model="checkboxModel.value1">
21735                    </label><br/>
21736                    <label>Value2:
21737                      <input type="checkbox" ng-model="checkboxModel.value2"
21738                             ng-true-value="'YES'" ng-false-value="'NO'">
21739                     </label><br/>
21740                    <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21741                    <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21742                   </form>
21743                 </file>
21744                 <file name="protractor.js" type="protractor">
21745                   it('should change state', function() {
21746                     var value1 = element(by.binding('checkboxModel.value1'));
21747                     var value2 = element(by.binding('checkboxModel.value2'));
21748
21749                     expect(value1.getText()).toContain('true');
21750                     expect(value2.getText()).toContain('YES');
21751
21752                     element(by.model('checkboxModel.value1')).click();
21753                     element(by.model('checkboxModel.value2')).click();
21754
21755                     expect(value1.getText()).toContain('false');
21756                     expect(value2.getText()).toContain('NO');
21757                   });
21758                 </file>
21759               </example>
21760            */
21761           'checkbox': checkboxInputType,
21762
21763           'hidden': noop,
21764           'button': noop,
21765           'submit': noop,
21766           'reset': noop,
21767           'file': noop
21768         };
21769
21770         function stringBasedInputType(ctrl) {
21771           ctrl.$formatters.push(function(value) {
21772             return ctrl.$isEmpty(value) ? value : value.toString();
21773           });
21774         }
21775
21776         function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21777           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21778           stringBasedInputType(ctrl);
21779         }
21780
21781         function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21782           var type = lowercase(element[0].type);
21783
21784           // In composition mode, users are still inputing intermediate text buffer,
21785           // hold the listener until composition is done.
21786           // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21787           if (!$sniffer.android) {
21788             var composing = false;
21789
21790             element.on('compositionstart', function(data) {
21791               composing = true;
21792             });
21793
21794             element.on('compositionend', function() {
21795               composing = false;
21796               listener();
21797             });
21798           }
21799
21800           var listener = function(ev) {
21801             if (timeout) {
21802               $browser.defer.cancel(timeout);
21803               timeout = null;
21804             }
21805             if (composing) return;
21806             var value = element.val(),
21807                 event = ev && ev.type;
21808
21809             // By default we will trim the value
21810             // If the attribute ng-trim exists we will avoid trimming
21811             // If input type is 'password', the value is never trimmed
21812             if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21813               value = trim(value);
21814             }
21815
21816             // If a control is suffering from bad input (due to native validators), browsers discard its
21817             // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21818             // control's value is the same empty value twice in a row.
21819             if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21820               ctrl.$setViewValue(value, event);
21821             }
21822           };
21823
21824           // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21825           // input event on backspace, delete or cut
21826           if ($sniffer.hasEvent('input')) {
21827             element.on('input', listener);
21828           } else {
21829             var timeout;
21830
21831             var deferListener = function(ev, input, origValue) {
21832               if (!timeout) {
21833                 timeout = $browser.defer(function() {
21834                   timeout = null;
21835                   if (!input || input.value !== origValue) {
21836                     listener(ev);
21837                   }
21838                 });
21839               }
21840             };
21841
21842             element.on('keydown', function(event) {
21843               var key = event.keyCode;
21844
21845               // ignore
21846               //    command            modifiers                   arrows
21847               if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21848
21849               deferListener(event, this, this.value);
21850             });
21851
21852             // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21853             if ($sniffer.hasEvent('paste')) {
21854               element.on('paste cut', deferListener);
21855             }
21856           }
21857
21858           // if user paste into input using mouse on older browser
21859           // or form autocomplete on newer browser, we need "change" event to catch it
21860           element.on('change', listener);
21861
21862           ctrl.$render = function() {
21863             // Workaround for Firefox validation #12102.
21864             var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21865             if (element.val() !== value) {
21866               element.val(value);
21867             }
21868           };
21869         }
21870
21871         function weekParser(isoWeek, existingDate) {
21872           if (isDate(isoWeek)) {
21873             return isoWeek;
21874           }
21875
21876           if (isString(isoWeek)) {
21877             WEEK_REGEXP.lastIndex = 0;
21878             var parts = WEEK_REGEXP.exec(isoWeek);
21879             if (parts) {
21880               var year = +parts[1],
21881                   week = +parts[2],
21882                   hours = 0,
21883                   minutes = 0,
21884                   seconds = 0,
21885                   milliseconds = 0,
21886                   firstThurs = getFirstThursdayOfYear(year),
21887                   addDays = (week - 1) * 7;
21888
21889               if (existingDate) {
21890                 hours = existingDate.getHours();
21891                 minutes = existingDate.getMinutes();
21892                 seconds = existingDate.getSeconds();
21893                 milliseconds = existingDate.getMilliseconds();
21894               }
21895
21896               return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21897             }
21898           }
21899
21900           return NaN;
21901         }
21902
21903         function createDateParser(regexp, mapping) {
21904           return function(iso, date) {
21905             var parts, map;
21906
21907             if (isDate(iso)) {
21908               return iso;
21909             }
21910
21911             if (isString(iso)) {
21912               // When a date is JSON'ified to wraps itself inside of an extra
21913               // set of double quotes. This makes the date parsing code unable
21914               // to match the date string and parse it as a date.
21915               if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21916                 iso = iso.substring(1, iso.length - 1);
21917               }
21918               if (ISO_DATE_REGEXP.test(iso)) {
21919                 return new Date(iso);
21920               }
21921               regexp.lastIndex = 0;
21922               parts = regexp.exec(iso);
21923
21924               if (parts) {
21925                 parts.shift();
21926                 if (date) {
21927                   map = {
21928                     yyyy: date.getFullYear(),
21929                     MM: date.getMonth() + 1,
21930                     dd: date.getDate(),
21931                     HH: date.getHours(),
21932                     mm: date.getMinutes(),
21933                     ss: date.getSeconds(),
21934                     sss: date.getMilliseconds() / 1000
21935                   };
21936                 } else {
21937                   map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21938                 }
21939
21940                 forEach(parts, function(part, index) {
21941                   if (index < mapping.length) {
21942                     map[mapping[index]] = +part;
21943                   }
21944                 });
21945                 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21946               }
21947             }
21948
21949             return NaN;
21950           };
21951         }
21952
21953         function createDateInputType(type, regexp, parseDate, format) {
21954           return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21955             badInputChecker(scope, element, attr, ctrl);
21956             baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21957             var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21958             var previousDate;
21959
21960             ctrl.$$parserName = type;
21961             ctrl.$parsers.push(function(value) {
21962               if (ctrl.$isEmpty(value)) return null;
21963               if (regexp.test(value)) {
21964                 // Note: We cannot read ctrl.$modelValue, as there might be a different
21965                 // parser/formatter in the processing chain so that the model
21966                 // contains some different data format!
21967                 var parsedDate = parseDate(value, previousDate);
21968                 if (timezone) {
21969                   parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21970                 }
21971                 return parsedDate;
21972               }
21973               return undefined;
21974             });
21975
21976             ctrl.$formatters.push(function(value) {
21977               if (value && !isDate(value)) {
21978                 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21979               }
21980               if (isValidDate(value)) {
21981                 previousDate = value;
21982                 if (previousDate && timezone) {
21983                   previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21984                 }
21985                 return $filter('date')(value, format, timezone);
21986               } else {
21987                 previousDate = null;
21988                 return '';
21989               }
21990             });
21991
21992             if (isDefined(attr.min) || attr.ngMin) {
21993               var minVal;
21994               ctrl.$validators.min = function(value) {
21995                 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21996               };
21997               attr.$observe('min', function(val) {
21998                 minVal = parseObservedDateValue(val);
21999                 ctrl.$validate();
22000               });
22001             }
22002
22003             if (isDefined(attr.max) || attr.ngMax) {
22004               var maxVal;
22005               ctrl.$validators.max = function(value) {
22006                 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22007               };
22008               attr.$observe('max', function(val) {
22009                 maxVal = parseObservedDateValue(val);
22010                 ctrl.$validate();
22011               });
22012             }
22013
22014             function isValidDate(value) {
22015               // Invalid Date: getTime() returns NaN
22016               return value && !(value.getTime && value.getTime() !== value.getTime());
22017             }
22018
22019             function parseObservedDateValue(val) {
22020               return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22021             }
22022           };
22023         }
22024
22025         function badInputChecker(scope, element, attr, ctrl) {
22026           var node = element[0];
22027           var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22028           if (nativeValidation) {
22029             ctrl.$parsers.push(function(value) {
22030               var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22031               // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22032               // - also sets validity.badInput (should only be validity.typeMismatch).
22033               // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22034               // - can ignore this case as we can still read out the erroneous email...
22035               return validity.badInput && !validity.typeMismatch ? undefined : value;
22036             });
22037           }
22038         }
22039
22040         function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22041           badInputChecker(scope, element, attr, ctrl);
22042           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22043
22044           ctrl.$$parserName = 'number';
22045           ctrl.$parsers.push(function(value) {
22046             if (ctrl.$isEmpty(value))      return null;
22047             if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22048             return undefined;
22049           });
22050
22051           ctrl.$formatters.push(function(value) {
22052             if (!ctrl.$isEmpty(value)) {
22053               if (!isNumber(value)) {
22054                 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22055               }
22056               value = value.toString();
22057             }
22058             return value;
22059           });
22060
22061           if (isDefined(attr.min) || attr.ngMin) {
22062             var minVal;
22063             ctrl.$validators.min = function(value) {
22064               return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22065             };
22066
22067             attr.$observe('min', function(val) {
22068               if (isDefined(val) && !isNumber(val)) {
22069                 val = parseFloat(val, 10);
22070               }
22071               minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22072               // TODO(matsko): implement validateLater to reduce number of validations
22073               ctrl.$validate();
22074             });
22075           }
22076
22077           if (isDefined(attr.max) || attr.ngMax) {
22078             var maxVal;
22079             ctrl.$validators.max = function(value) {
22080               return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22081             };
22082
22083             attr.$observe('max', function(val) {
22084               if (isDefined(val) && !isNumber(val)) {
22085                 val = parseFloat(val, 10);
22086               }
22087               maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22088               // TODO(matsko): implement validateLater to reduce number of validations
22089               ctrl.$validate();
22090             });
22091           }
22092         }
22093
22094         function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22095           // Note: no badInputChecker here by purpose as `url` is only a validation
22096           // in browsers, i.e. we can always read out input.value even if it is not valid!
22097           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22098           stringBasedInputType(ctrl);
22099
22100           ctrl.$$parserName = 'url';
22101           ctrl.$validators.url = function(modelValue, viewValue) {
22102             var value = modelValue || viewValue;
22103             return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22104           };
22105         }
22106
22107         function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22108           // Note: no badInputChecker here by purpose as `url` is only a validation
22109           // in browsers, i.e. we can always read out input.value even if it is not valid!
22110           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22111           stringBasedInputType(ctrl);
22112
22113           ctrl.$$parserName = 'email';
22114           ctrl.$validators.email = function(modelValue, viewValue) {
22115             var value = modelValue || viewValue;
22116             return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22117           };
22118         }
22119
22120         function radioInputType(scope, element, attr, ctrl) {
22121           // make the name unique, if not defined
22122           if (isUndefined(attr.name)) {
22123             element.attr('name', nextUid());
22124           }
22125
22126           var listener = function(ev) {
22127             if (element[0].checked) {
22128               ctrl.$setViewValue(attr.value, ev && ev.type);
22129             }
22130           };
22131
22132           element.on('click', listener);
22133
22134           ctrl.$render = function() {
22135             var value = attr.value;
22136             element[0].checked = (value == ctrl.$viewValue);
22137           };
22138
22139           attr.$observe('value', ctrl.$render);
22140         }
22141
22142         function parseConstantExpr($parse, context, name, expression, fallback) {
22143           var parseFn;
22144           if (isDefined(expression)) {
22145             parseFn = $parse(expression);
22146             if (!parseFn.constant) {
22147               throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22148                                            '`{1}`.', name, expression);
22149             }
22150             return parseFn(context);
22151           }
22152           return fallback;
22153         }
22154
22155         function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22156           var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22157           var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22158
22159           var listener = function(ev) {
22160             ctrl.$setViewValue(element[0].checked, ev && ev.type);
22161           };
22162
22163           element.on('click', listener);
22164
22165           ctrl.$render = function() {
22166             element[0].checked = ctrl.$viewValue;
22167           };
22168
22169           // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22170           // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22171           // it to a boolean.
22172           ctrl.$isEmpty = function(value) {
22173             return value === false;
22174           };
22175
22176           ctrl.$formatters.push(function(value) {
22177             return equals(value, trueValue);
22178           });
22179
22180           ctrl.$parsers.push(function(value) {
22181             return value ? trueValue : falseValue;
22182           });
22183         }
22184
22185
22186         /**
22187          * @ngdoc directive
22188          * @name textarea
22189          * @restrict E
22190          *
22191          * @description
22192          * HTML textarea element control with angular data-binding. The data-binding and validation
22193          * properties of this element are exactly the same as those of the
22194          * {@link ng.directive:input input element}.
22195          *
22196          * @param {string} ngModel Assignable angular expression to data-bind to.
22197          * @param {string=} name Property name of the form under which the control is published.
22198          * @param {string=} required Sets `required` validation error key if the value is not entered.
22199          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22200          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22201          *    `required` when you want to data-bind to the `required` attribute.
22202          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22203          *    minlength.
22204          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22205          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22206          *    length.
22207          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22208          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22209          *    If the expression evaluates to a RegExp object, then this is used directly.
22210          *    If the expression evaluates to a string, then it will be converted to a RegExp
22211          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22212          *    `new RegExp('^abc$')`.<br />
22213          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22214          *    start at the index of the last search's match, thus not taking the whole input value into
22215          *    account.
22216          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22217          *    interaction with the input element.
22218          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22219          */
22220
22221
22222         /**
22223          * @ngdoc directive
22224          * @name input
22225          * @restrict E
22226          *
22227          * @description
22228          * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22229          * input state control, and validation.
22230          * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22231          *
22232          * <div class="alert alert-warning">
22233          * **Note:** Not every feature offered is available for all input types.
22234          * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22235          * </div>
22236          *
22237          * @param {string} ngModel Assignable angular expression to data-bind to.
22238          * @param {string=} name Property name of the form under which the control is published.
22239          * @param {string=} required Sets `required` validation error key if the value is not entered.
22240          * @param {boolean=} ngRequired Sets `required` attribute if set to true
22241          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22242          *    minlength.
22243          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22244          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22245          *    length.
22246          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22247          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22248          *    If the expression evaluates to a RegExp object, then this is used directly.
22249          *    If the expression evaluates to a string, then it will be converted to a RegExp
22250          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22251          *    `new RegExp('^abc$')`.<br />
22252          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22253          *    start at the index of the last search's match, thus not taking the whole input value into
22254          *    account.
22255          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22256          *    interaction with the input element.
22257          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22258          *    This parameter is ignored for input[type=password] controls, which will never trim the
22259          *    input.
22260          *
22261          * @example
22262             <example name="input-directive" module="inputExample">
22263               <file name="index.html">
22264                <script>
22265                   angular.module('inputExample', [])
22266                     .controller('ExampleController', ['$scope', function($scope) {
22267                       $scope.user = {name: 'guest', last: 'visitor'};
22268                     }]);
22269                </script>
22270                <div ng-controller="ExampleController">
22271                  <form name="myForm">
22272                    <label>
22273                       User name:
22274                       <input type="text" name="userName" ng-model="user.name" required>
22275                    </label>
22276                    <div role="alert">
22277                      <span class="error" ng-show="myForm.userName.$error.required">
22278                       Required!</span>
22279                    </div>
22280                    <label>
22281                       Last name:
22282                       <input type="text" name="lastName" ng-model="user.last"
22283                       ng-minlength="3" ng-maxlength="10">
22284                    </label>
22285                    <div role="alert">
22286                      <span class="error" ng-show="myForm.lastName.$error.minlength">
22287                        Too short!</span>
22288                      <span class="error" ng-show="myForm.lastName.$error.maxlength">
22289                        Too long!</span>
22290                    </div>
22291                  </form>
22292                  <hr>
22293                  <tt>user = {{user}}</tt><br/>
22294                  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22295                  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22296                  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22297                  <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22298                  <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22299                  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22300                  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22301                  <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22302                </div>
22303               </file>
22304               <file name="protractor.js" type="protractor">
22305                 var user = element(by.exactBinding('user'));
22306                 var userNameValid = element(by.binding('myForm.userName.$valid'));
22307                 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22308                 var lastNameError = element(by.binding('myForm.lastName.$error'));
22309                 var formValid = element(by.binding('myForm.$valid'));
22310                 var userNameInput = element(by.model('user.name'));
22311                 var userLastInput = element(by.model('user.last'));
22312
22313                 it('should initialize to model', function() {
22314                   expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22315                   expect(userNameValid.getText()).toContain('true');
22316                   expect(formValid.getText()).toContain('true');
22317                 });
22318
22319                 it('should be invalid if empty when required', function() {
22320                   userNameInput.clear();
22321                   userNameInput.sendKeys('');
22322
22323                   expect(user.getText()).toContain('{"last":"visitor"}');
22324                   expect(userNameValid.getText()).toContain('false');
22325                   expect(formValid.getText()).toContain('false');
22326                 });
22327
22328                 it('should be valid if empty when min length is set', function() {
22329                   userLastInput.clear();
22330                   userLastInput.sendKeys('');
22331
22332                   expect(user.getText()).toContain('{"name":"guest","last":""}');
22333                   expect(lastNameValid.getText()).toContain('true');
22334                   expect(formValid.getText()).toContain('true');
22335                 });
22336
22337                 it('should be invalid if less than required min length', function() {
22338                   userLastInput.clear();
22339                   userLastInput.sendKeys('xx');
22340
22341                   expect(user.getText()).toContain('{"name":"guest"}');
22342                   expect(lastNameValid.getText()).toContain('false');
22343                   expect(lastNameError.getText()).toContain('minlength');
22344                   expect(formValid.getText()).toContain('false');
22345                 });
22346
22347                 it('should be invalid if longer than max length', function() {
22348                   userLastInput.clear();
22349                   userLastInput.sendKeys('some ridiculously long name');
22350
22351                   expect(user.getText()).toContain('{"name":"guest"}');
22352                   expect(lastNameValid.getText()).toContain('false');
22353                   expect(lastNameError.getText()).toContain('maxlength');
22354                   expect(formValid.getText()).toContain('false');
22355                 });
22356               </file>
22357             </example>
22358          */
22359         var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22360             function($browser, $sniffer, $filter, $parse) {
22361           return {
22362             restrict: 'E',
22363             require: ['?ngModel'],
22364             link: {
22365               pre: function(scope, element, attr, ctrls) {
22366                 if (ctrls[0]) {
22367                   (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22368                                                                       $browser, $filter, $parse);
22369                 }
22370               }
22371             }
22372           };
22373         }];
22374
22375
22376
22377         var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22378         /**
22379          * @ngdoc directive
22380          * @name ngValue
22381          *
22382          * @description
22383          * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22384          * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22385          * the bound value.
22386          *
22387          * `ngValue` is useful when dynamically generating lists of radio buttons using
22388          * {@link ngRepeat `ngRepeat`}, as shown below.
22389          *
22390          * Likewise, `ngValue` can be used to generate `<option>` elements for
22391          * the {@link select `select`} element. In that case however, only strings are supported
22392          * for the `value `attribute, so the resulting `ngModel` will always be a string.
22393          * Support for `select` models with non-string values is available via `ngOptions`.
22394          *
22395          * @element input
22396          * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22397          *   of the `input` element
22398          *
22399          * @example
22400             <example name="ngValue-directive" module="valueExample">
22401               <file name="index.html">
22402                <script>
22403                   angular.module('valueExample', [])
22404                     .controller('ExampleController', ['$scope', function($scope) {
22405                       $scope.names = ['pizza', 'unicorns', 'robots'];
22406                       $scope.my = { favorite: 'unicorns' };
22407                     }]);
22408                </script>
22409                 <form ng-controller="ExampleController">
22410                   <h2>Which is your favorite?</h2>
22411                     <label ng-repeat="name in names" for="{{name}}">
22412                       {{name}}
22413                       <input type="radio"
22414                              ng-model="my.favorite"
22415                              ng-value="name"
22416                              id="{{name}}"
22417                              name="favorite">
22418                     </label>
22419                   <div>You chose {{my.favorite}}</div>
22420                 </form>
22421               </file>
22422               <file name="protractor.js" type="protractor">
22423                 var favorite = element(by.binding('my.favorite'));
22424
22425                 it('should initialize to model', function() {
22426                   expect(favorite.getText()).toContain('unicorns');
22427                 });
22428                 it('should bind the values to the inputs', function() {
22429                   element.all(by.model('my.favorite')).get(0).click();
22430                   expect(favorite.getText()).toContain('pizza');
22431                 });
22432               </file>
22433             </example>
22434          */
22435         var ngValueDirective = function() {
22436           return {
22437             restrict: 'A',
22438             priority: 100,
22439             compile: function(tpl, tplAttr) {
22440               if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22441                 return function ngValueConstantLink(scope, elm, attr) {
22442                   attr.$set('value', scope.$eval(attr.ngValue));
22443                 };
22444               } else {
22445                 return function ngValueLink(scope, elm, attr) {
22446                   scope.$watch(attr.ngValue, function valueWatchAction(value) {
22447                     attr.$set('value', value);
22448                   });
22449                 };
22450               }
22451             }
22452           };
22453         };
22454
22455         /**
22456          * @ngdoc directive
22457          * @name ngBind
22458          * @restrict AC
22459          *
22460          * @description
22461          * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22462          * with the value of a given expression, and to update the text content when the value of that
22463          * expression changes.
22464          *
22465          * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22466          * `{{ expression }}` which is similar but less verbose.
22467          *
22468          * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22469          * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22470          * element attribute, it makes the bindings invisible to the user while the page is loading.
22471          *
22472          * An alternative solution to this problem would be using the
22473          * {@link ng.directive:ngCloak ngCloak} directive.
22474          *
22475          *
22476          * @element ANY
22477          * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22478          *
22479          * @example
22480          * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22481            <example module="bindExample">
22482              <file name="index.html">
22483                <script>
22484                  angular.module('bindExample', [])
22485                    .controller('ExampleController', ['$scope', function($scope) {
22486                      $scope.name = 'Whirled';
22487                    }]);
22488                </script>
22489                <div ng-controller="ExampleController">
22490                  <label>Enter name: <input type="text" ng-model="name"></label><br>
22491                  Hello <span ng-bind="name"></span>!
22492                </div>
22493              </file>
22494              <file name="protractor.js" type="protractor">
22495                it('should check ng-bind', function() {
22496                  var nameInput = element(by.model('name'));
22497
22498                  expect(element(by.binding('name')).getText()).toBe('Whirled');
22499                  nameInput.clear();
22500                  nameInput.sendKeys('world');
22501                  expect(element(by.binding('name')).getText()).toBe('world');
22502                });
22503              </file>
22504            </example>
22505          */
22506         var ngBindDirective = ['$compile', function($compile) {
22507           return {
22508             restrict: 'AC',
22509             compile: function ngBindCompile(templateElement) {
22510               $compile.$$addBindingClass(templateElement);
22511               return function ngBindLink(scope, element, attr) {
22512                 $compile.$$addBindingInfo(element, attr.ngBind);
22513                 element = element[0];
22514                 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22515                   element.textContent = isUndefined(value) ? '' : value;
22516                 });
22517               };
22518             }
22519           };
22520         }];
22521
22522
22523         /**
22524          * @ngdoc directive
22525          * @name ngBindTemplate
22526          *
22527          * @description
22528          * The `ngBindTemplate` directive specifies that the element
22529          * text content should be replaced with the interpolation of the template
22530          * in the `ngBindTemplate` attribute.
22531          * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22532          * expressions. This directive is needed since some HTML elements
22533          * (such as TITLE and OPTION) cannot contain SPAN elements.
22534          *
22535          * @element ANY
22536          * @param {string} ngBindTemplate template of form
22537          *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22538          *
22539          * @example
22540          * Try it here: enter text in text box and watch the greeting change.
22541            <example module="bindExample">
22542              <file name="index.html">
22543                <script>
22544                  angular.module('bindExample', [])
22545                    .controller('ExampleController', ['$scope', function($scope) {
22546                      $scope.salutation = 'Hello';
22547                      $scope.name = 'World';
22548                    }]);
22549                </script>
22550                <div ng-controller="ExampleController">
22551                 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22552                 <label>Name: <input type="text" ng-model="name"></label><br>
22553                 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22554                </div>
22555              </file>
22556              <file name="protractor.js" type="protractor">
22557                it('should check ng-bind', function() {
22558                  var salutationElem = element(by.binding('salutation'));
22559                  var salutationInput = element(by.model('salutation'));
22560                  var nameInput = element(by.model('name'));
22561
22562                  expect(salutationElem.getText()).toBe('Hello World!');
22563
22564                  salutationInput.clear();
22565                  salutationInput.sendKeys('Greetings');
22566                  nameInput.clear();
22567                  nameInput.sendKeys('user');
22568
22569                  expect(salutationElem.getText()).toBe('Greetings user!');
22570                });
22571              </file>
22572            </example>
22573          */
22574         var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22575           return {
22576             compile: function ngBindTemplateCompile(templateElement) {
22577               $compile.$$addBindingClass(templateElement);
22578               return function ngBindTemplateLink(scope, element, attr) {
22579                 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22580                 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22581                 element = element[0];
22582                 attr.$observe('ngBindTemplate', function(value) {
22583                   element.textContent = isUndefined(value) ? '' : value;
22584                 });
22585               };
22586             }
22587           };
22588         }];
22589
22590
22591         /**
22592          * @ngdoc directive
22593          * @name ngBindHtml
22594          *
22595          * @description
22596          * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22597          * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22598          * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22599          * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22600          * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22601          *
22602          * You may also bypass sanitization for values you know are safe. To do so, bind to
22603          * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22604          * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22605          *
22606          * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22607          * will have an exception (instead of an exploit.)
22608          *
22609          * @element ANY
22610          * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22611          *
22612          * @example
22613
22614            <example module="bindHtmlExample" deps="angular-sanitize.js">
22615              <file name="index.html">
22616                <div ng-controller="ExampleController">
22617                 <p ng-bind-html="myHTML"></p>
22618                </div>
22619              </file>
22620
22621              <file name="script.js">
22622                angular.module('bindHtmlExample', ['ngSanitize'])
22623                  .controller('ExampleController', ['$scope', function($scope) {
22624                    $scope.myHTML =
22625                       'I am an <code>HTML</code>string with ' +
22626                       '<a href="#">links!</a> and other <em>stuff</em>';
22627                  }]);
22628              </file>
22629
22630              <file name="protractor.js" type="protractor">
22631                it('should check ng-bind-html', function() {
22632                  expect(element(by.binding('myHTML')).getText()).toBe(
22633                      'I am an HTMLstring with links! and other stuff');
22634                });
22635              </file>
22636            </example>
22637          */
22638         var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22639           return {
22640             restrict: 'A',
22641             compile: function ngBindHtmlCompile(tElement, tAttrs) {
22642               var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22643               var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22644                 return (value || '').toString();
22645               });
22646               $compile.$$addBindingClass(tElement);
22647
22648               return function ngBindHtmlLink(scope, element, attr) {
22649                 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22650
22651                 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22652                   // we re-evaluate the expr because we want a TrustedValueHolderType
22653                   // for $sce, not a string
22654                   element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22655                 });
22656               };
22657             }
22658           };
22659         }];
22660
22661         /**
22662          * @ngdoc directive
22663          * @name ngChange
22664          *
22665          * @description
22666          * Evaluate the given expression when the user changes the input.
22667          * The expression is evaluated immediately, unlike the JavaScript onchange event
22668          * which only triggers at the end of a change (usually, when the user leaves the
22669          * form element or presses the return key).
22670          *
22671          * The `ngChange` expression is only evaluated when a change in the input value causes
22672          * a new value to be committed to the model.
22673          *
22674          * It will not be evaluated:
22675          * * if the value returned from the `$parsers` transformation pipeline has not changed
22676          * * if the input has continued to be invalid since the model will stay `null`
22677          * * if the model is changed programmatically and not by a change to the input value
22678          *
22679          *
22680          * Note, this directive requires `ngModel` to be present.
22681          *
22682          * @element input
22683          * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22684          * in input value.
22685          *
22686          * @example
22687          * <example name="ngChange-directive" module="changeExample">
22688          *   <file name="index.html">
22689          *     <script>
22690          *       angular.module('changeExample', [])
22691          *         .controller('ExampleController', ['$scope', function($scope) {
22692          *           $scope.counter = 0;
22693          *           $scope.change = function() {
22694          *             $scope.counter++;
22695          *           };
22696          *         }]);
22697          *     </script>
22698          *     <div ng-controller="ExampleController">
22699          *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22700          *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22701          *       <label for="ng-change-example2">Confirmed</label><br />
22702          *       <tt>debug = {{confirmed}}</tt><br/>
22703          *       <tt>counter = {{counter}}</tt><br/>
22704          *     </div>
22705          *   </file>
22706          *   <file name="protractor.js" type="protractor">
22707          *     var counter = element(by.binding('counter'));
22708          *     var debug = element(by.binding('confirmed'));
22709          *
22710          *     it('should evaluate the expression if changing from view', function() {
22711          *       expect(counter.getText()).toContain('0');
22712          *
22713          *       element(by.id('ng-change-example1')).click();
22714          *
22715          *       expect(counter.getText()).toContain('1');
22716          *       expect(debug.getText()).toContain('true');
22717          *     });
22718          *
22719          *     it('should not evaluate the expression if changing from model', function() {
22720          *       element(by.id('ng-change-example2')).click();
22721
22722          *       expect(counter.getText()).toContain('0');
22723          *       expect(debug.getText()).toContain('true');
22724          *     });
22725          *   </file>
22726          * </example>
22727          */
22728         var ngChangeDirective = valueFn({
22729           restrict: 'A',
22730           require: 'ngModel',
22731           link: function(scope, element, attr, ctrl) {
22732             ctrl.$viewChangeListeners.push(function() {
22733               scope.$eval(attr.ngChange);
22734             });
22735           }
22736         });
22737
22738         function classDirective(name, selector) {
22739           name = 'ngClass' + name;
22740           return ['$animate', function($animate) {
22741             return {
22742               restrict: 'AC',
22743               link: function(scope, element, attr) {
22744                 var oldVal;
22745
22746                 scope.$watch(attr[name], ngClassWatchAction, true);
22747
22748                 attr.$observe('class', function(value) {
22749                   ngClassWatchAction(scope.$eval(attr[name]));
22750                 });
22751
22752
22753                 if (name !== 'ngClass') {
22754                   scope.$watch('$index', function($index, old$index) {
22755                     // jshint bitwise: false
22756                     var mod = $index & 1;
22757                     if (mod !== (old$index & 1)) {
22758                       var classes = arrayClasses(scope.$eval(attr[name]));
22759                       mod === selector ?
22760                         addClasses(classes) :
22761                         removeClasses(classes);
22762                     }
22763                   });
22764                 }
22765
22766                 function addClasses(classes) {
22767                   var newClasses = digestClassCounts(classes, 1);
22768                   attr.$addClass(newClasses);
22769                 }
22770
22771                 function removeClasses(classes) {
22772                   var newClasses = digestClassCounts(classes, -1);
22773                   attr.$removeClass(newClasses);
22774                 }
22775
22776                 function digestClassCounts(classes, count) {
22777                   // Use createMap() to prevent class assumptions involving property
22778                   // names in Object.prototype
22779                   var classCounts = element.data('$classCounts') || createMap();
22780                   var classesToUpdate = [];
22781                   forEach(classes, function(className) {
22782                     if (count > 0 || classCounts[className]) {
22783                       classCounts[className] = (classCounts[className] || 0) + count;
22784                       if (classCounts[className] === +(count > 0)) {
22785                         classesToUpdate.push(className);
22786                       }
22787                     }
22788                   });
22789                   element.data('$classCounts', classCounts);
22790                   return classesToUpdate.join(' ');
22791                 }
22792
22793                 function updateClasses(oldClasses, newClasses) {
22794                   var toAdd = arrayDifference(newClasses, oldClasses);
22795                   var toRemove = arrayDifference(oldClasses, newClasses);
22796                   toAdd = digestClassCounts(toAdd, 1);
22797                   toRemove = digestClassCounts(toRemove, -1);
22798                   if (toAdd && toAdd.length) {
22799                     $animate.addClass(element, toAdd);
22800                   }
22801                   if (toRemove && toRemove.length) {
22802                     $animate.removeClass(element, toRemove);
22803                   }
22804                 }
22805
22806                 function ngClassWatchAction(newVal) {
22807                   if (selector === true || scope.$index % 2 === selector) {
22808                     var newClasses = arrayClasses(newVal || []);
22809                     if (!oldVal) {
22810                       addClasses(newClasses);
22811                     } else if (!equals(newVal,oldVal)) {
22812                       var oldClasses = arrayClasses(oldVal);
22813                       updateClasses(oldClasses, newClasses);
22814                     }
22815                   }
22816                   oldVal = shallowCopy(newVal);
22817                 }
22818               }
22819             };
22820
22821             function arrayDifference(tokens1, tokens2) {
22822               var values = [];
22823
22824               outer:
22825               for (var i = 0; i < tokens1.length; i++) {
22826                 var token = tokens1[i];
22827                 for (var j = 0; j < tokens2.length; j++) {
22828                   if (token == tokens2[j]) continue outer;
22829                 }
22830                 values.push(token);
22831               }
22832               return values;
22833             }
22834
22835             function arrayClasses(classVal) {
22836               var classes = [];
22837               if (isArray(classVal)) {
22838                 forEach(classVal, function(v) {
22839                   classes = classes.concat(arrayClasses(v));
22840                 });
22841                 return classes;
22842               } else if (isString(classVal)) {
22843                 return classVal.split(' ');
22844               } else if (isObject(classVal)) {
22845                 forEach(classVal, function(v, k) {
22846                   if (v) {
22847                     classes = classes.concat(k.split(' '));
22848                   }
22849                 });
22850                 return classes;
22851               }
22852               return classVal;
22853             }
22854           }];
22855         }
22856
22857         /**
22858          * @ngdoc directive
22859          * @name ngClass
22860          * @restrict AC
22861          *
22862          * @description
22863          * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22864          * an expression that represents all classes to be added.
22865          *
22866          * The directive operates in three different ways, depending on which of three types the expression
22867          * evaluates to:
22868          *
22869          * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22870          * names.
22871          *
22872          * 2. If the expression evaluates to an object, then for each key-value pair of the
22873          * object with a truthy value the corresponding key is used as a class name.
22874          *
22875          * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22876          * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22877          * to give you more control over what CSS classes appear. See the code below for an example of this.
22878          *
22879          *
22880          * The directive won't add duplicate classes if a particular class was already set.
22881          *
22882          * When the expression changes, the previously added classes are removed and only then are the
22883          * new classes added.
22884          *
22885          * @animations
22886          * **add** - happens just before the class is applied to the elements
22887          *
22888          * **remove** - happens just before the class is removed from the element
22889          *
22890          * @element ANY
22891          * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22892          *   of the evaluation can be a string representing space delimited class
22893          *   names, an array, or a map of class names to boolean values. In the case of a map, the
22894          *   names of the properties whose values are truthy will be added as css classes to the
22895          *   element.
22896          *
22897          * @example Example that demonstrates basic bindings via ngClass directive.
22898            <example>
22899              <file name="index.html">
22900                <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22901                <label>
22902                   <input type="checkbox" ng-model="deleted">
22903                   deleted (apply "strike" class)
22904                </label><br>
22905                <label>
22906                   <input type="checkbox" ng-model="important">
22907                   important (apply "bold" class)
22908                </label><br>
22909                <label>
22910                   <input type="checkbox" ng-model="error">
22911                   error (apply "has-error" class)
22912                </label>
22913                <hr>
22914                <p ng-class="style">Using String Syntax</p>
22915                <input type="text" ng-model="style"
22916                       placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22917                <hr>
22918                <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22919                <input ng-model="style1"
22920                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22921                <input ng-model="style2"
22922                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22923                <input ng-model="style3"
22924                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22925                <hr>
22926                <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22927                <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22928                <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22929              </file>
22930              <file name="style.css">
22931                .strike {
22932                    text-decoration: line-through;
22933                }
22934                .bold {
22935                    font-weight: bold;
22936                }
22937                .red {
22938                    color: red;
22939                }
22940                .has-error {
22941                    color: red;
22942                    background-color: yellow;
22943                }
22944                .orange {
22945                    color: orange;
22946                }
22947              </file>
22948              <file name="protractor.js" type="protractor">
22949                var ps = element.all(by.css('p'));
22950
22951                it('should let you toggle the class', function() {
22952
22953                  expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22954                  expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22955
22956                  element(by.model('important')).click();
22957                  expect(ps.first().getAttribute('class')).toMatch(/bold/);
22958
22959                  element(by.model('error')).click();
22960                  expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22961                });
22962
22963                it('should let you toggle string example', function() {
22964                  expect(ps.get(1).getAttribute('class')).toBe('');
22965                  element(by.model('style')).clear();
22966                  element(by.model('style')).sendKeys('red');
22967                  expect(ps.get(1).getAttribute('class')).toBe('red');
22968                });
22969
22970                it('array example should have 3 classes', function() {
22971                  expect(ps.get(2).getAttribute('class')).toBe('');
22972                  element(by.model('style1')).sendKeys('bold');
22973                  element(by.model('style2')).sendKeys('strike');
22974                  element(by.model('style3')).sendKeys('red');
22975                  expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22976                });
22977
22978                it('array with map example should have 2 classes', function() {
22979                  expect(ps.last().getAttribute('class')).toBe('');
22980                  element(by.model('style4')).sendKeys('bold');
22981                  element(by.model('warning')).click();
22982                  expect(ps.last().getAttribute('class')).toBe('bold orange');
22983                });
22984              </file>
22985            </example>
22986
22987            ## Animations
22988
22989            The example below demonstrates how to perform animations using ngClass.
22990
22991            <example module="ngAnimate" deps="angular-animate.js" animations="true">
22992              <file name="index.html">
22993               <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22994               <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22995               <br>
22996               <span class="base-class" ng-class="myVar">Sample Text</span>
22997              </file>
22998              <file name="style.css">
22999                .base-class {
23000                  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23001                }
23002
23003                .base-class.my-class {
23004                  color: red;
23005                  font-size:3em;
23006                }
23007              </file>
23008              <file name="protractor.js" type="protractor">
23009                it('should check ng-class', function() {
23010                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23011                    toMatch(/my-class/);
23012
23013                  element(by.id('setbtn')).click();
23014
23015                  expect(element(by.css('.base-class')).getAttribute('class')).
23016                    toMatch(/my-class/);
23017
23018                  element(by.id('clearbtn')).click();
23019
23020                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23021                    toMatch(/my-class/);
23022                });
23023              </file>
23024            </example>
23025
23026
23027            ## ngClass and pre-existing CSS3 Transitions/Animations
23028            The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23029            Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23030            any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23031            to view the step by step details of {@link $animate#addClass $animate.addClass} and
23032            {@link $animate#removeClass $animate.removeClass}.
23033          */
23034         var ngClassDirective = classDirective('', true);
23035
23036         /**
23037          * @ngdoc directive
23038          * @name ngClassOdd
23039          * @restrict AC
23040          *
23041          * @description
23042          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23043          * {@link ng.directive:ngClass ngClass}, except they work in
23044          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23045          *
23046          * This directive can be applied only within the scope of an
23047          * {@link ng.directive:ngRepeat ngRepeat}.
23048          *
23049          * @element ANY
23050          * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23051          *   of the evaluation can be a string representing space delimited class names or an array.
23052          *
23053          * @example
23054            <example>
23055              <file name="index.html">
23056                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23057                   <li ng-repeat="name in names">
23058                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23059                      {{name}}
23060                    </span>
23061                   </li>
23062                 </ol>
23063              </file>
23064              <file name="style.css">
23065                .odd {
23066                  color: red;
23067                }
23068                .even {
23069                  color: blue;
23070                }
23071              </file>
23072              <file name="protractor.js" type="protractor">
23073                it('should check ng-class-odd and ng-class-even', function() {
23074                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23075                    toMatch(/odd/);
23076                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23077                    toMatch(/even/);
23078                });
23079              </file>
23080            </example>
23081          */
23082         var ngClassOddDirective = classDirective('Odd', 0);
23083
23084         /**
23085          * @ngdoc directive
23086          * @name ngClassEven
23087          * @restrict AC
23088          *
23089          * @description
23090          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23091          * {@link ng.directive:ngClass ngClass}, except they work in
23092          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23093          *
23094          * This directive can be applied only within the scope of an
23095          * {@link ng.directive:ngRepeat ngRepeat}.
23096          *
23097          * @element ANY
23098          * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23099          *   result of the evaluation can be a string representing space delimited class names or an array.
23100          *
23101          * @example
23102            <example>
23103              <file name="index.html">
23104                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23105                   <li ng-repeat="name in names">
23106                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23107                      {{name}} &nbsp; &nbsp; &nbsp;
23108                    </span>
23109                   </li>
23110                 </ol>
23111              </file>
23112              <file name="style.css">
23113                .odd {
23114                  color: red;
23115                }
23116                .even {
23117                  color: blue;
23118                }
23119              </file>
23120              <file name="protractor.js" type="protractor">
23121                it('should check ng-class-odd and ng-class-even', function() {
23122                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23123                    toMatch(/odd/);
23124                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23125                    toMatch(/even/);
23126                });
23127              </file>
23128            </example>
23129          */
23130         var ngClassEvenDirective = classDirective('Even', 1);
23131
23132         /**
23133          * @ngdoc directive
23134          * @name ngCloak
23135          * @restrict AC
23136          *
23137          * @description
23138          * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23139          * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23140          * directive to avoid the undesirable flicker effect caused by the html template display.
23141          *
23142          * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23143          * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23144          * of the browser view.
23145          *
23146          * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23147          * `angular.min.js`.
23148          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23149          *
23150          * ```css
23151          * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23152          *   display: none !important;
23153          * }
23154          * ```
23155          *
23156          * When this css rule is loaded by the browser, all html elements (including their children) that
23157          * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23158          * during the compilation of the template it deletes the `ngCloak` element attribute, making
23159          * the compiled element visible.
23160          *
23161          * For the best result, the `angular.js` script must be loaded in the head section of the html
23162          * document; alternatively, the css rule above must be included in the external stylesheet of the
23163          * application.
23164          *
23165          * @element ANY
23166          *
23167          * @example
23168            <example>
23169              <file name="index.html">
23170                 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23171                 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23172              </file>
23173              <file name="protractor.js" type="protractor">
23174                it('should remove the template directive and css class', function() {
23175                  expect($('#template1').getAttribute('ng-cloak')).
23176                    toBeNull();
23177                  expect($('#template2').getAttribute('ng-cloak')).
23178                    toBeNull();
23179                });
23180              </file>
23181            </example>
23182          *
23183          */
23184         var ngCloakDirective = ngDirective({
23185           compile: function(element, attr) {
23186             attr.$set('ngCloak', undefined);
23187             element.removeClass('ng-cloak');
23188           }
23189         });
23190
23191         /**
23192          * @ngdoc directive
23193          * @name ngController
23194          *
23195          * @description
23196          * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23197          * supports the principles behind the Model-View-Controller design pattern.
23198          *
23199          * MVC components in angular:
23200          *
23201          * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23202          *   are accessed through bindings.
23203          * * View — The template (HTML with data bindings) that is rendered into the View.
23204          * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23205          *   logic behind the application to decorate the scope with functions and values
23206          *
23207          * Note that you can also attach controllers to the DOM by declaring it in a route definition
23208          * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23209          * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23210          * and executed twice.
23211          *
23212          * @element ANY
23213          * @scope
23214          * @priority 500
23215          * @param {expression} ngController Name of a constructor function registered with the current
23216          * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23217          * that on the current scope evaluates to a constructor function.
23218          *
23219          * The controller instance can be published into a scope property by specifying
23220          * `ng-controller="as propertyName"`.
23221          *
23222          * If the current `$controllerProvider` is configured to use globals (via
23223          * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23224          * also be the name of a globally accessible constructor function (not recommended).
23225          *
23226          * @example
23227          * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23228          * greeting are methods declared on the controller (see source tab). These methods can
23229          * easily be called from the angular markup. Any changes to the data are automatically reflected
23230          * in the View without the need for a manual update.
23231          *
23232          * Two different declaration styles are included below:
23233          *
23234          * * one binds methods and properties directly onto the controller using `this`:
23235          * `ng-controller="SettingsController1 as settings"`
23236          * * one injects `$scope` into the controller:
23237          * `ng-controller="SettingsController2"`
23238          *
23239          * The second option is more common in the Angular community, and is generally used in boilerplates
23240          * and in this guide. However, there are advantages to binding properties directly to the controller
23241          * and avoiding scope.
23242          *
23243          * * Using `controller as` makes it obvious which controller you are accessing in the template when
23244          * multiple controllers apply to an element.
23245          * * If you are writing your controllers as classes you have easier access to the properties and
23246          * methods, which will appear on the scope, from inside the controller code.
23247          * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23248          * inheritance masking primitives.
23249          *
23250          * This example demonstrates the `controller as` syntax.
23251          *
23252          * <example name="ngControllerAs" module="controllerAsExample">
23253          *   <file name="index.html">
23254          *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23255          *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23256          *      <button ng-click="settings.greet()">greet</button><br/>
23257          *      Contact:
23258          *      <ul>
23259          *        <li ng-repeat="contact in settings.contacts">
23260          *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23261          *             <option>phone</option>
23262          *             <option>email</option>
23263          *          </select>
23264          *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23265          *          <button ng-click="settings.clearContact(contact)">clear</button>
23266          *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23267          *        </li>
23268          *        <li><button ng-click="settings.addContact()">add</button></li>
23269          *     </ul>
23270          *    </div>
23271          *   </file>
23272          *   <file name="app.js">
23273          *    angular.module('controllerAsExample', [])
23274          *      .controller('SettingsController1', SettingsController1);
23275          *
23276          *    function SettingsController1() {
23277          *      this.name = "John Smith";
23278          *      this.contacts = [
23279          *        {type: 'phone', value: '408 555 1212'},
23280          *        {type: 'email', value: 'john.smith@example.org'} ];
23281          *    }
23282          *
23283          *    SettingsController1.prototype.greet = function() {
23284          *      alert(this.name);
23285          *    };
23286          *
23287          *    SettingsController1.prototype.addContact = function() {
23288          *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
23289          *    };
23290          *
23291          *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23292          *     var index = this.contacts.indexOf(contactToRemove);
23293          *      this.contacts.splice(index, 1);
23294          *    };
23295          *
23296          *    SettingsController1.prototype.clearContact = function(contact) {
23297          *      contact.type = 'phone';
23298          *      contact.value = '';
23299          *    };
23300          *   </file>
23301          *   <file name="protractor.js" type="protractor">
23302          *     it('should check controller as', function() {
23303          *       var container = element(by.id('ctrl-as-exmpl'));
23304          *         expect(container.element(by.model('settings.name'))
23305          *           .getAttribute('value')).toBe('John Smith');
23306          *
23307          *       var firstRepeat =
23308          *           container.element(by.repeater('contact in settings.contacts').row(0));
23309          *       var secondRepeat =
23310          *           container.element(by.repeater('contact in settings.contacts').row(1));
23311          *
23312          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23313          *           .toBe('408 555 1212');
23314          *
23315          *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23316          *           .toBe('john.smith@example.org');
23317          *
23318          *       firstRepeat.element(by.buttonText('clear')).click();
23319          *
23320          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23321          *           .toBe('');
23322          *
23323          *       container.element(by.buttonText('add')).click();
23324          *
23325          *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23326          *           .element(by.model('contact.value'))
23327          *           .getAttribute('value'))
23328          *           .toBe('yourname@example.org');
23329          *     });
23330          *   </file>
23331          * </example>
23332          *
23333          * This example demonstrates the "attach to `$scope`" style of controller.
23334          *
23335          * <example name="ngController" module="controllerExample">
23336          *  <file name="index.html">
23337          *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23338          *     <label>Name: <input type="text" ng-model="name"/></label>
23339          *     <button ng-click="greet()">greet</button><br/>
23340          *     Contact:
23341          *     <ul>
23342          *       <li ng-repeat="contact in contacts">
23343          *         <select ng-model="contact.type" id="select_{{$index}}">
23344          *            <option>phone</option>
23345          *            <option>email</option>
23346          *         </select>
23347          *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23348          *         <button ng-click="clearContact(contact)">clear</button>
23349          *         <button ng-click="removeContact(contact)">X</button>
23350          *       </li>
23351          *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23352          *    </ul>
23353          *   </div>
23354          *  </file>
23355          *  <file name="app.js">
23356          *   angular.module('controllerExample', [])
23357          *     .controller('SettingsController2', ['$scope', SettingsController2]);
23358          *
23359          *   function SettingsController2($scope) {
23360          *     $scope.name = "John Smith";
23361          *     $scope.contacts = [
23362          *       {type:'phone', value:'408 555 1212'},
23363          *       {type:'email', value:'john.smith@example.org'} ];
23364          *
23365          *     $scope.greet = function() {
23366          *       alert($scope.name);
23367          *     };
23368          *
23369          *     $scope.addContact = function() {
23370          *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
23371          *     };
23372          *
23373          *     $scope.removeContact = function(contactToRemove) {
23374          *       var index = $scope.contacts.indexOf(contactToRemove);
23375          *       $scope.contacts.splice(index, 1);
23376          *     };
23377          *
23378          *     $scope.clearContact = function(contact) {
23379          *       contact.type = 'phone';
23380          *       contact.value = '';
23381          *     };
23382          *   }
23383          *  </file>
23384          *  <file name="protractor.js" type="protractor">
23385          *    it('should check controller', function() {
23386          *      var container = element(by.id('ctrl-exmpl'));
23387          *
23388          *      expect(container.element(by.model('name'))
23389          *          .getAttribute('value')).toBe('John Smith');
23390          *
23391          *      var firstRepeat =
23392          *          container.element(by.repeater('contact in contacts').row(0));
23393          *      var secondRepeat =
23394          *          container.element(by.repeater('contact in contacts').row(1));
23395          *
23396          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23397          *          .toBe('408 555 1212');
23398          *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23399          *          .toBe('john.smith@example.org');
23400          *
23401          *      firstRepeat.element(by.buttonText('clear')).click();
23402          *
23403          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23404          *          .toBe('');
23405          *
23406          *      container.element(by.buttonText('add')).click();
23407          *
23408          *      expect(container.element(by.repeater('contact in contacts').row(2))
23409          *          .element(by.model('contact.value'))
23410          *          .getAttribute('value'))
23411          *          .toBe('yourname@example.org');
23412          *    });
23413          *  </file>
23414          *</example>
23415
23416          */
23417         var ngControllerDirective = [function() {
23418           return {
23419             restrict: 'A',
23420             scope: true,
23421             controller: '@',
23422             priority: 500
23423           };
23424         }];
23425
23426         /**
23427          * @ngdoc directive
23428          * @name ngCsp
23429          *
23430          * @element html
23431          * @description
23432          *
23433          * Angular has some features that can break certain
23434          * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23435          *
23436          * If you intend to implement these rules then you must tell Angular not to use these features.
23437          *
23438          * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23439          *
23440          *
23441          * The following rules affect Angular:
23442          *
23443          * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23444          * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23445          * increase in the speed of evaluating Angular expressions.
23446          *
23447          * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23448          * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23449          * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23450          * `angular-csp.css` in your HTML manually.
23451          *
23452          * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23453          * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23454          * however, triggers a CSP error to be logged in the console:
23455          *
23456          * ```
23457          * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23458          * script in the following Content Security Policy directive: "default-src 'self'". Note that
23459          * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23460          * ```
23461          *
23462          * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23463          * directive on an element of the HTML document that appears before the `<script>` tag that loads
23464          * the `angular.js` file.
23465          *
23466          * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23467          *
23468          * You can specify which of the CSP related Angular features should be deactivated by providing
23469          * a value for the `ng-csp` attribute. The options are as follows:
23470          *
23471          * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23472          *
23473          * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23474          *
23475          * You can use these values in the following combinations:
23476          *
23477          *
23478          * * No declaration means that Angular will assume that you can do inline styles, but it will do
23479          * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23480          * of Angular.
23481          *
23482          * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23483          * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23484          * of Angular.
23485          *
23486          * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23487          * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23488          *
23489          * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23490          * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23491          *
23492          * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23493          * styles nor use eval, which is the same as an empty: ng-csp.
23494          * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23495          *
23496          * @example
23497          * This example shows how to apply the `ngCsp` directive to the `html` tag.
23498            ```html
23499              <!doctype html>
23500              <html ng-app ng-csp>
23501              ...
23502              ...
23503              </html>
23504            ```
23505           * @example
23506               // Note: the suffix `.csp` in the example name triggers
23507               // csp mode in our http server!
23508               <example name="example.csp" module="cspExample" ng-csp="true">
23509                 <file name="index.html">
23510                   <div ng-controller="MainController as ctrl">
23511                     <div>
23512                       <button ng-click="ctrl.inc()" id="inc">Increment</button>
23513                       <span id="counter">
23514                         {{ctrl.counter}}
23515                       </span>
23516                     </div>
23517
23518                     <div>
23519                       <button ng-click="ctrl.evil()" id="evil">Evil</button>
23520                       <span id="evilError">
23521                         {{ctrl.evilError}}
23522                       </span>
23523                     </div>
23524                   </div>
23525                 </file>
23526                 <file name="script.js">
23527                    angular.module('cspExample', [])
23528                      .controller('MainController', function() {
23529                         this.counter = 0;
23530                         this.inc = function() {
23531                           this.counter++;
23532                         };
23533                         this.evil = function() {
23534                           // jshint evil:true
23535                           try {
23536                             eval('1+2');
23537                           } catch (e) {
23538                             this.evilError = e.message;
23539                           }
23540                         };
23541                       });
23542                 </file>
23543                 <file name="protractor.js" type="protractor">
23544                   var util, webdriver;
23545
23546                   var incBtn = element(by.id('inc'));
23547                   var counter = element(by.id('counter'));
23548                   var evilBtn = element(by.id('evil'));
23549                   var evilError = element(by.id('evilError'));
23550
23551                   function getAndClearSevereErrors() {
23552                     return browser.manage().logs().get('browser').then(function(browserLog) {
23553                       return browserLog.filter(function(logEntry) {
23554                         return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23555                       });
23556                     });
23557                   }
23558
23559                   function clearErrors() {
23560                     getAndClearSevereErrors();
23561                   }
23562
23563                   function expectNoErrors() {
23564                     getAndClearSevereErrors().then(function(filteredLog) {
23565                       expect(filteredLog.length).toEqual(0);
23566                       if (filteredLog.length) {
23567                         console.log('browser console errors: ' + util.inspect(filteredLog));
23568                       }
23569                     });
23570                   }
23571
23572                   function expectError(regex) {
23573                     getAndClearSevereErrors().then(function(filteredLog) {
23574                       var found = false;
23575                       filteredLog.forEach(function(log) {
23576                         if (log.message.match(regex)) {
23577                           found = true;
23578                         }
23579                       });
23580                       if (!found) {
23581                         throw new Error('expected an error that matches ' + regex);
23582                       }
23583                     });
23584                   }
23585
23586                   beforeEach(function() {
23587                     util = require('util');
23588                     webdriver = require('protractor/node_modules/selenium-webdriver');
23589                   });
23590
23591                   // For now, we only test on Chrome,
23592                   // as Safari does not load the page with Protractor's injected scripts,
23593                   // and Firefox webdriver always disables content security policy (#6358)
23594                   if (browser.params.browser !== 'chrome') {
23595                     return;
23596                   }
23597
23598                   it('should not report errors when the page is loaded', function() {
23599                     // clear errors so we are not dependent on previous tests
23600                     clearErrors();
23601                     // Need to reload the page as the page is already loaded when
23602                     // we come here
23603                     browser.driver.getCurrentUrl().then(function(url) {
23604                       browser.get(url);
23605                     });
23606                     expectNoErrors();
23607                   });
23608
23609                   it('should evaluate expressions', function() {
23610                     expect(counter.getText()).toEqual('0');
23611                     incBtn.click();
23612                     expect(counter.getText()).toEqual('1');
23613                     expectNoErrors();
23614                   });
23615
23616                   it('should throw and report an error when using "eval"', function() {
23617                     evilBtn.click();
23618                     expect(evilError.getText()).toMatch(/Content Security Policy/);
23619                     expectError(/Content Security Policy/);
23620                   });
23621                 </file>
23622               </example>
23623           */
23624
23625         // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23626         // bootstrap the system (before $parse is instantiated), for this reason we just have
23627         // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23628
23629         /**
23630          * @ngdoc directive
23631          * @name ngClick
23632          *
23633          * @description
23634          * The ngClick directive allows you to specify custom behavior when
23635          * an element is clicked.
23636          *
23637          * @element ANY
23638          * @priority 0
23639          * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23640          * click. ({@link guide/expression#-event- Event object is available as `$event`})
23641          *
23642          * @example
23643            <example>
23644              <file name="index.html">
23645               <button ng-click="count = count + 1" ng-init="count=0">
23646                 Increment
23647               </button>
23648               <span>
23649                 count: {{count}}
23650               </span>
23651              </file>
23652              <file name="protractor.js" type="protractor">
23653                it('should check ng-click', function() {
23654                  expect(element(by.binding('count')).getText()).toMatch('0');
23655                  element(by.css('button')).click();
23656                  expect(element(by.binding('count')).getText()).toMatch('1');
23657                });
23658              </file>
23659            </example>
23660          */
23661         /*
23662          * A collection of directives that allows creation of custom event handlers that are defined as
23663          * angular expressions and are compiled and executed within the current scope.
23664          */
23665         var ngEventDirectives = {};
23666
23667         // For events that might fire synchronously during DOM manipulation
23668         // we need to execute their event handlers asynchronously using $evalAsync,
23669         // so that they are not executed in an inconsistent state.
23670         var forceAsyncEvents = {
23671           'blur': true,
23672           'focus': true
23673         };
23674         forEach(
23675           'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23676           function(eventName) {
23677             var directiveName = directiveNormalize('ng-' + eventName);
23678             ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23679               return {
23680                 restrict: 'A',
23681                 compile: function($element, attr) {
23682                   // We expose the powerful $event object on the scope that provides access to the Window,
23683                   // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23684                   // checks at the cost of speed since event handler expressions are not executed as
23685                   // frequently as regular change detection.
23686                   var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23687                   return function ngEventHandler(scope, element) {
23688                     element.on(eventName, function(event) {
23689                       var callback = function() {
23690                         fn(scope, {$event:event});
23691                       };
23692                       if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23693                         scope.$evalAsync(callback);
23694                       } else {
23695                         scope.$apply(callback);
23696                       }
23697                     });
23698                   };
23699                 }
23700               };
23701             }];
23702           }
23703         );
23704
23705         /**
23706          * @ngdoc directive
23707          * @name ngDblclick
23708          *
23709          * @description
23710          * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23711          *
23712          * @element ANY
23713          * @priority 0
23714          * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23715          * a dblclick. (The Event object is available as `$event`)
23716          *
23717          * @example
23718            <example>
23719              <file name="index.html">
23720               <button ng-dblclick="count = count + 1" ng-init="count=0">
23721                 Increment (on double click)
23722               </button>
23723               count: {{count}}
23724              </file>
23725            </example>
23726          */
23727
23728
23729         /**
23730          * @ngdoc directive
23731          * @name ngMousedown
23732          *
23733          * @description
23734          * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23735          *
23736          * @element ANY
23737          * @priority 0
23738          * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23739          * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23740          *
23741          * @example
23742            <example>
23743              <file name="index.html">
23744               <button ng-mousedown="count = count + 1" ng-init="count=0">
23745                 Increment (on mouse down)
23746               </button>
23747               count: {{count}}
23748              </file>
23749            </example>
23750          */
23751
23752
23753         /**
23754          * @ngdoc directive
23755          * @name ngMouseup
23756          *
23757          * @description
23758          * Specify custom behavior on mouseup event.
23759          *
23760          * @element ANY
23761          * @priority 0
23762          * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23763          * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23764          *
23765          * @example
23766            <example>
23767              <file name="index.html">
23768               <button ng-mouseup="count = count + 1" ng-init="count=0">
23769                 Increment (on mouse up)
23770               </button>
23771               count: {{count}}
23772              </file>
23773            </example>
23774          */
23775
23776         /**
23777          * @ngdoc directive
23778          * @name ngMouseover
23779          *
23780          * @description
23781          * Specify custom behavior on mouseover event.
23782          *
23783          * @element ANY
23784          * @priority 0
23785          * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23786          * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23787          *
23788          * @example
23789            <example>
23790              <file name="index.html">
23791               <button ng-mouseover="count = count + 1" ng-init="count=0">
23792                 Increment (when mouse is over)
23793               </button>
23794               count: {{count}}
23795              </file>
23796            </example>
23797          */
23798
23799
23800         /**
23801          * @ngdoc directive
23802          * @name ngMouseenter
23803          *
23804          * @description
23805          * Specify custom behavior on mouseenter event.
23806          *
23807          * @element ANY
23808          * @priority 0
23809          * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23810          * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23811          *
23812          * @example
23813            <example>
23814              <file name="index.html">
23815               <button ng-mouseenter="count = count + 1" ng-init="count=0">
23816                 Increment (when mouse enters)
23817               </button>
23818               count: {{count}}
23819              </file>
23820            </example>
23821          */
23822
23823
23824         /**
23825          * @ngdoc directive
23826          * @name ngMouseleave
23827          *
23828          * @description
23829          * Specify custom behavior on mouseleave event.
23830          *
23831          * @element ANY
23832          * @priority 0
23833          * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23834          * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23835          *
23836          * @example
23837            <example>
23838              <file name="index.html">
23839               <button ng-mouseleave="count = count + 1" ng-init="count=0">
23840                 Increment (when mouse leaves)
23841               </button>
23842               count: {{count}}
23843              </file>
23844            </example>
23845          */
23846
23847
23848         /**
23849          * @ngdoc directive
23850          * @name ngMousemove
23851          *
23852          * @description
23853          * Specify custom behavior on mousemove event.
23854          *
23855          * @element ANY
23856          * @priority 0
23857          * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23858          * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23859          *
23860          * @example
23861            <example>
23862              <file name="index.html">
23863               <button ng-mousemove="count = count + 1" ng-init="count=0">
23864                 Increment (when mouse moves)
23865               </button>
23866               count: {{count}}
23867              </file>
23868            </example>
23869          */
23870
23871
23872         /**
23873          * @ngdoc directive
23874          * @name ngKeydown
23875          *
23876          * @description
23877          * Specify custom behavior on keydown event.
23878          *
23879          * @element ANY
23880          * @priority 0
23881          * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23882          * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23883          *
23884          * @example
23885            <example>
23886              <file name="index.html">
23887               <input ng-keydown="count = count + 1" ng-init="count=0">
23888               key down count: {{count}}
23889              </file>
23890            </example>
23891          */
23892
23893
23894         /**
23895          * @ngdoc directive
23896          * @name ngKeyup
23897          *
23898          * @description
23899          * Specify custom behavior on keyup event.
23900          *
23901          * @element ANY
23902          * @priority 0
23903          * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23904          * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23905          *
23906          * @example
23907            <example>
23908              <file name="index.html">
23909                <p>Typing in the input box below updates the key count</p>
23910                <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23911
23912                <p>Typing in the input box below updates the keycode</p>
23913                <input ng-keyup="event=$event">
23914                <p>event keyCode: {{ event.keyCode }}</p>
23915                <p>event altKey: {{ event.altKey }}</p>
23916              </file>
23917            </example>
23918          */
23919
23920
23921         /**
23922          * @ngdoc directive
23923          * @name ngKeypress
23924          *
23925          * @description
23926          * Specify custom behavior on keypress event.
23927          *
23928          * @element ANY
23929          * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23930          * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23931          * and can be interrogated for keyCode, altKey, etc.)
23932          *
23933          * @example
23934            <example>
23935              <file name="index.html">
23936               <input ng-keypress="count = count + 1" ng-init="count=0">
23937               key press count: {{count}}
23938              </file>
23939            </example>
23940          */
23941
23942
23943         /**
23944          * @ngdoc directive
23945          * @name ngSubmit
23946          *
23947          * @description
23948          * Enables binding angular expressions to onsubmit events.
23949          *
23950          * Additionally it prevents the default action (which for form means sending the request to the
23951          * server and reloading the current page), but only if the form does not contain `action`,
23952          * `data-action`, or `x-action` attributes.
23953          *
23954          * <div class="alert alert-warning">
23955          * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23956          * `ngSubmit` handlers together. See the
23957          * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23958          * for a detailed discussion of when `ngSubmit` may be triggered.
23959          * </div>
23960          *
23961          * @element form
23962          * @priority 0
23963          * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23964          * ({@link guide/expression#-event- Event object is available as `$event`})
23965          *
23966          * @example
23967            <example module="submitExample">
23968              <file name="index.html">
23969               <script>
23970                 angular.module('submitExample', [])
23971                   .controller('ExampleController', ['$scope', function($scope) {
23972                     $scope.list = [];
23973                     $scope.text = 'hello';
23974                     $scope.submit = function() {
23975                       if ($scope.text) {
23976                         $scope.list.push(this.text);
23977                         $scope.text = '';
23978                       }
23979                     };
23980                   }]);
23981               </script>
23982               <form ng-submit="submit()" ng-controller="ExampleController">
23983                 Enter text and hit enter:
23984                 <input type="text" ng-model="text" name="text" />
23985                 <input type="submit" id="submit" value="Submit" />
23986                 <pre>list={{list}}</pre>
23987               </form>
23988              </file>
23989              <file name="protractor.js" type="protractor">
23990                it('should check ng-submit', function() {
23991                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23992                  element(by.css('#submit')).click();
23993                  expect(element(by.binding('list')).getText()).toContain('hello');
23994                  expect(element(by.model('text')).getAttribute('value')).toBe('');
23995                });
23996                it('should ignore empty strings', function() {
23997                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23998                  element(by.css('#submit')).click();
23999                  element(by.css('#submit')).click();
24000                  expect(element(by.binding('list')).getText()).toContain('hello');
24001                 });
24002              </file>
24003            </example>
24004          */
24005
24006         /**
24007          * @ngdoc directive
24008          * @name ngFocus
24009          *
24010          * @description
24011          * Specify custom behavior on focus event.
24012          *
24013          * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24014          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24015          * during an `$apply` to ensure a consistent state.
24016          *
24017          * @element window, input, select, textarea, a
24018          * @priority 0
24019          * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24020          * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24021          *
24022          * @example
24023          * See {@link ng.directive:ngClick ngClick}
24024          */
24025
24026         /**
24027          * @ngdoc directive
24028          * @name ngBlur
24029          *
24030          * @description
24031          * Specify custom behavior on blur event.
24032          *
24033          * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24034          * an element has lost focus.
24035          *
24036          * Note: As the `blur` event is executed synchronously also during DOM manipulations
24037          * (e.g. removing a focussed input),
24038          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24039          * during an `$apply` to ensure a consistent state.
24040          *
24041          * @element window, input, select, textarea, a
24042          * @priority 0
24043          * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24044          * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24045          *
24046          * @example
24047          * See {@link ng.directive:ngClick ngClick}
24048          */
24049
24050         /**
24051          * @ngdoc directive
24052          * @name ngCopy
24053          *
24054          * @description
24055          * Specify custom behavior on copy event.
24056          *
24057          * @element window, input, select, textarea, a
24058          * @priority 0
24059          * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24060          * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24061          *
24062          * @example
24063            <example>
24064              <file name="index.html">
24065               <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24066               copied: {{copied}}
24067              </file>
24068            </example>
24069          */
24070
24071         /**
24072          * @ngdoc directive
24073          * @name ngCut
24074          *
24075          * @description
24076          * Specify custom behavior on cut event.
24077          *
24078          * @element window, input, select, textarea, a
24079          * @priority 0
24080          * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24081          * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24082          *
24083          * @example
24084            <example>
24085              <file name="index.html">
24086               <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24087               cut: {{cut}}
24088              </file>
24089            </example>
24090          */
24091
24092         /**
24093          * @ngdoc directive
24094          * @name ngPaste
24095          *
24096          * @description
24097          * Specify custom behavior on paste event.
24098          *
24099          * @element window, input, select, textarea, a
24100          * @priority 0
24101          * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24102          * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24103          *
24104          * @example
24105            <example>
24106              <file name="index.html">
24107               <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24108               pasted: {{paste}}
24109              </file>
24110            </example>
24111          */
24112
24113         /**
24114          * @ngdoc directive
24115          * @name ngIf
24116          * @restrict A
24117          * @multiElement
24118          *
24119          * @description
24120          * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24121          * {expression}. If the expression assigned to `ngIf` evaluates to a false
24122          * value then the element is removed from the DOM, otherwise a clone of the
24123          * element is reinserted into the DOM.
24124          *
24125          * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24126          * element in the DOM rather than changing its visibility via the `display` css property.  A common
24127          * case when this difference is significant is when using css selectors that rely on an element's
24128          * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24129          *
24130          * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24131          * is created when the element is restored.  The scope created within `ngIf` inherits from
24132          * its parent scope using
24133          * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24134          * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24135          * a javascript primitive defined in the parent scope. In this case any modifications made to the
24136          * variable within the child scope will override (hide) the value in the parent scope.
24137          *
24138          * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24139          * is if an element's class attribute is directly modified after it's compiled, using something like
24140          * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24141          * the added class will be lost because the original compiled state is used to regenerate the element.
24142          *
24143          * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24144          * and `leave` effects.
24145          *
24146          * @animations
24147          * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24148          * leave - happens just before the `ngIf` contents are removed from the DOM
24149          *
24150          * @element ANY
24151          * @scope
24152          * @priority 600
24153          * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24154          *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24155          *     element is added to the DOM tree.
24156          *
24157          * @example
24158           <example module="ngAnimate" deps="angular-animate.js" animations="true">
24159             <file name="index.html">
24160               <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24161               Show when checked:
24162               <span ng-if="checked" class="animate-if">
24163                 This is removed when the checkbox is unchecked.
24164               </span>
24165             </file>
24166             <file name="animations.css">
24167               .animate-if {
24168                 background:white;
24169                 border:1px solid black;
24170                 padding:10px;
24171               }
24172
24173               .animate-if.ng-enter, .animate-if.ng-leave {
24174                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24175               }
24176
24177               .animate-if.ng-enter,
24178               .animate-if.ng-leave.ng-leave-active {
24179                 opacity:0;
24180               }
24181
24182               .animate-if.ng-leave,
24183               .animate-if.ng-enter.ng-enter-active {
24184                 opacity:1;
24185               }
24186             </file>
24187           </example>
24188          */
24189         var ngIfDirective = ['$animate', function($animate) {
24190           return {
24191             multiElement: true,
24192             transclude: 'element',
24193             priority: 600,
24194             terminal: true,
24195             restrict: 'A',
24196             $$tlb: true,
24197             link: function($scope, $element, $attr, ctrl, $transclude) {
24198                 var block, childScope, previousElements;
24199                 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24200
24201                   if (value) {
24202                     if (!childScope) {
24203                       $transclude(function(clone, newScope) {
24204                         childScope = newScope;
24205                         clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24206                         // Note: We only need the first/last node of the cloned nodes.
24207                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24208                         // by a directive with templateUrl when its template arrives.
24209                         block = {
24210                           clone: clone
24211                         };
24212                         $animate.enter(clone, $element.parent(), $element);
24213                       });
24214                     }
24215                   } else {
24216                     if (previousElements) {
24217                       previousElements.remove();
24218                       previousElements = null;
24219                     }
24220                     if (childScope) {
24221                       childScope.$destroy();
24222                       childScope = null;
24223                     }
24224                     if (block) {
24225                       previousElements = getBlockNodes(block.clone);
24226                       $animate.leave(previousElements).then(function() {
24227                         previousElements = null;
24228                       });
24229                       block = null;
24230                     }
24231                   }
24232                 });
24233             }
24234           };
24235         }];
24236
24237         /**
24238          * @ngdoc directive
24239          * @name ngInclude
24240          * @restrict ECA
24241          *
24242          * @description
24243          * Fetches, compiles and includes an external HTML fragment.
24244          *
24245          * By default, the template URL is restricted to the same domain and protocol as the
24246          * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24247          * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24248          * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24249          * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24250          * ng.$sce Strict Contextual Escaping}.
24251          *
24252          * In addition, the browser's
24253          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24254          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24255          * policy may further restrict whether the template is successfully loaded.
24256          * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24257          * access on some browsers.
24258          *
24259          * @animations
24260          * enter - animation is used to bring new content into the browser.
24261          * leave - animation is used to animate existing content away.
24262          *
24263          * The enter and leave animation occur concurrently.
24264          *
24265          * @scope
24266          * @priority 400
24267          *
24268          * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24269          *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24270          * @param {string=} onload Expression to evaluate when a new partial is loaded.
24271          *                  <div class="alert alert-warning">
24272          *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24273          *                  a function with the name on the window element, which will usually throw a
24274          *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24275          *                  different form that {@link guide/directive#normalization matches} `onload`.
24276          *                  </div>
24277            *
24278          * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24279          *                  $anchorScroll} to scroll the viewport after the content is loaded.
24280          *
24281          *                  - If the attribute is not set, disable scrolling.
24282          *                  - If the attribute is set without value, enable scrolling.
24283          *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24284          *
24285          * @example
24286           <example module="includeExample" deps="angular-animate.js" animations="true">
24287             <file name="index.html">
24288              <div ng-controller="ExampleController">
24289                <select ng-model="template" ng-options="t.name for t in templates">
24290                 <option value="">(blank)</option>
24291                </select>
24292                url of the template: <code>{{template.url}}</code>
24293                <hr/>
24294                <div class="slide-animate-container">
24295                  <div class="slide-animate" ng-include="template.url"></div>
24296                </div>
24297              </div>
24298             </file>
24299             <file name="script.js">
24300               angular.module('includeExample', ['ngAnimate'])
24301                 .controller('ExampleController', ['$scope', function($scope) {
24302                   $scope.templates =
24303                     [ { name: 'template1.html', url: 'template1.html'},
24304                       { name: 'template2.html', url: 'template2.html'} ];
24305                   $scope.template = $scope.templates[0];
24306                 }]);
24307              </file>
24308             <file name="template1.html">
24309               Content of template1.html
24310             </file>
24311             <file name="template2.html">
24312               Content of template2.html
24313             </file>
24314             <file name="animations.css">
24315               .slide-animate-container {
24316                 position:relative;
24317                 background:white;
24318                 border:1px solid black;
24319                 height:40px;
24320                 overflow:hidden;
24321               }
24322
24323               .slide-animate {
24324                 padding:10px;
24325               }
24326
24327               .slide-animate.ng-enter, .slide-animate.ng-leave {
24328                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24329
24330                 position:absolute;
24331                 top:0;
24332                 left:0;
24333                 right:0;
24334                 bottom:0;
24335                 display:block;
24336                 padding:10px;
24337               }
24338
24339               .slide-animate.ng-enter {
24340                 top:-50px;
24341               }
24342               .slide-animate.ng-enter.ng-enter-active {
24343                 top:0;
24344               }
24345
24346               .slide-animate.ng-leave {
24347                 top:0;
24348               }
24349               .slide-animate.ng-leave.ng-leave-active {
24350                 top:50px;
24351               }
24352             </file>
24353             <file name="protractor.js" type="protractor">
24354               var templateSelect = element(by.model('template'));
24355               var includeElem = element(by.css('[ng-include]'));
24356
24357               it('should load template1.html', function() {
24358                 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24359               });
24360
24361               it('should load template2.html', function() {
24362                 if (browser.params.browser == 'firefox') {
24363                   // Firefox can't handle using selects
24364                   // See https://github.com/angular/protractor/issues/480
24365                   return;
24366                 }
24367                 templateSelect.click();
24368                 templateSelect.all(by.css('option')).get(2).click();
24369                 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24370               });
24371
24372               it('should change to blank', function() {
24373                 if (browser.params.browser == 'firefox') {
24374                   // Firefox can't handle using selects
24375                   return;
24376                 }
24377                 templateSelect.click();
24378                 templateSelect.all(by.css('option')).get(0).click();
24379                 expect(includeElem.isPresent()).toBe(false);
24380               });
24381             </file>
24382           </example>
24383          */
24384
24385
24386         /**
24387          * @ngdoc event
24388          * @name ngInclude#$includeContentRequested
24389          * @eventType emit on the scope ngInclude was declared in
24390          * @description
24391          * Emitted every time the ngInclude content is requested.
24392          *
24393          * @param {Object} angularEvent Synthetic event object.
24394          * @param {String} src URL of content to load.
24395          */
24396
24397
24398         /**
24399          * @ngdoc event
24400          * @name ngInclude#$includeContentLoaded
24401          * @eventType emit on the current ngInclude scope
24402          * @description
24403          * Emitted every time the ngInclude content is reloaded.
24404          *
24405          * @param {Object} angularEvent Synthetic event object.
24406          * @param {String} src URL of content to load.
24407          */
24408
24409
24410         /**
24411          * @ngdoc event
24412          * @name ngInclude#$includeContentError
24413          * @eventType emit on the scope ngInclude was declared in
24414          * @description
24415          * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24416          *
24417          * @param {Object} angularEvent Synthetic event object.
24418          * @param {String} src URL of content to load.
24419          */
24420         var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24421                           function($templateRequest,   $anchorScroll,   $animate) {
24422           return {
24423             restrict: 'ECA',
24424             priority: 400,
24425             terminal: true,
24426             transclude: 'element',
24427             controller: angular.noop,
24428             compile: function(element, attr) {
24429               var srcExp = attr.ngInclude || attr.src,
24430                   onloadExp = attr.onload || '',
24431                   autoScrollExp = attr.autoscroll;
24432
24433               return function(scope, $element, $attr, ctrl, $transclude) {
24434                 var changeCounter = 0,
24435                     currentScope,
24436                     previousElement,
24437                     currentElement;
24438
24439                 var cleanupLastIncludeContent = function() {
24440                   if (previousElement) {
24441                     previousElement.remove();
24442                     previousElement = null;
24443                   }
24444                   if (currentScope) {
24445                     currentScope.$destroy();
24446                     currentScope = null;
24447                   }
24448                   if (currentElement) {
24449                     $animate.leave(currentElement).then(function() {
24450                       previousElement = null;
24451                     });
24452                     previousElement = currentElement;
24453                     currentElement = null;
24454                   }
24455                 };
24456
24457                 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24458                   var afterAnimation = function() {
24459                     if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24460                       $anchorScroll();
24461                     }
24462                   };
24463                   var thisChangeId = ++changeCounter;
24464
24465                   if (src) {
24466                     //set the 2nd param to true to ignore the template request error so that the inner
24467                     //contents and scope can be cleaned up.
24468                     $templateRequest(src, true).then(function(response) {
24469                       if (thisChangeId !== changeCounter) return;
24470                       var newScope = scope.$new();
24471                       ctrl.template = response;
24472
24473                       // Note: This will also link all children of ng-include that were contained in the original
24474                       // html. If that content contains controllers, ... they could pollute/change the scope.
24475                       // However, using ng-include on an element with additional content does not make sense...
24476                       // Note: We can't remove them in the cloneAttchFn of $transclude as that
24477                       // function is called before linking the content, which would apply child
24478                       // directives to non existing elements.
24479                       var clone = $transclude(newScope, function(clone) {
24480                         cleanupLastIncludeContent();
24481                         $animate.enter(clone, null, $element).then(afterAnimation);
24482                       });
24483
24484                       currentScope = newScope;
24485                       currentElement = clone;
24486
24487                       currentScope.$emit('$includeContentLoaded', src);
24488                       scope.$eval(onloadExp);
24489                     }, function() {
24490                       if (thisChangeId === changeCounter) {
24491                         cleanupLastIncludeContent();
24492                         scope.$emit('$includeContentError', src);
24493                       }
24494                     });
24495                     scope.$emit('$includeContentRequested', src);
24496                   } else {
24497                     cleanupLastIncludeContent();
24498                     ctrl.template = null;
24499                   }
24500                 });
24501               };
24502             }
24503           };
24504         }];
24505
24506         // This directive is called during the $transclude call of the first `ngInclude` directive.
24507         // It will replace and compile the content of the element with the loaded template.
24508         // We need this directive so that the element content is already filled when
24509         // the link function of another directive on the same element as ngInclude
24510         // is called.
24511         var ngIncludeFillContentDirective = ['$compile',
24512           function($compile) {
24513             return {
24514               restrict: 'ECA',
24515               priority: -400,
24516               require: 'ngInclude',
24517               link: function(scope, $element, $attr, ctrl) {
24518                 if (/SVG/.test($element[0].toString())) {
24519                   // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24520                   // support innerHTML, so detect this here and try to generate the contents
24521                   // specially.
24522                   $element.empty();
24523                   $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24524                       function namespaceAdaptedClone(clone) {
24525                     $element.append(clone);
24526                   }, {futureParentElement: $element});
24527                   return;
24528                 }
24529
24530                 $element.html(ctrl.template);
24531                 $compile($element.contents())(scope);
24532               }
24533             };
24534           }];
24535
24536         /**
24537          * @ngdoc directive
24538          * @name ngInit
24539          * @restrict AC
24540          *
24541          * @description
24542          * The `ngInit` directive allows you to evaluate an expression in the
24543          * current scope.
24544          *
24545          * <div class="alert alert-danger">
24546          * This directive can be abused to add unnecessary amounts of logic into your templates.
24547          * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24548          * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24549          * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24550          * rather than `ngInit` to initialize values on a scope.
24551          * </div>
24552          *
24553          * <div class="alert alert-warning">
24554          * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24555          * sure you have parentheses to ensure correct operator precedence:
24556          * <pre class="prettyprint">
24557          * `<div ng-init="test1 = ($index | toString)"></div>`
24558          * </pre>
24559          * </div>
24560          *
24561          * @priority 450
24562          *
24563          * @element ANY
24564          * @param {expression} ngInit {@link guide/expression Expression} to eval.
24565          *
24566          * @example
24567            <example module="initExample">
24568              <file name="index.html">
24569            <script>
24570              angular.module('initExample', [])
24571                .controller('ExampleController', ['$scope', function($scope) {
24572                  $scope.list = [['a', 'b'], ['c', 'd']];
24573                }]);
24574            </script>
24575            <div ng-controller="ExampleController">
24576              <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24577                <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24578                   <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24579                </div>
24580              </div>
24581            </div>
24582              </file>
24583              <file name="protractor.js" type="protractor">
24584                it('should alias index positions', function() {
24585                  var elements = element.all(by.css('.example-init'));
24586                  expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24587                  expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24588                  expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24589                  expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24590                });
24591              </file>
24592            </example>
24593          */
24594         var ngInitDirective = ngDirective({
24595           priority: 450,
24596           compile: function() {
24597             return {
24598               pre: function(scope, element, attrs) {
24599                 scope.$eval(attrs.ngInit);
24600               }
24601             };
24602           }
24603         });
24604
24605         /**
24606          * @ngdoc directive
24607          * @name ngList
24608          *
24609          * @description
24610          * Text input that converts between a delimited string and an array of strings. The default
24611          * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24612          * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24613          *
24614          * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24615          * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24616          *   list item is respected. This implies that the user of the directive is responsible for
24617          *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24618          *   tab or newline character.
24619          * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24620          *   when joining the list items back together) and whitespace around each list item is stripped
24621          *   before it is added to the model.
24622          *
24623          * ### Example with Validation
24624          *
24625          * <example name="ngList-directive" module="listExample">
24626          *   <file name="app.js">
24627          *      angular.module('listExample', [])
24628          *        .controller('ExampleController', ['$scope', function($scope) {
24629          *          $scope.names = ['morpheus', 'neo', 'trinity'];
24630          *        }]);
24631          *   </file>
24632          *   <file name="index.html">
24633          *    <form name="myForm" ng-controller="ExampleController">
24634          *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24635          *      <span role="alert">
24636          *        <span class="error" ng-show="myForm.namesInput.$error.required">
24637          *        Required!</span>
24638          *      </span>
24639          *      <br>
24640          *      <tt>names = {{names}}</tt><br/>
24641          *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24642          *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24643          *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24644          *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24645          *     </form>
24646          *   </file>
24647          *   <file name="protractor.js" type="protractor">
24648          *     var listInput = element(by.model('names'));
24649          *     var names = element(by.exactBinding('names'));
24650          *     var valid = element(by.binding('myForm.namesInput.$valid'));
24651          *     var error = element(by.css('span.error'));
24652          *
24653          *     it('should initialize to model', function() {
24654          *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24655          *       expect(valid.getText()).toContain('true');
24656          *       expect(error.getCssValue('display')).toBe('none');
24657          *     });
24658          *
24659          *     it('should be invalid if empty', function() {
24660          *       listInput.clear();
24661          *       listInput.sendKeys('');
24662          *
24663          *       expect(names.getText()).toContain('');
24664          *       expect(valid.getText()).toContain('false');
24665          *       expect(error.getCssValue('display')).not.toBe('none');
24666          *     });
24667          *   </file>
24668          * </example>
24669          *
24670          * ### Example - splitting on newline
24671          * <example name="ngList-directive-newlines">
24672          *   <file name="index.html">
24673          *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24674          *    <pre>{{ list | json }}</pre>
24675          *   </file>
24676          *   <file name="protractor.js" type="protractor">
24677          *     it("should split the text by newlines", function() {
24678          *       var listInput = element(by.model('list'));
24679          *       var output = element(by.binding('list | json'));
24680          *       listInput.sendKeys('abc\ndef\nghi');
24681          *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24682          *     });
24683          *   </file>
24684          * </example>
24685          *
24686          * @element input
24687          * @param {string=} ngList optional delimiter that should be used to split the value.
24688          */
24689         var ngListDirective = function() {
24690           return {
24691             restrict: 'A',
24692             priority: 100,
24693             require: 'ngModel',
24694             link: function(scope, element, attr, ctrl) {
24695               // We want to control whitespace trimming so we use this convoluted approach
24696               // to access the ngList attribute, which doesn't pre-trim the attribute
24697               var ngList = element.attr(attr.$attr.ngList) || ', ';
24698               var trimValues = attr.ngTrim !== 'false';
24699               var separator = trimValues ? trim(ngList) : ngList;
24700
24701               var parse = function(viewValue) {
24702                 // If the viewValue is invalid (say required but empty) it will be `undefined`
24703                 if (isUndefined(viewValue)) return;
24704
24705                 var list = [];
24706
24707                 if (viewValue) {
24708                   forEach(viewValue.split(separator), function(value) {
24709                     if (value) list.push(trimValues ? trim(value) : value);
24710                   });
24711                 }
24712
24713                 return list;
24714               };
24715
24716               ctrl.$parsers.push(parse);
24717               ctrl.$formatters.push(function(value) {
24718                 if (isArray(value)) {
24719                   return value.join(ngList);
24720                 }
24721
24722                 return undefined;
24723               });
24724
24725               // Override the standard $isEmpty because an empty array means the input is empty.
24726               ctrl.$isEmpty = function(value) {
24727                 return !value || !value.length;
24728               };
24729             }
24730           };
24731         };
24732
24733         /* global VALID_CLASS: true,
24734           INVALID_CLASS: true,
24735           PRISTINE_CLASS: true,
24736           DIRTY_CLASS: true,
24737           UNTOUCHED_CLASS: true,
24738           TOUCHED_CLASS: true,
24739         */
24740
24741         var VALID_CLASS = 'ng-valid',
24742             INVALID_CLASS = 'ng-invalid',
24743             PRISTINE_CLASS = 'ng-pristine',
24744             DIRTY_CLASS = 'ng-dirty',
24745             UNTOUCHED_CLASS = 'ng-untouched',
24746             TOUCHED_CLASS = 'ng-touched',
24747             PENDING_CLASS = 'ng-pending';
24748
24749         var ngModelMinErr = minErr('ngModel');
24750
24751         /**
24752          * @ngdoc type
24753          * @name ngModel.NgModelController
24754          *
24755          * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24756          * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24757          * is set.
24758          * @property {*} $modelValue The value in the model that the control is bound to.
24759          * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24760                the control reads value from the DOM. The functions are called in array order, each passing
24761                its return value through to the next. The last return value is forwarded to the
24762                {@link ngModel.NgModelController#$validators `$validators`} collection.
24763
24764         Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24765         `$viewValue`}.
24766
24767         Returning `undefined` from a parser means a parse error occurred. In that case,
24768         no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24769         will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24770         is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24771
24772          *
24773          * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24774                the model value changes. The functions are called in reverse array order, each passing the value through to the
24775                next. The last return value is used as the actual DOM value.
24776                Used to format / convert values for display in the control.
24777          * ```js
24778          * function formatter(value) {
24779          *   if (value) {
24780          *     return value.toUpperCase();
24781          *   }
24782          * }
24783          * ngModel.$formatters.push(formatter);
24784          * ```
24785          *
24786          * @property {Object.<string, function>} $validators A collection of validators that are applied
24787          *      whenever the model value changes. The key value within the object refers to the name of the
24788          *      validator while the function refers to the validation operation. The validation operation is
24789          *      provided with the model value as an argument and must return a true or false value depending
24790          *      on the response of that validation.
24791          *
24792          * ```js
24793          * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24794          *   var value = modelValue || viewValue;
24795          *   return /[0-9]+/.test(value) &&
24796          *          /[a-z]+/.test(value) &&
24797          *          /[A-Z]+/.test(value) &&
24798          *          /\W+/.test(value);
24799          * };
24800          * ```
24801          *
24802          * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24803          *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24804          *      is expected to return a promise when it is run during the model validation process. Once the promise
24805          *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24806          *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24807          *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24808          *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24809          *      will only run once all synchronous validators have passed.
24810          *
24811          * Please note that if $http is used then it is important that the server returns a success HTTP response code
24812          * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24813          *
24814          * ```js
24815          * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24816          *   var value = modelValue || viewValue;
24817          *
24818          *   // Lookup user by username
24819          *   return $http.get('/api/users/' + value).
24820          *      then(function resolved() {
24821          *        //username exists, this means validation fails
24822          *        return $q.reject('exists');
24823          *      }, function rejected() {
24824          *        //username does not exist, therefore this validation passes
24825          *        return true;
24826          *      });
24827          * };
24828          * ```
24829          *
24830          * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24831          *     view value has changed. It is called with no arguments, and its return value is ignored.
24832          *     This can be used in place of additional $watches against the model value.
24833          *
24834          * @property {Object} $error An object hash with all failing validator ids as keys.
24835          * @property {Object} $pending An object hash with all pending validator ids as keys.
24836          *
24837          * @property {boolean} $untouched True if control has not lost focus yet.
24838          * @property {boolean} $touched True if control has lost focus.
24839          * @property {boolean} $pristine True if user has not interacted with the control yet.
24840          * @property {boolean} $dirty True if user has already interacted with the control.
24841          * @property {boolean} $valid True if there is no error.
24842          * @property {boolean} $invalid True if at least one error on the control.
24843          * @property {string} $name The name attribute of the control.
24844          *
24845          * @description
24846          *
24847          * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24848          * The controller contains services for data-binding, validation, CSS updates, and value formatting
24849          * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24850          * listening to DOM events.
24851          * Such DOM related logic should be provided by other directives which make use of
24852          * `NgModelController` for data-binding to control elements.
24853          * Angular provides this DOM logic for most {@link input `input`} elements.
24854          * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24855          * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24856          *
24857          * @example
24858          * ### Custom Control Example
24859          * This example shows how to use `NgModelController` with a custom control to achieve
24860          * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24861          * collaborate together to achieve the desired result.
24862          *
24863          * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24864          * contents be edited in place by the user.
24865          *
24866          * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24867          * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24868          * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24869          * that content using the `$sce` service.
24870          *
24871          * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24872             <file name="style.css">
24873               [contenteditable] {
24874                 border: 1px solid black;
24875                 background-color: white;
24876                 min-height: 20px;
24877               }
24878
24879               .ng-invalid {
24880                 border: 1px solid red;
24881               }
24882
24883             </file>
24884             <file name="script.js">
24885               angular.module('customControl', ['ngSanitize']).
24886                 directive('contenteditable', ['$sce', function($sce) {
24887                   return {
24888                     restrict: 'A', // only activate on element attribute
24889                     require: '?ngModel', // get a hold of NgModelController
24890                     link: function(scope, element, attrs, ngModel) {
24891                       if (!ngModel) return; // do nothing if no ng-model
24892
24893                       // Specify how UI should be updated
24894                       ngModel.$render = function() {
24895                         element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24896                       };
24897
24898                       // Listen for change events to enable binding
24899                       element.on('blur keyup change', function() {
24900                         scope.$evalAsync(read);
24901                       });
24902                       read(); // initialize
24903
24904                       // Write data to the model
24905                       function read() {
24906                         var html = element.html();
24907                         // When we clear the content editable the browser leaves a <br> behind
24908                         // If strip-br attribute is provided then we strip this out
24909                         if ( attrs.stripBr && html == '<br>' ) {
24910                           html = '';
24911                         }
24912                         ngModel.$setViewValue(html);
24913                       }
24914                     }
24915                   };
24916                 }]);
24917             </file>
24918             <file name="index.html">
24919               <form name="myForm">
24920                <div contenteditable
24921                     name="myWidget" ng-model="userContent"
24922                     strip-br="true"
24923                     required>Change me!</div>
24924                 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24925                <hr>
24926                <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24927               </form>
24928             </file>
24929             <file name="protractor.js" type="protractor">
24930             it('should data-bind and become invalid', function() {
24931               if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24932                 // SafariDriver can't handle contenteditable
24933                 // and Firefox driver can't clear contenteditables very well
24934                 return;
24935               }
24936               var contentEditable = element(by.css('[contenteditable]'));
24937               var content = 'Change me!';
24938
24939               expect(contentEditable.getText()).toEqual(content);
24940
24941               contentEditable.clear();
24942               contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24943               expect(contentEditable.getText()).toEqual('');
24944               expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24945             });
24946             </file>
24947          * </example>
24948          *
24949          *
24950          */
24951         var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24952             function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24953           this.$viewValue = Number.NaN;
24954           this.$modelValue = Number.NaN;
24955           this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24956           this.$validators = {};
24957           this.$asyncValidators = {};
24958           this.$parsers = [];
24959           this.$formatters = [];
24960           this.$viewChangeListeners = [];
24961           this.$untouched = true;
24962           this.$touched = false;
24963           this.$pristine = true;
24964           this.$dirty = false;
24965           this.$valid = true;
24966           this.$invalid = false;
24967           this.$error = {}; // keep invalid keys here
24968           this.$$success = {}; // keep valid keys here
24969           this.$pending = undefined; // keep pending keys here
24970           this.$name = $interpolate($attr.name || '', false)($scope);
24971           this.$$parentForm = nullFormCtrl;
24972
24973           var parsedNgModel = $parse($attr.ngModel),
24974               parsedNgModelAssign = parsedNgModel.assign,
24975               ngModelGet = parsedNgModel,
24976               ngModelSet = parsedNgModelAssign,
24977               pendingDebounce = null,
24978               parserValid,
24979               ctrl = this;
24980
24981           this.$$setOptions = function(options) {
24982             ctrl.$options = options;
24983             if (options && options.getterSetter) {
24984               var invokeModelGetter = $parse($attr.ngModel + '()'),
24985                   invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24986
24987               ngModelGet = function($scope) {
24988                 var modelValue = parsedNgModel($scope);
24989                 if (isFunction(modelValue)) {
24990                   modelValue = invokeModelGetter($scope);
24991                 }
24992                 return modelValue;
24993               };
24994               ngModelSet = function($scope, newValue) {
24995                 if (isFunction(parsedNgModel($scope))) {
24996                   invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24997                 } else {
24998                   parsedNgModelAssign($scope, ctrl.$modelValue);
24999                 }
25000               };
25001             } else if (!parsedNgModel.assign) {
25002               throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25003                   $attr.ngModel, startingTag($element));
25004             }
25005           };
25006
25007           /**
25008            * @ngdoc method
25009            * @name ngModel.NgModelController#$render
25010            *
25011            * @description
25012            * Called when the view needs to be updated. It is expected that the user of the ng-model
25013            * directive will implement this method.
25014            *
25015            * The `$render()` method is invoked in the following situations:
25016            *
25017            * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25018            *   committed value then `$render()` is called to update the input control.
25019            * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25020            *   the `$viewValue` are different from last time.
25021            *
25022            * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25023            * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25024            * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25025            * invoked if you only change a property on the objects.
25026            */
25027           this.$render = noop;
25028
25029           /**
25030            * @ngdoc method
25031            * @name ngModel.NgModelController#$isEmpty
25032            *
25033            * @description
25034            * This is called when we need to determine if the value of an input is empty.
25035            *
25036            * For instance, the required directive does this to work out if the input has data or not.
25037            *
25038            * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25039            *
25040            * You can override this for input directives whose concept of being empty is different from the
25041            * default. The `checkboxInputType` directive does this because in its case a value of `false`
25042            * implies empty.
25043            *
25044            * @param {*} value The value of the input to check for emptiness.
25045            * @returns {boolean} True if `value` is "empty".
25046            */
25047           this.$isEmpty = function(value) {
25048             return isUndefined(value) || value === '' || value === null || value !== value;
25049           };
25050
25051           var currentValidationRunId = 0;
25052
25053           /**
25054            * @ngdoc method
25055            * @name ngModel.NgModelController#$setValidity
25056            *
25057            * @description
25058            * Change the validity state, and notify the form.
25059            *
25060            * This method can be called within $parsers/$formatters or a custom validation implementation.
25061            * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25062            * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25063            *
25064            * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25065            *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25066            *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25067            *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25068            *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25069            *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25070            * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25071            *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25072            *                          Skipped is used by Angular when validators do not run because of parse errors and
25073            *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25074            */
25075           addSetValidityMethod({
25076             ctrl: this,
25077             $element: $element,
25078             set: function(object, property) {
25079               object[property] = true;
25080             },
25081             unset: function(object, property) {
25082               delete object[property];
25083             },
25084             $animate: $animate
25085           });
25086
25087           /**
25088            * @ngdoc method
25089            * @name ngModel.NgModelController#$setPristine
25090            *
25091            * @description
25092            * Sets the control to its pristine state.
25093            *
25094            * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25095            * state (`ng-pristine` class). A model is considered to be pristine when the control
25096            * has not been changed from when first compiled.
25097            */
25098           this.$setPristine = function() {
25099             ctrl.$dirty = false;
25100             ctrl.$pristine = true;
25101             $animate.removeClass($element, DIRTY_CLASS);
25102             $animate.addClass($element, PRISTINE_CLASS);
25103           };
25104
25105           /**
25106            * @ngdoc method
25107            * @name ngModel.NgModelController#$setDirty
25108            *
25109            * @description
25110            * Sets the control to its dirty state.
25111            *
25112            * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25113            * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25114            * from when first compiled.
25115            */
25116           this.$setDirty = function() {
25117             ctrl.$dirty = true;
25118             ctrl.$pristine = false;
25119             $animate.removeClass($element, PRISTINE_CLASS);
25120             $animate.addClass($element, DIRTY_CLASS);
25121             ctrl.$$parentForm.$setDirty();
25122           };
25123
25124           /**
25125            * @ngdoc method
25126            * @name ngModel.NgModelController#$setUntouched
25127            *
25128            * @description
25129            * Sets the control to its untouched state.
25130            *
25131            * This method can be called to remove the `ng-touched` class and set the control to its
25132            * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25133            * by default, however this function can be used to restore that state if the model has
25134            * already been touched by the user.
25135            */
25136           this.$setUntouched = function() {
25137             ctrl.$touched = false;
25138             ctrl.$untouched = true;
25139             $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25140           };
25141
25142           /**
25143            * @ngdoc method
25144            * @name ngModel.NgModelController#$setTouched
25145            *
25146            * @description
25147            * Sets the control to its touched state.
25148            *
25149            * This method can be called to remove the `ng-untouched` class and set the control to its
25150            * touched state (`ng-touched` class). A model is considered to be touched when the user has
25151            * first focused the control element and then shifted focus away from the control (blur event).
25152            */
25153           this.$setTouched = function() {
25154             ctrl.$touched = true;
25155             ctrl.$untouched = false;
25156             $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25157           };
25158
25159           /**
25160            * @ngdoc method
25161            * @name ngModel.NgModelController#$rollbackViewValue
25162            *
25163            * @description
25164            * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25165            * which may be caused by a pending debounced event or because the input is waiting for a some
25166            * future event.
25167            *
25168            * If you have an input that uses `ng-model-options` to set up debounced events or events such
25169            * as blur you can have a situation where there is a period when the `$viewValue`
25170            * is out of synch with the ngModel's `$modelValue`.
25171            *
25172            * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25173            * programmatically before these debounced/future events have resolved/occurred, because Angular's
25174            * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25175            *
25176            * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25177            * input which may have such events pending. This is important in order to make sure that the
25178            * input field will be updated with the new model value and any pending operations are cancelled.
25179            *
25180            * <example name="ng-model-cancel-update" module="cancel-update-example">
25181            *   <file name="app.js">
25182            *     angular.module('cancel-update-example', [])
25183            *
25184            *     .controller('CancelUpdateController', ['$scope', function($scope) {
25185            *       $scope.resetWithCancel = function(e) {
25186            *         if (e.keyCode == 27) {
25187            *           $scope.myForm.myInput1.$rollbackViewValue();
25188            *           $scope.myValue = '';
25189            *         }
25190            *       };
25191            *       $scope.resetWithoutCancel = function(e) {
25192            *         if (e.keyCode == 27) {
25193            *           $scope.myValue = '';
25194            *         }
25195            *       };
25196            *     }]);
25197            *   </file>
25198            *   <file name="index.html">
25199            *     <div ng-controller="CancelUpdateController">
25200            *       <p>Try typing something in each input.  See that the model only updates when you
25201            *          blur off the input.
25202            *        </p>
25203            *        <p>Now see what happens if you start typing then press the Escape key</p>
25204            *
25205            *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25206            *         <p id="inputDescription1">With $rollbackViewValue()</p>
25207            *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25208            *                ng-keydown="resetWithCancel($event)"><br/>
25209            *         myValue: "{{ myValue }}"
25210            *
25211            *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25212            *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25213            *                ng-keydown="resetWithoutCancel($event)"><br/>
25214            *         myValue: "{{ myValue }}"
25215            *       </form>
25216            *     </div>
25217            *   </file>
25218            * </example>
25219            */
25220           this.$rollbackViewValue = function() {
25221             $timeout.cancel(pendingDebounce);
25222             ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25223             ctrl.$render();
25224           };
25225
25226           /**
25227            * @ngdoc method
25228            * @name ngModel.NgModelController#$validate
25229            *
25230            * @description
25231            * Runs each of the registered validators (first synchronous validators and then
25232            * asynchronous validators).
25233            * If the validity changes to invalid, the model will be set to `undefined`,
25234            * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25235            * If the validity changes to valid, it will set the model to the last available valid
25236            * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25237            */
25238           this.$validate = function() {
25239             // ignore $validate before model is initialized
25240             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25241               return;
25242             }
25243
25244             var viewValue = ctrl.$$lastCommittedViewValue;
25245             // Note: we use the $$rawModelValue as $modelValue might have been
25246             // set to undefined during a view -> model update that found validation
25247             // errors. We can't parse the view here, since that could change
25248             // the model although neither viewValue nor the model on the scope changed
25249             var modelValue = ctrl.$$rawModelValue;
25250
25251             var prevValid = ctrl.$valid;
25252             var prevModelValue = ctrl.$modelValue;
25253
25254             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25255
25256             ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25257               // If there was no change in validity, don't update the model
25258               // This prevents changing an invalid modelValue to undefined
25259               if (!allowInvalid && prevValid !== allValid) {
25260                 // Note: Don't check ctrl.$valid here, as we could have
25261                 // external validators (e.g. calculated on the server),
25262                 // that just call $setValidity and need the model value
25263                 // to calculate their validity.
25264                 ctrl.$modelValue = allValid ? modelValue : undefined;
25265
25266                 if (ctrl.$modelValue !== prevModelValue) {
25267                   ctrl.$$writeModelToScope();
25268                 }
25269               }
25270             });
25271
25272           };
25273
25274           this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25275             currentValidationRunId++;
25276             var localValidationRunId = currentValidationRunId;
25277
25278             // check parser error
25279             if (!processParseErrors()) {
25280               validationDone(false);
25281               return;
25282             }
25283             if (!processSyncValidators()) {
25284               validationDone(false);
25285               return;
25286             }
25287             processAsyncValidators();
25288
25289             function processParseErrors() {
25290               var errorKey = ctrl.$$parserName || 'parse';
25291               if (isUndefined(parserValid)) {
25292                 setValidity(errorKey, null);
25293               } else {
25294                 if (!parserValid) {
25295                   forEach(ctrl.$validators, function(v, name) {
25296                     setValidity(name, null);
25297                   });
25298                   forEach(ctrl.$asyncValidators, function(v, name) {
25299                     setValidity(name, null);
25300                   });
25301                 }
25302                 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25303                 setValidity(errorKey, parserValid);
25304                 return parserValid;
25305               }
25306               return true;
25307             }
25308
25309             function processSyncValidators() {
25310               var syncValidatorsValid = true;
25311               forEach(ctrl.$validators, function(validator, name) {
25312                 var result = validator(modelValue, viewValue);
25313                 syncValidatorsValid = syncValidatorsValid && result;
25314                 setValidity(name, result);
25315               });
25316               if (!syncValidatorsValid) {
25317                 forEach(ctrl.$asyncValidators, function(v, name) {
25318                   setValidity(name, null);
25319                 });
25320                 return false;
25321               }
25322               return true;
25323             }
25324
25325             function processAsyncValidators() {
25326               var validatorPromises = [];
25327               var allValid = true;
25328               forEach(ctrl.$asyncValidators, function(validator, name) {
25329                 var promise = validator(modelValue, viewValue);
25330                 if (!isPromiseLike(promise)) {
25331                   throw ngModelMinErr("$asyncValidators",
25332                     "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25333                 }
25334                 setValidity(name, undefined);
25335                 validatorPromises.push(promise.then(function() {
25336                   setValidity(name, true);
25337                 }, function(error) {
25338                   allValid = false;
25339                   setValidity(name, false);
25340                 }));
25341               });
25342               if (!validatorPromises.length) {
25343                 validationDone(true);
25344               } else {
25345                 $q.all(validatorPromises).then(function() {
25346                   validationDone(allValid);
25347                 }, noop);
25348               }
25349             }
25350
25351             function setValidity(name, isValid) {
25352               if (localValidationRunId === currentValidationRunId) {
25353                 ctrl.$setValidity(name, isValid);
25354               }
25355             }
25356
25357             function validationDone(allValid) {
25358               if (localValidationRunId === currentValidationRunId) {
25359
25360                 doneCallback(allValid);
25361               }
25362             }
25363           };
25364
25365           /**
25366            * @ngdoc method
25367            * @name ngModel.NgModelController#$commitViewValue
25368            *
25369            * @description
25370            * Commit a pending update to the `$modelValue`.
25371            *
25372            * Updates may be pending by a debounced event or because the input is waiting for a some future
25373            * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25374            * usually handles calling this in response to input events.
25375            */
25376           this.$commitViewValue = function() {
25377             var viewValue = ctrl.$viewValue;
25378
25379             $timeout.cancel(pendingDebounce);
25380
25381             // If the view value has not changed then we should just exit, except in the case where there is
25382             // a native validator on the element. In this case the validation state may have changed even though
25383             // the viewValue has stayed empty.
25384             if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25385               return;
25386             }
25387             ctrl.$$lastCommittedViewValue = viewValue;
25388
25389             // change to dirty
25390             if (ctrl.$pristine) {
25391               this.$setDirty();
25392             }
25393             this.$$parseAndValidate();
25394           };
25395
25396           this.$$parseAndValidate = function() {
25397             var viewValue = ctrl.$$lastCommittedViewValue;
25398             var modelValue = viewValue;
25399             parserValid = isUndefined(modelValue) ? undefined : true;
25400
25401             if (parserValid) {
25402               for (var i = 0; i < ctrl.$parsers.length; i++) {
25403                 modelValue = ctrl.$parsers[i](modelValue);
25404                 if (isUndefined(modelValue)) {
25405                   parserValid = false;
25406                   break;
25407                 }
25408               }
25409             }
25410             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25411               // ctrl.$modelValue has not been touched yet...
25412               ctrl.$modelValue = ngModelGet($scope);
25413             }
25414             var prevModelValue = ctrl.$modelValue;
25415             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25416             ctrl.$$rawModelValue = modelValue;
25417
25418             if (allowInvalid) {
25419               ctrl.$modelValue = modelValue;
25420               writeToModelIfNeeded();
25421             }
25422
25423             // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25424             // This can happen if e.g. $setViewValue is called from inside a parser
25425             ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25426               if (!allowInvalid) {
25427                 // Note: Don't check ctrl.$valid here, as we could have
25428                 // external validators (e.g. calculated on the server),
25429                 // that just call $setValidity and need the model value
25430                 // to calculate their validity.
25431                 ctrl.$modelValue = allValid ? modelValue : undefined;
25432                 writeToModelIfNeeded();
25433               }
25434             });
25435
25436             function writeToModelIfNeeded() {
25437               if (ctrl.$modelValue !== prevModelValue) {
25438                 ctrl.$$writeModelToScope();
25439               }
25440             }
25441           };
25442
25443           this.$$writeModelToScope = function() {
25444             ngModelSet($scope, ctrl.$modelValue);
25445             forEach(ctrl.$viewChangeListeners, function(listener) {
25446               try {
25447                 listener();
25448               } catch (e) {
25449                 $exceptionHandler(e);
25450               }
25451             });
25452           };
25453
25454           /**
25455            * @ngdoc method
25456            * @name ngModel.NgModelController#$setViewValue
25457            *
25458            * @description
25459            * Update the view value.
25460            *
25461            * This method should be called when a control wants to change the view value; typically,
25462            * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25463            * directive calls it when the value of the input changes and {@link ng.directive:select select}
25464            * calls it when an option is selected.
25465            *
25466            * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25467            * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25468            * value sent directly for processing, finally to be applied to `$modelValue` and then the
25469            * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25470            * in the `$viewChangeListeners` list, are called.
25471            *
25472            * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25473            * and the `default` trigger is not listed, all those actions will remain pending until one of the
25474            * `updateOn` events is triggered on the DOM element.
25475            * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25476            * directive is used with a custom debounce for this particular event.
25477            * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25478            * is specified, once the timer runs out.
25479            *
25480            * When used with standard inputs, the view value will always be a string (which is in some cases
25481            * parsed into another type, such as a `Date` object for `input[date]`.)
25482            * However, custom controls might also pass objects to this method. In this case, we should make
25483            * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25484            * perform a deep watch of objects, it only looks for a change of identity. If you only change
25485            * the property of the object then ngModel will not realise that the object has changed and
25486            * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25487            * not change properties of the copy once it has been passed to `$setViewValue`.
25488            * Otherwise you may cause the model value on the scope to change incorrectly.
25489            *
25490            * <div class="alert alert-info">
25491            * In any case, the value passed to the method should always reflect the current value
25492            * of the control. For example, if you are calling `$setViewValue` for an input element,
25493            * you should pass the input DOM value. Otherwise, the control and the scope model become
25494            * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25495            * the control's DOM value in any way. If we want to change the control's DOM value
25496            * programmatically, we should update the `ngModel` scope expression. Its new value will be
25497            * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25498            * to update the DOM, and finally call `$validate` on it.
25499            * </div>
25500            *
25501            * @param {*} value value from the view.
25502            * @param {string} trigger Event that triggered the update.
25503            */
25504           this.$setViewValue = function(value, trigger) {
25505             ctrl.$viewValue = value;
25506             if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25507               ctrl.$$debounceViewValueCommit(trigger);
25508             }
25509           };
25510
25511           this.$$debounceViewValueCommit = function(trigger) {
25512             var debounceDelay = 0,
25513                 options = ctrl.$options,
25514                 debounce;
25515
25516             if (options && isDefined(options.debounce)) {
25517               debounce = options.debounce;
25518               if (isNumber(debounce)) {
25519                 debounceDelay = debounce;
25520               } else if (isNumber(debounce[trigger])) {
25521                 debounceDelay = debounce[trigger];
25522               } else if (isNumber(debounce['default'])) {
25523                 debounceDelay = debounce['default'];
25524               }
25525             }
25526
25527             $timeout.cancel(pendingDebounce);
25528             if (debounceDelay) {
25529               pendingDebounce = $timeout(function() {
25530                 ctrl.$commitViewValue();
25531               }, debounceDelay);
25532             } else if ($rootScope.$$phase) {
25533               ctrl.$commitViewValue();
25534             } else {
25535               $scope.$apply(function() {
25536                 ctrl.$commitViewValue();
25537               });
25538             }
25539           };
25540
25541           // model -> value
25542           // Note: we cannot use a normal scope.$watch as we want to detect the following:
25543           // 1. scope value is 'a'
25544           // 2. user enters 'b'
25545           // 3. ng-change kicks in and reverts scope value to 'a'
25546           //    -> scope value did not change since the last digest as
25547           //       ng-change executes in apply phase
25548           // 4. view should be changed back to 'a'
25549           $scope.$watch(function ngModelWatch() {
25550             var modelValue = ngModelGet($scope);
25551
25552             // if scope model value and ngModel value are out of sync
25553             // TODO(perf): why not move this to the action fn?
25554             if (modelValue !== ctrl.$modelValue &&
25555                // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25556                (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25557             ) {
25558               ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25559               parserValid = undefined;
25560
25561               var formatters = ctrl.$formatters,
25562                   idx = formatters.length;
25563
25564               var viewValue = modelValue;
25565               while (idx--) {
25566                 viewValue = formatters[idx](viewValue);
25567               }
25568               if (ctrl.$viewValue !== viewValue) {
25569                 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25570                 ctrl.$render();
25571
25572                 ctrl.$$runValidators(modelValue, viewValue, noop);
25573               }
25574             }
25575
25576             return modelValue;
25577           });
25578         }];
25579
25580
25581         /**
25582          * @ngdoc directive
25583          * @name ngModel
25584          *
25585          * @element input
25586          * @priority 1
25587          *
25588          * @description
25589          * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25590          * property on the scope using {@link ngModel.NgModelController NgModelController},
25591          * which is created and exposed by this directive.
25592          *
25593          * `ngModel` is responsible for:
25594          *
25595          * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25596          *   require.
25597          * - Providing validation behavior (i.e. required, number, email, url).
25598          * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25599          * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25600          * - Registering the control with its parent {@link ng.directive:form form}.
25601          *
25602          * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25603          * current scope. If the property doesn't already exist on this scope, it will be created
25604          * implicitly and added to the scope.
25605          *
25606          * For best practices on using `ngModel`, see:
25607          *
25608          *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25609          *
25610          * For basic examples, how to use `ngModel`, see:
25611          *
25612          *  - {@link ng.directive:input input}
25613          *    - {@link input[text] text}
25614          *    - {@link input[checkbox] checkbox}
25615          *    - {@link input[radio] radio}
25616          *    - {@link input[number] number}
25617          *    - {@link input[email] email}
25618          *    - {@link input[url] url}
25619          *    - {@link input[date] date}
25620          *    - {@link input[datetime-local] datetime-local}
25621          *    - {@link input[time] time}
25622          *    - {@link input[month] month}
25623          *    - {@link input[week] week}
25624          *  - {@link ng.directive:select select}
25625          *  - {@link ng.directive:textarea textarea}
25626          *
25627          * # CSS classes
25628          * The following CSS classes are added and removed on the associated input/select/textarea element
25629          * depending on the validity of the model.
25630          *
25631          *  - `ng-valid`: the model is valid
25632          *  - `ng-invalid`: the model is invalid
25633          *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25634          *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25635          *  - `ng-pristine`: the control hasn't been interacted with yet
25636          *  - `ng-dirty`: the control has been interacted with
25637          *  - `ng-touched`: the control has been blurred
25638          *  - `ng-untouched`: the control hasn't been blurred
25639          *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25640          *
25641          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25642          *
25643          * ## Animation Hooks
25644          *
25645          * Animations within models are triggered when any of the associated CSS classes are added and removed
25646          * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25647          * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25648          * The animations that are triggered within ngModel are similar to how they work in ngClass and
25649          * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25650          *
25651          * The following example shows a simple way to utilize CSS transitions to style an input element
25652          * that has been rendered as invalid after it has been validated:
25653          *
25654          * <pre>
25655          * //be sure to include ngAnimate as a module to hook into more
25656          * //advanced animations
25657          * .my-input {
25658          *   transition:0.5s linear all;
25659          *   background: white;
25660          * }
25661          * .my-input.ng-invalid {
25662          *   background: red;
25663          *   color:white;
25664          * }
25665          * </pre>
25666          *
25667          * @example
25668          * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25669              <file name="index.html">
25670                <script>
25671                 angular.module('inputExample', [])
25672                   .controller('ExampleController', ['$scope', function($scope) {
25673                     $scope.val = '1';
25674                   }]);
25675                </script>
25676                <style>
25677                  .my-input {
25678                    transition:all linear 0.5s;
25679                    background: transparent;
25680                  }
25681                  .my-input.ng-invalid {
25682                    color:white;
25683                    background: red;
25684                  }
25685                </style>
25686                <p id="inputDescription">
25687                 Update input to see transitions when valid/invalid.
25688                 Integer is a valid value.
25689                </p>
25690                <form name="testForm" ng-controller="ExampleController">
25691                  <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25692                         aria-describedby="inputDescription" />
25693                </form>
25694              </file>
25695          * </example>
25696          *
25697          * ## Binding to a getter/setter
25698          *
25699          * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25700          * function that returns a representation of the model when called with zero arguments, and sets
25701          * the internal state of a model when called with an argument. It's sometimes useful to use this
25702          * for models that have an internal representation that's different from what the model exposes
25703          * to the view.
25704          *
25705          * <div class="alert alert-success">
25706          * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25707          * frequently than other parts of your code.
25708          * </div>
25709          *
25710          * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25711          * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25712          * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25713          * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25714          *
25715          * The following example shows how to use `ngModel` with a getter/setter:
25716          *
25717          * @example
25718          * <example name="ngModel-getter-setter" module="getterSetterExample">
25719              <file name="index.html">
25720                <div ng-controller="ExampleController">
25721                  <form name="userForm">
25722                    <label>Name:
25723                      <input type="text" name="userName"
25724                             ng-model="user.name"
25725                             ng-model-options="{ getterSetter: true }" />
25726                    </label>
25727                  </form>
25728                  <pre>user.name = <span ng-bind="user.name()"></span></pre>
25729                </div>
25730              </file>
25731              <file name="app.js">
25732                angular.module('getterSetterExample', [])
25733                  .controller('ExampleController', ['$scope', function($scope) {
25734                    var _name = 'Brian';
25735                    $scope.user = {
25736                      name: function(newName) {
25737                       // Note that newName can be undefined for two reasons:
25738                       // 1. Because it is called as a getter and thus called with no arguments
25739                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25740                       //    input is invalid
25741                       return arguments.length ? (_name = newName) : _name;
25742                      }
25743                    };
25744                  }]);
25745              </file>
25746          * </example>
25747          */
25748         var ngModelDirective = ['$rootScope', function($rootScope) {
25749           return {
25750             restrict: 'A',
25751             require: ['ngModel', '^?form', '^?ngModelOptions'],
25752             controller: NgModelController,
25753             // Prelink needs to run before any input directive
25754             // so that we can set the NgModelOptions in NgModelController
25755             // before anyone else uses it.
25756             priority: 1,
25757             compile: function ngModelCompile(element) {
25758               // Setup initial state of the control
25759               element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25760
25761               return {
25762                 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25763                   var modelCtrl = ctrls[0],
25764                       formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25765
25766                   modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25767
25768                   // notify others, especially parent forms
25769                   formCtrl.$addControl(modelCtrl);
25770
25771                   attr.$observe('name', function(newValue) {
25772                     if (modelCtrl.$name !== newValue) {
25773                       modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25774                     }
25775                   });
25776
25777                   scope.$on('$destroy', function() {
25778                     modelCtrl.$$parentForm.$removeControl(modelCtrl);
25779                   });
25780                 },
25781                 post: function ngModelPostLink(scope, element, attr, ctrls) {
25782                   var modelCtrl = ctrls[0];
25783                   if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25784                     element.on(modelCtrl.$options.updateOn, function(ev) {
25785                       modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25786                     });
25787                   }
25788
25789                   element.on('blur', function(ev) {
25790                     if (modelCtrl.$touched) return;
25791
25792                     if ($rootScope.$$phase) {
25793                       scope.$evalAsync(modelCtrl.$setTouched);
25794                     } else {
25795                       scope.$apply(modelCtrl.$setTouched);
25796                     }
25797                   });
25798                 }
25799               };
25800             }
25801           };
25802         }];
25803
25804         var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25805
25806         /**
25807          * @ngdoc directive
25808          * @name ngModelOptions
25809          *
25810          * @description
25811          * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25812          * events that will trigger a model update and/or a debouncing delay so that the actual update only
25813          * takes place when a timer expires; this timer will be reset after another change takes place.
25814          *
25815          * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25816          * be different from the value in the actual model. This means that if you update the model you
25817          * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25818          * order to make sure it is synchronized with the model and that any debounced action is canceled.
25819          *
25820          * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25821          * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25822          * important because `form` controllers are published to the related scope under the name in their
25823          * `name` attribute.
25824          *
25825          * Any pending changes will take place immediately when an enclosing form is submitted via the
25826          * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25827          * to have access to the updated model.
25828          *
25829          * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25830          *
25831          * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25832          *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25833          *     events using an space delimited list. There is a special event called `default` that
25834          *     matches the default events belonging of the control.
25835          *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25836          *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25837          *     custom value for each event. For example:
25838          *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25839          *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25840          *     not validate correctly instead of the default behavior of setting the model to undefined.
25841          *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25842                `ngModel` as getters/setters.
25843          *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25844          *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25845          *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25846          *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25847          *     If not specified, the timezone of the browser will be used.
25848          *
25849          * @example
25850
25851           The following example shows how to override immediate updates. Changes on the inputs within the
25852           form will update the model only when the control loses focus (blur event). If `escape` key is
25853           pressed while the input field is focused, the value is reset to the value in the current model.
25854
25855           <example name="ngModelOptions-directive-blur" module="optionsExample">
25856             <file name="index.html">
25857               <div ng-controller="ExampleController">
25858                 <form name="userForm">
25859                   <label>Name:
25860                     <input type="text" name="userName"
25861                            ng-model="user.name"
25862                            ng-model-options="{ updateOn: 'blur' }"
25863                            ng-keyup="cancel($event)" />
25864                   </label><br />
25865                   <label>Other data:
25866                     <input type="text" ng-model="user.data" />
25867                   </label><br />
25868                 </form>
25869                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25870                 <pre>user.data = <span ng-bind="user.data"></span></pre>
25871               </div>
25872             </file>
25873             <file name="app.js">
25874               angular.module('optionsExample', [])
25875                 .controller('ExampleController', ['$scope', function($scope) {
25876                   $scope.user = { name: 'John', data: '' };
25877
25878                   $scope.cancel = function(e) {
25879                     if (e.keyCode == 27) {
25880                       $scope.userForm.userName.$rollbackViewValue();
25881                     }
25882                   };
25883                 }]);
25884             </file>
25885             <file name="protractor.js" type="protractor">
25886               var model = element(by.binding('user.name'));
25887               var input = element(by.model('user.name'));
25888               var other = element(by.model('user.data'));
25889
25890               it('should allow custom events', function() {
25891                 input.sendKeys(' Doe');
25892                 input.click();
25893                 expect(model.getText()).toEqual('John');
25894                 other.click();
25895                 expect(model.getText()).toEqual('John Doe');
25896               });
25897
25898               it('should $rollbackViewValue when model changes', function() {
25899                 input.sendKeys(' Doe');
25900                 expect(input.getAttribute('value')).toEqual('John Doe');
25901                 input.sendKeys(protractor.Key.ESCAPE);
25902                 expect(input.getAttribute('value')).toEqual('John');
25903                 other.click();
25904                 expect(model.getText()).toEqual('John');
25905               });
25906             </file>
25907           </example>
25908
25909           This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25910           If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25911
25912           <example name="ngModelOptions-directive-debounce" module="optionsExample">
25913             <file name="index.html">
25914               <div ng-controller="ExampleController">
25915                 <form name="userForm">
25916                   <label>Name:
25917                     <input type="text" name="userName"
25918                            ng-model="user.name"
25919                            ng-model-options="{ debounce: 1000 }" />
25920                   </label>
25921                   <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25922                   <br />
25923                 </form>
25924                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25925               </div>
25926             </file>
25927             <file name="app.js">
25928               angular.module('optionsExample', [])
25929                 .controller('ExampleController', ['$scope', function($scope) {
25930                   $scope.user = { name: 'Igor' };
25931                 }]);
25932             </file>
25933           </example>
25934
25935           This one shows how to bind to getter/setters:
25936
25937           <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25938             <file name="index.html">
25939               <div ng-controller="ExampleController">
25940                 <form name="userForm">
25941                   <label>Name:
25942                     <input type="text" name="userName"
25943                            ng-model="user.name"
25944                            ng-model-options="{ getterSetter: true }" />
25945                   </label>
25946                 </form>
25947                 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25948               </div>
25949             </file>
25950             <file name="app.js">
25951               angular.module('getterSetterExample', [])
25952                 .controller('ExampleController', ['$scope', function($scope) {
25953                   var _name = 'Brian';
25954                   $scope.user = {
25955                     name: function(newName) {
25956                       // Note that newName can be undefined for two reasons:
25957                       // 1. Because it is called as a getter and thus called with no arguments
25958                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25959                       //    input is invalid
25960                       return arguments.length ? (_name = newName) : _name;
25961                     }
25962                   };
25963                 }]);
25964             </file>
25965           </example>
25966          */
25967         var ngModelOptionsDirective = function() {
25968           return {
25969             restrict: 'A',
25970             controller: ['$scope', '$attrs', function($scope, $attrs) {
25971               var that = this;
25972               this.$options = copy($scope.$eval($attrs.ngModelOptions));
25973               // Allow adding/overriding bound events
25974               if (isDefined(this.$options.updateOn)) {
25975                 this.$options.updateOnDefault = false;
25976                 // extract "default" pseudo-event from list of events that can trigger a model update
25977                 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25978                   that.$options.updateOnDefault = true;
25979                   return ' ';
25980                 }));
25981               } else {
25982                 this.$options.updateOnDefault = true;
25983               }
25984             }]
25985           };
25986         };
25987
25988
25989
25990         // helper methods
25991         function addSetValidityMethod(context) {
25992           var ctrl = context.ctrl,
25993               $element = context.$element,
25994               classCache = {},
25995               set = context.set,
25996               unset = context.unset,
25997               $animate = context.$animate;
25998
25999           classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
26000
26001           ctrl.$setValidity = setValidity;
26002
26003           function setValidity(validationErrorKey, state, controller) {
26004             if (isUndefined(state)) {
26005               createAndSet('$pending', validationErrorKey, controller);
26006             } else {
26007               unsetAndCleanup('$pending', validationErrorKey, controller);
26008             }
26009             if (!isBoolean(state)) {
26010               unset(ctrl.$error, validationErrorKey, controller);
26011               unset(ctrl.$$success, validationErrorKey, controller);
26012             } else {
26013               if (state) {
26014                 unset(ctrl.$error, validationErrorKey, controller);
26015                 set(ctrl.$$success, validationErrorKey, controller);
26016               } else {
26017                 set(ctrl.$error, validationErrorKey, controller);
26018                 unset(ctrl.$$success, validationErrorKey, controller);
26019               }
26020             }
26021             if (ctrl.$pending) {
26022               cachedToggleClass(PENDING_CLASS, true);
26023               ctrl.$valid = ctrl.$invalid = undefined;
26024               toggleValidationCss('', null);
26025             } else {
26026               cachedToggleClass(PENDING_CLASS, false);
26027               ctrl.$valid = isObjectEmpty(ctrl.$error);
26028               ctrl.$invalid = !ctrl.$valid;
26029               toggleValidationCss('', ctrl.$valid);
26030             }
26031
26032             // re-read the state as the set/unset methods could have
26033             // combined state in ctrl.$error[validationError] (used for forms),
26034             // where setting/unsetting only increments/decrements the value,
26035             // and does not replace it.
26036             var combinedState;
26037             if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26038               combinedState = undefined;
26039             } else if (ctrl.$error[validationErrorKey]) {
26040               combinedState = false;
26041             } else if (ctrl.$$success[validationErrorKey]) {
26042               combinedState = true;
26043             } else {
26044               combinedState = null;
26045             }
26046
26047             toggleValidationCss(validationErrorKey, combinedState);
26048             ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26049           }
26050
26051           function createAndSet(name, value, controller) {
26052             if (!ctrl[name]) {
26053               ctrl[name] = {};
26054             }
26055             set(ctrl[name], value, controller);
26056           }
26057
26058           function unsetAndCleanup(name, value, controller) {
26059             if (ctrl[name]) {
26060               unset(ctrl[name], value, controller);
26061             }
26062             if (isObjectEmpty(ctrl[name])) {
26063               ctrl[name] = undefined;
26064             }
26065           }
26066
26067           function cachedToggleClass(className, switchValue) {
26068             if (switchValue && !classCache[className]) {
26069               $animate.addClass($element, className);
26070               classCache[className] = true;
26071             } else if (!switchValue && classCache[className]) {
26072               $animate.removeClass($element, className);
26073               classCache[className] = false;
26074             }
26075           }
26076
26077           function toggleValidationCss(validationErrorKey, isValid) {
26078             validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26079
26080             cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26081             cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26082           }
26083         }
26084
26085         function isObjectEmpty(obj) {
26086           if (obj) {
26087             for (var prop in obj) {
26088               if (obj.hasOwnProperty(prop)) {
26089                 return false;
26090               }
26091             }
26092           }
26093           return true;
26094         }
26095
26096         /**
26097          * @ngdoc directive
26098          * @name ngNonBindable
26099          * @restrict AC
26100          * @priority 1000
26101          *
26102          * @description
26103          * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26104          * DOM element. This is useful if the element contains what appears to be Angular directives and
26105          * bindings but which should be ignored by Angular. This could be the case if you have a site that
26106          * displays snippets of code, for instance.
26107          *
26108          * @element ANY
26109          *
26110          * @example
26111          * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26112          * but the one wrapped in `ngNonBindable` is left alone.
26113          *
26114          * @example
26115             <example>
26116               <file name="index.html">
26117                 <div>Normal: {{1 + 2}}</div>
26118                 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26119               </file>
26120               <file name="protractor.js" type="protractor">
26121                it('should check ng-non-bindable', function() {
26122                  expect(element(by.binding('1 + 2')).getText()).toContain('3');
26123                  expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26124                });
26125               </file>
26126             </example>
26127          */
26128         var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26129
26130         /* global jqLiteRemove */
26131
26132         var ngOptionsMinErr = minErr('ngOptions');
26133
26134         /**
26135          * @ngdoc directive
26136          * @name ngOptions
26137          * @restrict A
26138          *
26139          * @description
26140          *
26141          * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26142          * elements for the `<select>` element using the array or object obtained by evaluating the
26143          * `ngOptions` comprehension expression.
26144          *
26145          * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26146          * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26147          * increasing speed by not creating a new scope for each repeated instance, as well as providing
26148          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26149          * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26150          *  to a non-string value. This is because an option element can only be bound to string values at
26151          * present.
26152          *
26153          * When an item in the `<select>` menu is selected, the array element or object property
26154          * represented by the selected option will be bound to the model identified by the `ngModel`
26155          * directive.
26156          *
26157          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26158          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26159          * option. See example below for demonstration.
26160          *
26161          * ## Complex Models (objects or collections)
26162          *
26163          * By default, `ngModel` watches the model by reference, not value. This is important to know when
26164          * binding the select to a model that is an object or a collection.
26165          *
26166          * One issue occurs if you want to preselect an option. For example, if you set
26167          * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26168          * because the objects are not identical. So by default, you should always reference the item in your collection
26169          * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26170          *
26171          * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26172          * of the item not by reference, but by the result of the `track by` expression. For example, if your
26173          * collection items have an id property, you would `track by item.id`.
26174          *
26175          * A different issue with objects or collections is that ngModel won't detect if an object property or
26176          * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26177          * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26178          * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26179          * has not changed identity, but only a property on the object or an item in the collection changes.
26180          *
26181          * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26182          * if the model is an array). This means that changing a property deeper than the first level inside the
26183          * object/collection will not trigger a re-rendering.
26184          *
26185          * ## `select` **`as`**
26186          *
26187          * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26188          * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26189          * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26190          * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26191          *
26192          *
26193          * ### `select` **`as`** and **`track by`**
26194          *
26195          * <div class="alert alert-warning">
26196          * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26197          * </div>
26198          *
26199          * Given this array of items on the $scope:
26200          *
26201          * ```js
26202          * $scope.items = [{
26203          *   id: 1,
26204          *   label: 'aLabel',
26205          *   subItem: { name: 'aSubItem' }
26206          * }, {
26207          *   id: 2,
26208          *   label: 'bLabel',
26209          *   subItem: { name: 'bSubItem' }
26210          * }];
26211          * ```
26212          *
26213          * This will work:
26214          *
26215          * ```html
26216          * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26217          * ```
26218          * ```js
26219          * $scope.selected = $scope.items[0];
26220          * ```
26221          *
26222          * but this will not work:
26223          *
26224          * ```html
26225          * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26226          * ```
26227          * ```js
26228          * $scope.selected = $scope.items[0].subItem;
26229          * ```
26230          *
26231          * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26232          * `items` array. Because the selected option has been set programmatically in the controller, the
26233          * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26234          * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26235          * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26236          * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26237          * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26238          *
26239          *
26240          * @param {string} ngModel Assignable angular expression to data-bind to.
26241          * @param {string=} name Property name of the form under which the control is published.
26242          * @param {string=} required The control is considered valid only if value is entered.
26243          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26244          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26245          *    `required` when you want to data-bind to the `required` attribute.
26246          * @param {comprehension_expression=} ngOptions in one of the following forms:
26247          *
26248          *   * for array data sources:
26249          *     * `label` **`for`** `value` **`in`** `array`
26250          *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26251          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26252          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26253          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26254          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26255          *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26256          *        (for including a filter with `track by`)
26257          *   * for object data sources:
26258          *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26259          *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26260          *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26261          *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26262          *     * `select` **`as`** `label` **`group by`** `group`
26263          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26264          *     * `select` **`as`** `label` **`disable when`** `disable`
26265          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26266          *
26267          * Where:
26268          *
26269          *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26270          *   * `value`: local variable which will refer to each item in the `array` or each property value
26271          *      of `object` during iteration.
26272          *   * `key`: local variable which will refer to a property name in `object` during iteration.
26273          *   * `label`: The result of this expression will be the label for `<option>` element. The
26274          *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26275          *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26276          *      element. If not specified, `select` expression will default to `value`.
26277          *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26278          *      DOM element.
26279          *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26280          *      element. Return `true` to disable.
26281          *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26282          *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26283          *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26284          *      even when the options are recreated (e.g. reloaded from the server).
26285          *
26286          * @example
26287             <example module="selectExample">
26288               <file name="index.html">
26289                 <script>
26290                 angular.module('selectExample', [])
26291                   .controller('ExampleController', ['$scope', function($scope) {
26292                     $scope.colors = [
26293                       {name:'black', shade:'dark'},
26294                       {name:'white', shade:'light', notAnOption: true},
26295                       {name:'red', shade:'dark'},
26296                       {name:'blue', shade:'dark', notAnOption: true},
26297                       {name:'yellow', shade:'light', notAnOption: false}
26298                     ];
26299                     $scope.myColor = $scope.colors[2]; // red
26300                   }]);
26301                 </script>
26302                 <div ng-controller="ExampleController">
26303                   <ul>
26304                     <li ng-repeat="color in colors">
26305                       <label>Name: <input ng-model="color.name"></label>
26306                       <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26307                       <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26308                     </li>
26309                     <li>
26310                       <button ng-click="colors.push({})">add</button>
26311                     </li>
26312                   </ul>
26313                   <hr/>
26314                   <label>Color (null not allowed):
26315                     <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26316                   </label><br/>
26317                   <label>Color (null allowed):
26318                   <span  class="nullable">
26319                     <select ng-model="myColor" ng-options="color.name for color in colors">
26320                       <option value="">-- choose color --</option>
26321                     </select>
26322                   </span></label><br/>
26323
26324                   <label>Color grouped by shade:
26325                     <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26326                     </select>
26327                   </label><br/>
26328
26329                   <label>Color grouped by shade, with some disabled:
26330                     <select ng-model="myColor"
26331                           ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26332                     </select>
26333                   </label><br/>
26334
26335
26336
26337                   Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26338                   <br/>
26339                   <hr/>
26340                   Currently selected: {{ {selected_color:myColor} }}
26341                   <div style="border:solid 1px black; height:20px"
26342                        ng-style="{'background-color':myColor.name}">
26343                   </div>
26344                 </div>
26345               </file>
26346               <file name="protractor.js" type="protractor">
26347                  it('should check ng-options', function() {
26348                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26349                    element.all(by.model('myColor')).first().click();
26350                    element.all(by.css('select[ng-model="myColor"] option')).first().click();
26351                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26352                    element(by.css('.nullable select[ng-model="myColor"]')).click();
26353                    element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26354                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26355                  });
26356               </file>
26357             </example>
26358          */
26359
26360         // jshint maxlen: false
26361         //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26362         var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
26363                                 // 1: value expression (valueFn)
26364                                 // 2: label expression (displayFn)
26365                                 // 3: group by expression (groupByFn)
26366                                 // 4: disable when expression (disableWhenFn)
26367                                 // 5: array item variable name
26368                                 // 6: object item key variable name
26369                                 // 7: object item value variable name
26370                                 // 8: collection expression
26371                                 // 9: track by expression
26372         // jshint maxlen: 100
26373
26374
26375         var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26376
26377           function parseOptionsExpression(optionsExp, selectElement, scope) {
26378
26379             var match = optionsExp.match(NG_OPTIONS_REGEXP);
26380             if (!(match)) {
26381               throw ngOptionsMinErr('iexp',
26382                 "Expected expression in form of " +
26383                 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26384                 " but got '{0}'. Element: {1}",
26385                 optionsExp, startingTag(selectElement));
26386             }
26387
26388             // Extract the parts from the ngOptions expression
26389
26390             // The variable name for the value of the item in the collection
26391             var valueName = match[5] || match[7];
26392             // The variable name for the key of the item in the collection
26393             var keyName = match[6];
26394
26395             // An expression that generates the viewValue for an option if there is a label expression
26396             var selectAs = / as /.test(match[0]) && match[1];
26397             // An expression that is used to track the id of each object in the options collection
26398             var trackBy = match[9];
26399             // An expression that generates the viewValue for an option if there is no label expression
26400             var valueFn = $parse(match[2] ? match[1] : valueName);
26401             var selectAsFn = selectAs && $parse(selectAs);
26402             var viewValueFn = selectAsFn || valueFn;
26403             var trackByFn = trackBy && $parse(trackBy);
26404
26405             // Get the value by which we are going to track the option
26406             // if we have a trackFn then use that (passing scope and locals)
26407             // otherwise just hash the given viewValue
26408             var getTrackByValueFn = trackBy ?
26409                                       function(value, locals) { return trackByFn(scope, locals); } :
26410                                       function getHashOfValue(value) { return hashKey(value); };
26411             var getTrackByValue = function(value, key) {
26412               return getTrackByValueFn(value, getLocals(value, key));
26413             };
26414
26415             var displayFn = $parse(match[2] || match[1]);
26416             var groupByFn = $parse(match[3] || '');
26417             var disableWhenFn = $parse(match[4] || '');
26418             var valuesFn = $parse(match[8]);
26419
26420             var locals = {};
26421             var getLocals = keyName ? function(value, key) {
26422               locals[keyName] = key;
26423               locals[valueName] = value;
26424               return locals;
26425             } : function(value) {
26426               locals[valueName] = value;
26427               return locals;
26428             };
26429
26430
26431             function Option(selectValue, viewValue, label, group, disabled) {
26432               this.selectValue = selectValue;
26433               this.viewValue = viewValue;
26434               this.label = label;
26435               this.group = group;
26436               this.disabled = disabled;
26437             }
26438
26439             function getOptionValuesKeys(optionValues) {
26440               var optionValuesKeys;
26441
26442               if (!keyName && isArrayLike(optionValues)) {
26443                 optionValuesKeys = optionValues;
26444               } else {
26445                 // if object, extract keys, in enumeration order, unsorted
26446                 optionValuesKeys = [];
26447                 for (var itemKey in optionValues) {
26448                   if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26449                     optionValuesKeys.push(itemKey);
26450                   }
26451                 }
26452               }
26453               return optionValuesKeys;
26454             }
26455
26456             return {
26457               trackBy: trackBy,
26458               getTrackByValue: getTrackByValue,
26459               getWatchables: $parse(valuesFn, function(optionValues) {
26460                 // Create a collection of things that we would like to watch (watchedArray)
26461                 // so that they can all be watched using a single $watchCollection
26462                 // that only runs the handler once if anything changes
26463                 var watchedArray = [];
26464                 optionValues = optionValues || [];
26465
26466                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26467                 var optionValuesLength = optionValuesKeys.length;
26468                 for (var index = 0; index < optionValuesLength; index++) {
26469                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26470                   var value = optionValues[key];
26471
26472                   var locals = getLocals(optionValues[key], key);
26473                   var selectValue = getTrackByValueFn(optionValues[key], locals);
26474                   watchedArray.push(selectValue);
26475
26476                   // Only need to watch the displayFn if there is a specific label expression
26477                   if (match[2] || match[1]) {
26478                     var label = displayFn(scope, locals);
26479                     watchedArray.push(label);
26480                   }
26481
26482                   // Only need to watch the disableWhenFn if there is a specific disable expression
26483                   if (match[4]) {
26484                     var disableWhen = disableWhenFn(scope, locals);
26485                     watchedArray.push(disableWhen);
26486                   }
26487                 }
26488                 return watchedArray;
26489               }),
26490
26491               getOptions: function() {
26492
26493                 var optionItems = [];
26494                 var selectValueMap = {};
26495
26496                 // The option values were already computed in the `getWatchables` fn,
26497                 // which must have been called to trigger `getOptions`
26498                 var optionValues = valuesFn(scope) || [];
26499                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26500                 var optionValuesLength = optionValuesKeys.length;
26501
26502                 for (var index = 0; index < optionValuesLength; index++) {
26503                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26504                   var value = optionValues[key];
26505                   var locals = getLocals(value, key);
26506                   var viewValue = viewValueFn(scope, locals);
26507                   var selectValue = getTrackByValueFn(viewValue, locals);
26508                   var label = displayFn(scope, locals);
26509                   var group = groupByFn(scope, locals);
26510                   var disabled = disableWhenFn(scope, locals);
26511                   var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26512
26513                   optionItems.push(optionItem);
26514                   selectValueMap[selectValue] = optionItem;
26515                 }
26516
26517                 return {
26518                   items: optionItems,
26519                   selectValueMap: selectValueMap,
26520                   getOptionFromViewValue: function(value) {
26521                     return selectValueMap[getTrackByValue(value)];
26522                   },
26523                   getViewValueFromOption: function(option) {
26524                     // If the viewValue could be an object that may be mutated by the application,
26525                     // we need to make a copy and not return the reference to the value on the option.
26526                     return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26527                   }
26528                 };
26529               }
26530             };
26531           }
26532
26533
26534           // we can't just jqLite('<option>') since jqLite is not smart enough
26535           // to create it in <select> and IE barfs otherwise.
26536           var optionTemplate = document.createElement('option'),
26537               optGroupTemplate = document.createElement('optgroup');
26538
26539
26540             function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26541
26542               // if ngModel is not defined, we don't need to do anything
26543               var ngModelCtrl = ctrls[1];
26544               if (!ngModelCtrl) return;
26545
26546               var selectCtrl = ctrls[0];
26547               var multiple = attr.multiple;
26548
26549               // The emptyOption allows the application developer to provide their own custom "empty"
26550               // option when the viewValue does not match any of the option values.
26551               var emptyOption;
26552               for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26553                 if (children[i].value === '') {
26554                   emptyOption = children.eq(i);
26555                   break;
26556                 }
26557               }
26558
26559               var providedEmptyOption = !!emptyOption;
26560
26561               var unknownOption = jqLite(optionTemplate.cloneNode(false));
26562               unknownOption.val('?');
26563
26564               var options;
26565               var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26566
26567
26568               var renderEmptyOption = function() {
26569                 if (!providedEmptyOption) {
26570                   selectElement.prepend(emptyOption);
26571                 }
26572                 selectElement.val('');
26573                 emptyOption.prop('selected', true); // needed for IE
26574                 emptyOption.attr('selected', true);
26575               };
26576
26577               var removeEmptyOption = function() {
26578                 if (!providedEmptyOption) {
26579                   emptyOption.remove();
26580                 }
26581               };
26582
26583
26584               var renderUnknownOption = function() {
26585                 selectElement.prepend(unknownOption);
26586                 selectElement.val('?');
26587                 unknownOption.prop('selected', true); // needed for IE
26588                 unknownOption.attr('selected', true);
26589               };
26590
26591               var removeUnknownOption = function() {
26592                 unknownOption.remove();
26593               };
26594
26595               // Update the controller methods for multiple selectable options
26596               if (!multiple) {
26597
26598                 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26599                   var option = options.getOptionFromViewValue(value);
26600
26601                   if (option && !option.disabled) {
26602                     if (selectElement[0].value !== option.selectValue) {
26603                       removeUnknownOption();
26604                       removeEmptyOption();
26605
26606                       selectElement[0].value = option.selectValue;
26607                       option.element.selected = true;
26608                       option.element.setAttribute('selected', 'selected');
26609                     }
26610                   } else {
26611                     if (value === null || providedEmptyOption) {
26612                       removeUnknownOption();
26613                       renderEmptyOption();
26614                     } else {
26615                       removeEmptyOption();
26616                       renderUnknownOption();
26617                     }
26618                   }
26619                 };
26620
26621                 selectCtrl.readValue = function readNgOptionsValue() {
26622
26623                   var selectedOption = options.selectValueMap[selectElement.val()];
26624
26625                   if (selectedOption && !selectedOption.disabled) {
26626                     removeEmptyOption();
26627                     removeUnknownOption();
26628                     return options.getViewValueFromOption(selectedOption);
26629                   }
26630                   return null;
26631                 };
26632
26633                 // If we are using `track by` then we must watch the tracked value on the model
26634                 // since ngModel only watches for object identity change
26635                 if (ngOptions.trackBy) {
26636                   scope.$watch(
26637                     function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26638                     function() { ngModelCtrl.$render(); }
26639                   );
26640                 }
26641
26642               } else {
26643
26644                 ngModelCtrl.$isEmpty = function(value) {
26645                   return !value || value.length === 0;
26646                 };
26647
26648
26649                 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26650                   options.items.forEach(function(option) {
26651                     option.element.selected = false;
26652                   });
26653
26654                   if (value) {
26655                     value.forEach(function(item) {
26656                       var option = options.getOptionFromViewValue(item);
26657                       if (option && !option.disabled) option.element.selected = true;
26658                     });
26659                   }
26660                 };
26661
26662
26663                 selectCtrl.readValue = function readNgOptionsMultiple() {
26664                   var selectedValues = selectElement.val() || [],
26665                       selections = [];
26666
26667                   forEach(selectedValues, function(value) {
26668                     var option = options.selectValueMap[value];
26669                     if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26670                   });
26671
26672                   return selections;
26673                 };
26674
26675                 // If we are using `track by` then we must watch these tracked values on the model
26676                 // since ngModel only watches for object identity change
26677                 if (ngOptions.trackBy) {
26678
26679                   scope.$watchCollection(function() {
26680                     if (isArray(ngModelCtrl.$viewValue)) {
26681                       return ngModelCtrl.$viewValue.map(function(value) {
26682                         return ngOptions.getTrackByValue(value);
26683                       });
26684                     }
26685                   }, function() {
26686                     ngModelCtrl.$render();
26687                   });
26688
26689                 }
26690               }
26691
26692
26693               if (providedEmptyOption) {
26694
26695                 // we need to remove it before calling selectElement.empty() because otherwise IE will
26696                 // remove the label from the element. wtf?
26697                 emptyOption.remove();
26698
26699                 // compile the element since there might be bindings in it
26700                 $compile(emptyOption)(scope);
26701
26702                 // remove the class, which is added automatically because we recompile the element and it
26703                 // becomes the compilation root
26704                 emptyOption.removeClass('ng-scope');
26705               } else {
26706                 emptyOption = jqLite(optionTemplate.cloneNode(false));
26707               }
26708
26709               // We need to do this here to ensure that the options object is defined
26710               // when we first hit it in writeNgOptionsValue
26711               updateOptions();
26712
26713               // We will re-render the option elements if the option values or labels change
26714               scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26715
26716               // ------------------------------------------------------------------ //
26717
26718
26719               function updateOptionElement(option, element) {
26720                 option.element = element;
26721                 element.disabled = option.disabled;
26722                 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26723                 // selects in certain circumstances when multiple selects are next to each other and display
26724                 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26725                 // See https://github.com/angular/angular.js/issues/11314 for more info.
26726                 // This is unfortunately untestable with unit / e2e tests
26727                 if (option.label !== element.label) {
26728                   element.label = option.label;
26729                   element.textContent = option.label;
26730                 }
26731                 if (option.value !== element.value) element.value = option.selectValue;
26732               }
26733
26734               function addOrReuseElement(parent, current, type, templateElement) {
26735                 var element;
26736                 // Check whether we can reuse the next element
26737                 if (current && lowercase(current.nodeName) === type) {
26738                   // The next element is the right type so reuse it
26739                   element = current;
26740                 } else {
26741                   // The next element is not the right type so create a new one
26742                   element = templateElement.cloneNode(false);
26743                   if (!current) {
26744                     // There are no more elements so just append it to the select
26745                     parent.appendChild(element);
26746                   } else {
26747                     // The next element is not a group so insert the new one
26748                     parent.insertBefore(element, current);
26749                   }
26750                 }
26751                 return element;
26752               }
26753
26754
26755               function removeExcessElements(current) {
26756                 var next;
26757                 while (current) {
26758                   next = current.nextSibling;
26759                   jqLiteRemove(current);
26760                   current = next;
26761                 }
26762               }
26763
26764
26765               function skipEmptyAndUnknownOptions(current) {
26766                 var emptyOption_ = emptyOption && emptyOption[0];
26767                 var unknownOption_ = unknownOption && unknownOption[0];
26768
26769                 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26770                 // because the compiled empty option might have been replaced by a comment because
26771                 // it had an "element" transclusion directive on it (such as ngIf)
26772                 if (emptyOption_ || unknownOption_) {
26773                   while (current &&
26774                         (current === emptyOption_ ||
26775                         current === unknownOption_ ||
26776                         current.nodeType === NODE_TYPE_COMMENT ||
26777                         current.value === '')) {
26778                     current = current.nextSibling;
26779                   }
26780                 }
26781                 return current;
26782               }
26783
26784
26785               function updateOptions() {
26786
26787                 var previousValue = options && selectCtrl.readValue();
26788
26789                 options = ngOptions.getOptions();
26790
26791                 var groupMap = {};
26792                 var currentElement = selectElement[0].firstChild;
26793
26794                 // Ensure that the empty option is always there if it was explicitly provided
26795                 if (providedEmptyOption) {
26796                   selectElement.prepend(emptyOption);
26797                 }
26798
26799                 currentElement = skipEmptyAndUnknownOptions(currentElement);
26800
26801                 options.items.forEach(function updateOption(option) {
26802                   var group;
26803                   var groupElement;
26804                   var optionElement;
26805
26806                   if (option.group) {
26807
26808                     // This option is to live in a group
26809                     // See if we have already created this group
26810                     group = groupMap[option.group];
26811
26812                     if (!group) {
26813
26814                       // We have not already created this group
26815                       groupElement = addOrReuseElement(selectElement[0],
26816                                                        currentElement,
26817                                                        'optgroup',
26818                                                        optGroupTemplate);
26819                       // Move to the next element
26820                       currentElement = groupElement.nextSibling;
26821
26822                       // Update the label on the group element
26823                       groupElement.label = option.group;
26824
26825                       // Store it for use later
26826                       group = groupMap[option.group] = {
26827                         groupElement: groupElement,
26828                         currentOptionElement: groupElement.firstChild
26829                       };
26830
26831                     }
26832
26833                     // So now we have a group for this option we add the option to the group
26834                     optionElement = addOrReuseElement(group.groupElement,
26835                                                       group.currentOptionElement,
26836                                                       'option',
26837                                                       optionTemplate);
26838                     updateOptionElement(option, optionElement);
26839                     // Move to the next element
26840                     group.currentOptionElement = optionElement.nextSibling;
26841
26842                   } else {
26843
26844                     // This option is not in a group
26845                     optionElement = addOrReuseElement(selectElement[0],
26846                                                       currentElement,
26847                                                       'option',
26848                                                       optionTemplate);
26849                     updateOptionElement(option, optionElement);
26850                     // Move to the next element
26851                     currentElement = optionElement.nextSibling;
26852                   }
26853                 });
26854
26855
26856                 // Now remove all excess options and group
26857                 Object.keys(groupMap).forEach(function(key) {
26858                   removeExcessElements(groupMap[key].currentOptionElement);
26859                 });
26860                 removeExcessElements(currentElement);
26861
26862                 ngModelCtrl.$render();
26863
26864                 // Check to see if the value has changed due to the update to the options
26865                 if (!ngModelCtrl.$isEmpty(previousValue)) {
26866                   var nextValue = selectCtrl.readValue();
26867                   if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26868                     ngModelCtrl.$setViewValue(nextValue);
26869                     ngModelCtrl.$render();
26870                   }
26871                 }
26872
26873               }
26874           }
26875
26876           return {
26877             restrict: 'A',
26878             terminal: true,
26879             require: ['select', '?ngModel'],
26880             link: {
26881               pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26882                 // Deactivate the SelectController.register method to prevent
26883                 // option directives from accidentally registering themselves
26884                 // (and unwanted $destroy handlers etc.)
26885                 ctrls[0].registerOption = noop;
26886               },
26887               post: ngOptionsPostLink
26888             }
26889           };
26890         }];
26891
26892         /**
26893          * @ngdoc directive
26894          * @name ngPluralize
26895          * @restrict EA
26896          *
26897          * @description
26898          * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26899          * These rules are bundled with angular.js, but can be overridden
26900          * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26901          * by specifying the mappings between
26902          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26903          * and the strings to be displayed.
26904          *
26905          * # Plural categories and explicit number rules
26906          * There are two
26907          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26908          * in Angular's default en-US locale: "one" and "other".
26909          *
26910          * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26911          * any number that is not 1), an explicit number rule can only match one number. For example, the
26912          * explicit number rule for "3" matches the number 3. There are examples of plural categories
26913          * and explicit number rules throughout the rest of this documentation.
26914          *
26915          * # Configuring ngPluralize
26916          * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26917          * You can also provide an optional attribute, `offset`.
26918          *
26919          * The value of the `count` attribute can be either a string or an {@link guide/expression
26920          * Angular expression}; these are evaluated on the current scope for its bound value.
26921          *
26922          * The `when` attribute specifies the mappings between plural categories and the actual
26923          * string to be displayed. The value of the attribute should be a JSON object.
26924          *
26925          * The following example shows how to configure ngPluralize:
26926          *
26927          * ```html
26928          * <ng-pluralize count="personCount"
26929                          when="{'0': 'Nobody is viewing.',
26930          *                      'one': '1 person is viewing.',
26931          *                      'other': '{} people are viewing.'}">
26932          * </ng-pluralize>
26933          *```
26934          *
26935          * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26936          * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26937          * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26938          * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26939          * show "a dozen people are viewing".
26940          *
26941          * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26942          * into pluralized strings. In the previous example, Angular will replace `{}` with
26943          * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26944          * for <span ng-non-bindable>{{numberExpression}}</span>.
26945          *
26946          * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26947          * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26948          *
26949          * # Configuring ngPluralize with offset
26950          * The `offset` attribute allows further customization of pluralized text, which can result in
26951          * a better user experience. For example, instead of the message "4 people are viewing this document",
26952          * you might display "John, Kate and 2 others are viewing this document".
26953          * The offset attribute allows you to offset a number by any desired value.
26954          * Let's take a look at an example:
26955          *
26956          * ```html
26957          * <ng-pluralize count="personCount" offset=2
26958          *               when="{'0': 'Nobody is viewing.',
26959          *                      '1': '{{person1}} is viewing.',
26960          *                      '2': '{{person1}} and {{person2}} are viewing.',
26961          *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26962          *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26963          * </ng-pluralize>
26964          * ```
26965          *
26966          * Notice that we are still using two plural categories(one, other), but we added
26967          * three explicit number rules 0, 1 and 2.
26968          * When one person, perhaps John, views the document, "John is viewing" will be shown.
26969          * When three people view the document, no explicit number rule is found, so
26970          * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26971          * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26972          * is shown.
26973          *
26974          * Note that when you specify offsets, you must provide explicit number rules for
26975          * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26976          * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26977          * plural categories "one" and "other".
26978          *
26979          * @param {string|expression} count The variable to be bound to.
26980          * @param {string} when The mapping between plural category to its corresponding strings.
26981          * @param {number=} offset Offset to deduct from the total number.
26982          *
26983          * @example
26984             <example module="pluralizeExample">
26985               <file name="index.html">
26986                 <script>
26987                   angular.module('pluralizeExample', [])
26988                     .controller('ExampleController', ['$scope', function($scope) {
26989                       $scope.person1 = 'Igor';
26990                       $scope.person2 = 'Misko';
26991                       $scope.personCount = 1;
26992                     }]);
26993                 </script>
26994                 <div ng-controller="ExampleController">
26995                   <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26996                   <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26997                   <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26998
26999                   <!--- Example with simple pluralization rules for en locale --->
27000                   Without Offset:
27001                   <ng-pluralize count="personCount"
27002                                 when="{'0': 'Nobody is viewing.',
27003                                        'one': '1 person is viewing.',
27004                                        'other': '{} people are viewing.'}">
27005                   </ng-pluralize><br>
27006
27007                   <!--- Example with offset --->
27008                   With Offset(2):
27009                   <ng-pluralize count="personCount" offset=2
27010                                 when="{'0': 'Nobody is viewing.',
27011                                        '1': '{{person1}} is viewing.',
27012                                        '2': '{{person1}} and {{person2}} are viewing.',
27013                                        'one': '{{person1}}, {{person2}} and one other person are viewing.',
27014                                        'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27015                   </ng-pluralize>
27016                 </div>
27017               </file>
27018               <file name="protractor.js" type="protractor">
27019                 it('should show correct pluralized string', function() {
27020                   var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27021                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27022                   var countInput = element(by.model('personCount'));
27023
27024                   expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27025                   expect(withOffset.getText()).toEqual('Igor is viewing.');
27026
27027                   countInput.clear();
27028                   countInput.sendKeys('0');
27029
27030                   expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27031                   expect(withOffset.getText()).toEqual('Nobody is viewing.');
27032
27033                   countInput.clear();
27034                   countInput.sendKeys('2');
27035
27036                   expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27037                   expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27038
27039                   countInput.clear();
27040                   countInput.sendKeys('3');
27041
27042                   expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27043                   expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27044
27045                   countInput.clear();
27046                   countInput.sendKeys('4');
27047
27048                   expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27049                   expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27050                 });
27051                 it('should show data-bound names', function() {
27052                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27053                   var personCount = element(by.model('personCount'));
27054                   var person1 = element(by.model('person1'));
27055                   var person2 = element(by.model('person2'));
27056                   personCount.clear();
27057                   personCount.sendKeys('4');
27058                   person1.clear();
27059                   person1.sendKeys('Di');
27060                   person2.clear();
27061                   person2.sendKeys('Vojta');
27062                   expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27063                 });
27064               </file>
27065             </example>
27066          */
27067         var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27068           var BRACE = /{}/g,
27069               IS_WHEN = /^when(Minus)?(.+)$/;
27070
27071           return {
27072             link: function(scope, element, attr) {
27073               var numberExp = attr.count,
27074                   whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27075                   offset = attr.offset || 0,
27076                   whens = scope.$eval(whenExp) || {},
27077                   whensExpFns = {},
27078                   startSymbol = $interpolate.startSymbol(),
27079                   endSymbol = $interpolate.endSymbol(),
27080                   braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27081                   watchRemover = angular.noop,
27082                   lastCount;
27083
27084               forEach(attr, function(expression, attributeName) {
27085                 var tmpMatch = IS_WHEN.exec(attributeName);
27086                 if (tmpMatch) {
27087                   var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27088                   whens[whenKey] = element.attr(attr.$attr[attributeName]);
27089                 }
27090               });
27091               forEach(whens, function(expression, key) {
27092                 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27093
27094               });
27095
27096               scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27097                 var count = parseFloat(newVal);
27098                 var countIsNaN = isNaN(count);
27099
27100                 if (!countIsNaN && !(count in whens)) {
27101                   // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27102                   // Otherwise, check it against pluralization rules in $locale service.
27103                   count = $locale.pluralCat(count - offset);
27104                 }
27105
27106                 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27107                 // In JS `NaN !== NaN`, so we have to exlicitly check.
27108                 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27109                   watchRemover();
27110                   var whenExpFn = whensExpFns[count];
27111                   if (isUndefined(whenExpFn)) {
27112                     if (newVal != null) {
27113                       $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27114                     }
27115                     watchRemover = noop;
27116                     updateElementText();
27117                   } else {
27118                     watchRemover = scope.$watch(whenExpFn, updateElementText);
27119                   }
27120                   lastCount = count;
27121                 }
27122               });
27123
27124               function updateElementText(newText) {
27125                 element.text(newText || '');
27126               }
27127             }
27128           };
27129         }];
27130
27131         /**
27132          * @ngdoc directive
27133          * @name ngRepeat
27134          * @multiElement
27135          *
27136          * @description
27137          * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27138          * instance gets its own scope, where the given loop variable is set to the current collection item,
27139          * and `$index` is set to the item index or key.
27140          *
27141          * Special properties are exposed on the local scope of each template instance, including:
27142          *
27143          * | Variable  | Type            | Details                                                                     |
27144          * |-----------|-----------------|-----------------------------------------------------------------------------|
27145          * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27146          * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27147          * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27148          * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27149          * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27150          * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27151          *
27152          * <div class="alert alert-info">
27153          *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27154          *   This may be useful when, for instance, nesting ngRepeats.
27155          * </div>
27156          *
27157          *
27158          * # Iterating over object properties
27159          *
27160          * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27161          * syntax:
27162          *
27163          * ```js
27164          * <div ng-repeat="(key, value) in myObj"> ... </div>
27165          * ```
27166          *
27167          * You need to be aware that the JavaScript specification does not define the order of keys
27168          * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27169          * used to sort the keys alphabetically.)
27170          *
27171          * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27172          * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27173          * keys in the order in which they were defined, although there are exceptions when keys are deleted
27174          * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27175          *
27176          * If this is not desired, the recommended workaround is to convert your object into an array
27177          * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27178          * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27179          * or implement a `$watch` on the object yourself.
27180          *
27181          *
27182          * # Tracking and Duplicates
27183          *
27184          * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27185          * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27186          *
27187          * * When an item is added, a new instance of the template is added to the DOM.
27188          * * When an item is removed, its template instance is removed from the DOM.
27189          * * When items are reordered, their respective templates are reordered in the DOM.
27190          *
27191          * To minimize creation of DOM elements, `ngRepeat` uses a function
27192          * to "keep track" of all items in the collection and their corresponding DOM elements.
27193          * For example, if an item is added to the collection, ngRepeat will know that all other items
27194          * already have DOM elements, and will not re-render them.
27195          *
27196          * The default tracking function (which tracks items by their identity) does not allow
27197          * duplicate items in arrays. This is because when there are duplicates, it is not possible
27198          * to maintain a one-to-one mapping between collection items and DOM elements.
27199          *
27200          * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27201          * with your own using the `track by` expression.
27202          *
27203          * For example, you may track items by the index of each item in the collection, using the
27204          * special scope property `$index`:
27205          * ```html
27206          *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27207          *      {{n}}
27208          *    </div>
27209          * ```
27210          *
27211          * You may also use arbitrary expressions in `track by`, including references to custom functions
27212          * on the scope:
27213          * ```html
27214          *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27215          *      {{n}}
27216          *    </div>
27217          * ```
27218          *
27219          * <div class="alert alert-success">
27220          * If you are working with objects that have an identifier property, you should track
27221          * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27222          * will not have to rebuild the DOM elements for items it has already rendered, even if the
27223          * JavaScript objects in the collection have been substituted for new ones. For large collections,
27224          * this signifincantly improves rendering performance. If you don't have a unique identifier,
27225          * `track by $index` can also provide a performance boost.
27226          * </div>
27227          * ```html
27228          *    <div ng-repeat="model in collection track by model.id">
27229          *      {{model.name}}
27230          *    </div>
27231          * ```
27232          *
27233          * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27234          * `$id` function, which tracks items by their identity:
27235          * ```html
27236          *    <div ng-repeat="obj in collection track by $id(obj)">
27237          *      {{obj.prop}}
27238          *    </div>
27239          * ```
27240          *
27241          * <div class="alert alert-warning">
27242          * **Note:** `track by` must always be the last expression:
27243          * </div>
27244          * ```
27245          * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27246          *     {{model.name}}
27247          * </div>
27248          * ```
27249          *
27250          * # Special repeat start and end points
27251          * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27252          * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27253          * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
27254          * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27255          *
27256          * The example below makes use of this feature:
27257          * ```html
27258          *   <header ng-repeat-start="item in items">
27259          *     Header {{ item }}
27260          *   </header>
27261          *   <div class="body">
27262          *     Body {{ item }}
27263          *   </div>
27264          *   <footer ng-repeat-end>
27265          *     Footer {{ item }}
27266          *   </footer>
27267          * ```
27268          *
27269          * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27270          * ```html
27271          *   <header>
27272          *     Header A
27273          *   </header>
27274          *   <div class="body">
27275          *     Body A
27276          *   </div>
27277          *   <footer>
27278          *     Footer A
27279          *   </footer>
27280          *   <header>
27281          *     Header B
27282          *   </header>
27283          *   <div class="body">
27284          *     Body B
27285          *   </div>
27286          *   <footer>
27287          *     Footer B
27288          *   </footer>
27289          * ```
27290          *
27291          * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27292          * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27293          *
27294          * @animations
27295          * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27296          *
27297          * **.leave** - when an item is removed from the list or when an item is filtered out
27298          *
27299          * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27300          *
27301          * @element ANY
27302          * @scope
27303          * @priority 1000
27304          * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27305          *   formats are currently supported:
27306          *
27307          *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27308          *     is a scope expression giving the collection to enumerate.
27309          *
27310          *     For example: `album in artist.albums`.
27311          *
27312          *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27313          *     and `expression` is the scope expression giving the collection to enumerate.
27314          *
27315          *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27316          *
27317          *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27318          *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27319          *     is specified, ng-repeat associates elements by identity. It is an error to have
27320          *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27321          *     mapped to the same DOM element, which is not possible.)
27322          *
27323          *     Note that the tracking expression must come last, after any filters, and the alias expression.
27324          *
27325          *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27326          *     will be associated by item identity in the array.
27327          *
27328          *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27329          *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27330          *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27331          *     element in the same way in the DOM.
27332          *
27333          *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27334          *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27335          *     property is same.
27336          *
27337          *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27338          *     to items in conjunction with a tracking expression.
27339          *
27340          *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27341          *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27342          *     when a filter is active on the repeater, but the filtered result set is empty.
27343          *
27344          *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27345          *     the items have been processed through the filter.
27346          *
27347          *     Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
27348          *     (and not as operator, inside an expression).
27349          *
27350          *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27351          *
27352          * @example
27353          * This example initializes the scope to a list of names and
27354          * then uses `ngRepeat` to display every person:
27355           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27356             <file name="index.html">
27357               <div ng-init="friends = [
27358                 {name:'John', age:25, gender:'boy'},
27359                 {name:'Jessie', age:30, gender:'girl'},
27360                 {name:'Johanna', age:28, gender:'girl'},
27361                 {name:'Joy', age:15, gender:'girl'},
27362                 {name:'Mary', age:28, gender:'girl'},
27363                 {name:'Peter', age:95, gender:'boy'},
27364                 {name:'Sebastian', age:50, gender:'boy'},
27365                 {name:'Erika', age:27, gender:'girl'},
27366                 {name:'Patrick', age:40, gender:'boy'},
27367                 {name:'Samantha', age:60, gender:'girl'}
27368               ]">
27369                 I have {{friends.length}} friends. They are:
27370                 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27371                 <ul class="example-animate-container">
27372                   <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27373                     [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27374                   </li>
27375                   <li class="animate-repeat" ng-if="results.length == 0">
27376                     <strong>No results found...</strong>
27377                   </li>
27378                 </ul>
27379               </div>
27380             </file>
27381             <file name="animations.css">
27382               .example-animate-container {
27383                 background:white;
27384                 border:1px solid black;
27385                 list-style:none;
27386                 margin:0;
27387                 padding:0 10px;
27388               }
27389
27390               .animate-repeat {
27391                 line-height:40px;
27392                 list-style:none;
27393                 box-sizing:border-box;
27394               }
27395
27396               .animate-repeat.ng-move,
27397               .animate-repeat.ng-enter,
27398               .animate-repeat.ng-leave {
27399                 transition:all linear 0.5s;
27400               }
27401
27402               .animate-repeat.ng-leave.ng-leave-active,
27403               .animate-repeat.ng-move,
27404               .animate-repeat.ng-enter {
27405                 opacity:0;
27406                 max-height:0;
27407               }
27408
27409               .animate-repeat.ng-leave,
27410               .animate-repeat.ng-move.ng-move-active,
27411               .animate-repeat.ng-enter.ng-enter-active {
27412                 opacity:1;
27413                 max-height:40px;
27414               }
27415             </file>
27416             <file name="protractor.js" type="protractor">
27417               var friends = element.all(by.repeater('friend in friends'));
27418
27419               it('should render initial data set', function() {
27420                 expect(friends.count()).toBe(10);
27421                 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27422                 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27423                 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27424                 expect(element(by.binding('friends.length')).getText())
27425                     .toMatch("I have 10 friends. They are:");
27426               });
27427
27428                it('should update repeater when filter predicate changes', function() {
27429                  expect(friends.count()).toBe(10);
27430
27431                  element(by.model('q')).sendKeys('ma');
27432
27433                  expect(friends.count()).toBe(2);
27434                  expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27435                  expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27436                });
27437               </file>
27438             </example>
27439          */
27440         var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27441           var NG_REMOVED = '$$NG_REMOVED';
27442           var ngRepeatMinErr = minErr('ngRepeat');
27443
27444           var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27445             // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27446             scope[valueIdentifier] = value;
27447             if (keyIdentifier) scope[keyIdentifier] = key;
27448             scope.$index = index;
27449             scope.$first = (index === 0);
27450             scope.$last = (index === (arrayLength - 1));
27451             scope.$middle = !(scope.$first || scope.$last);
27452             // jshint bitwise: false
27453             scope.$odd = !(scope.$even = (index&1) === 0);
27454             // jshint bitwise: true
27455           };
27456
27457           var getBlockStart = function(block) {
27458             return block.clone[0];
27459           };
27460
27461           var getBlockEnd = function(block) {
27462             return block.clone[block.clone.length - 1];
27463           };
27464
27465
27466           return {
27467             restrict: 'A',
27468             multiElement: true,
27469             transclude: 'element',
27470             priority: 1000,
27471             terminal: true,
27472             $$tlb: true,
27473             compile: function ngRepeatCompile($element, $attr) {
27474               var expression = $attr.ngRepeat;
27475               var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27476
27477               var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
27478
27479               if (!match) {
27480                 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27481                     expression);
27482               }
27483
27484               var lhs = match[1];
27485               var rhs = match[2];
27486               var aliasAs = match[3];
27487               var trackByExp = match[4];
27488
27489               match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27490
27491               if (!match) {
27492                 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27493                     lhs);
27494               }
27495               var valueIdentifier = match[3] || match[1];
27496               var keyIdentifier = match[2];
27497
27498               if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27499                   /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27500                 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27501                   aliasAs);
27502               }
27503
27504               var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27505               var hashFnLocals = {$id: hashKey};
27506
27507               if (trackByExp) {
27508                 trackByExpGetter = $parse(trackByExp);
27509               } else {
27510                 trackByIdArrayFn = function(key, value) {
27511                   return hashKey(value);
27512                 };
27513                 trackByIdObjFn = function(key) {
27514                   return key;
27515                 };
27516               }
27517
27518               return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27519
27520                 if (trackByExpGetter) {
27521                   trackByIdExpFn = function(key, value, index) {
27522                     // assign key, value, and $index to the locals so that they can be used in hash functions
27523                     if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27524                     hashFnLocals[valueIdentifier] = value;
27525                     hashFnLocals.$index = index;
27526                     return trackByExpGetter($scope, hashFnLocals);
27527                   };
27528                 }
27529
27530                 // Store a list of elements from previous run. This is a hash where key is the item from the
27531                 // iterator, and the value is objects with following properties.
27532                 //   - scope: bound scope
27533                 //   - element: previous element.
27534                 //   - index: position
27535                 //
27536                 // We are using no-proto object so that we don't need to guard against inherited props via
27537                 // hasOwnProperty.
27538                 var lastBlockMap = createMap();
27539
27540                 //watch props
27541                 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27542                   var index, length,
27543                       previousNode = $element[0],     // node that cloned nodes should be inserted after
27544                                                       // initialized to the comment node anchor
27545                       nextNode,
27546                       // Same as lastBlockMap but it has the current state. It will become the
27547                       // lastBlockMap on the next iteration.
27548                       nextBlockMap = createMap(),
27549                       collectionLength,
27550                       key, value, // key/value of iteration
27551                       trackById,
27552                       trackByIdFn,
27553                       collectionKeys,
27554                       block,       // last object information {scope, element, id}
27555                       nextBlockOrder,
27556                       elementsToRemove;
27557
27558                   if (aliasAs) {
27559                     $scope[aliasAs] = collection;
27560                   }
27561
27562                   if (isArrayLike(collection)) {
27563                     collectionKeys = collection;
27564                     trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27565                   } else {
27566                     trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27567                     // if object, extract keys, in enumeration order, unsorted
27568                     collectionKeys = [];
27569                     for (var itemKey in collection) {
27570                       if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27571                         collectionKeys.push(itemKey);
27572                       }
27573                     }
27574                   }
27575
27576                   collectionLength = collectionKeys.length;
27577                   nextBlockOrder = new Array(collectionLength);
27578
27579                   // locate existing items
27580                   for (index = 0; index < collectionLength; index++) {
27581                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27582                     value = collection[key];
27583                     trackById = trackByIdFn(key, value, index);
27584                     if (lastBlockMap[trackById]) {
27585                       // found previously seen block
27586                       block = lastBlockMap[trackById];
27587                       delete lastBlockMap[trackById];
27588                       nextBlockMap[trackById] = block;
27589                       nextBlockOrder[index] = block;
27590                     } else if (nextBlockMap[trackById]) {
27591                       // if collision detected. restore lastBlockMap and throw an error
27592                       forEach(nextBlockOrder, function(block) {
27593                         if (block && block.scope) lastBlockMap[block.id] = block;
27594                       });
27595                       throw ngRepeatMinErr('dupes',
27596                           "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27597                           expression, trackById, value);
27598                     } else {
27599                       // new never before seen block
27600                       nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27601                       nextBlockMap[trackById] = true;
27602                     }
27603                   }
27604
27605                   // remove leftover items
27606                   for (var blockKey in lastBlockMap) {
27607                     block = lastBlockMap[blockKey];
27608                     elementsToRemove = getBlockNodes(block.clone);
27609                     $animate.leave(elementsToRemove);
27610                     if (elementsToRemove[0].parentNode) {
27611                       // if the element was not removed yet because of pending animation, mark it as deleted
27612                       // so that we can ignore it later
27613                       for (index = 0, length = elementsToRemove.length; index < length; index++) {
27614                         elementsToRemove[index][NG_REMOVED] = true;
27615                       }
27616                     }
27617                     block.scope.$destroy();
27618                   }
27619
27620                   // we are not using forEach for perf reasons (trying to avoid #call)
27621                   for (index = 0; index < collectionLength; index++) {
27622                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27623                     value = collection[key];
27624                     block = nextBlockOrder[index];
27625
27626                     if (block.scope) {
27627                       // if we have already seen this object, then we need to reuse the
27628                       // associated scope/element
27629
27630                       nextNode = previousNode;
27631
27632                       // skip nodes that are already pending removal via leave animation
27633                       do {
27634                         nextNode = nextNode.nextSibling;
27635                       } while (nextNode && nextNode[NG_REMOVED]);
27636
27637                       if (getBlockStart(block) != nextNode) {
27638                         // existing item which got moved
27639                         $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27640                       }
27641                       previousNode = getBlockEnd(block);
27642                       updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27643                     } else {
27644                       // new item which we don't know about
27645                       $transclude(function ngRepeatTransclude(clone, scope) {
27646                         block.scope = scope;
27647                         // http://jsperf.com/clone-vs-createcomment
27648                         var endNode = ngRepeatEndComment.cloneNode(false);
27649                         clone[clone.length++] = endNode;
27650
27651                         // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27652                         $animate.enter(clone, null, jqLite(previousNode));
27653                         previousNode = endNode;
27654                         // Note: We only need the first/last node of the cloned nodes.
27655                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27656                         // by a directive with templateUrl when its template arrives.
27657                         block.clone = clone;
27658                         nextBlockMap[block.id] = block;
27659                         updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27660                       });
27661                     }
27662                   }
27663                   lastBlockMap = nextBlockMap;
27664                 });
27665               };
27666             }
27667           };
27668         }];
27669
27670         var NG_HIDE_CLASS = 'ng-hide';
27671         var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27672         /**
27673          * @ngdoc directive
27674          * @name ngShow
27675          * @multiElement
27676          *
27677          * @description
27678          * The `ngShow` directive shows or hides the given HTML element based on the expression
27679          * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27680          * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27681          * in AngularJS and sets the display style to none (using an !important flag).
27682          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27683          *
27684          * ```html
27685          * <!-- when $scope.myValue is truthy (element is visible) -->
27686          * <div ng-show="myValue"></div>
27687          *
27688          * <!-- when $scope.myValue is falsy (element is hidden) -->
27689          * <div ng-show="myValue" class="ng-hide"></div>
27690          * ```
27691          *
27692          * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27693          * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27694          * from the element causing the element not to appear hidden.
27695          *
27696          * ## Why is !important used?
27697          *
27698          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27699          * can be easily overridden by heavier selectors. For example, something as simple
27700          * as changing the display style on a HTML list item would make hidden elements appear visible.
27701          * This also becomes a bigger issue when dealing with CSS frameworks.
27702          *
27703          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27704          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27705          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27706          *
27707          * ### Overriding `.ng-hide`
27708          *
27709          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27710          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27711          * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27712          * with extra animation classes that can be added.
27713          *
27714          * ```css
27715          * .ng-hide:not(.ng-hide-animate) {
27716          *   /&#42; this is just another form of hiding an element &#42;/
27717          *   display: block!important;
27718          *   position: absolute;
27719          *   top: -9999px;
27720          *   left: -9999px;
27721          * }
27722          * ```
27723          *
27724          * By default you don't need to override in CSS anything and the animations will work around the display style.
27725          *
27726          * ## A note about animations with `ngShow`
27727          *
27728          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27729          * is true and false. This system works like the animation system present with ngClass except that
27730          * you must also include the !important flag to override the display property
27731          * so that you can perform an animation when the element is hidden during the time of the animation.
27732          *
27733          * ```css
27734          * //
27735          * //a working example can be found at the bottom of this page
27736          * //
27737          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27738          *   /&#42; this is required as of 1.3x to properly
27739          *      apply all styling in a show/hide animation &#42;/
27740          *   transition: 0s linear all;
27741          * }
27742          *
27743          * .my-element.ng-hide-add-active,
27744          * .my-element.ng-hide-remove-active {
27745          *   /&#42; the transition is defined in the active class &#42;/
27746          *   transition: 1s linear all;
27747          * }
27748          *
27749          * .my-element.ng-hide-add { ... }
27750          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27751          * .my-element.ng-hide-remove { ... }
27752          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27753          * ```
27754          *
27755          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27756          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27757          *
27758          * @animations
27759          * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27760          * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27761          *
27762          * @element ANY
27763          * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27764          *     then the element is shown or hidden respectively.
27765          *
27766          * @example
27767           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27768             <file name="index.html">
27769               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27770               <div>
27771                 Show:
27772                 <div class="check-element animate-show" ng-show="checked">
27773                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27774                 </div>
27775               </div>
27776               <div>
27777                 Hide:
27778                 <div class="check-element animate-show" ng-hide="checked">
27779                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27780                 </div>
27781               </div>
27782             </file>
27783             <file name="glyphicons.css">
27784               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27785             </file>
27786             <file name="animations.css">
27787               .animate-show {
27788                 line-height: 20px;
27789                 opacity: 1;
27790                 padding: 10px;
27791                 border: 1px solid black;
27792                 background: white;
27793               }
27794
27795               .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27796                 transition: all linear 0.5s;
27797               }
27798
27799               .animate-show.ng-hide {
27800                 line-height: 0;
27801                 opacity: 0;
27802                 padding: 0 10px;
27803               }
27804
27805               .check-element {
27806                 padding: 10px;
27807                 border: 1px solid black;
27808                 background: white;
27809               }
27810             </file>
27811             <file name="protractor.js" type="protractor">
27812               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27813               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27814
27815               it('should check ng-show / ng-hide', function() {
27816                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27817                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27818
27819                 element(by.model('checked')).click();
27820
27821                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27822                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27823               });
27824             </file>
27825           </example>
27826          */
27827         var ngShowDirective = ['$animate', function($animate) {
27828           return {
27829             restrict: 'A',
27830             multiElement: true,
27831             link: function(scope, element, attr) {
27832               scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27833                 // we're adding a temporary, animation-specific class for ng-hide since this way
27834                 // we can control when the element is actually displayed on screen without having
27835                 // to have a global/greedy CSS selector that breaks when other animations are run.
27836                 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27837                 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27838                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27839                 });
27840               });
27841             }
27842           };
27843         }];
27844
27845
27846         /**
27847          * @ngdoc directive
27848          * @name ngHide
27849          * @multiElement
27850          *
27851          * @description
27852          * The `ngHide` directive shows or hides the given HTML element based on the expression
27853          * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27854          * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27855          * in AngularJS and sets the display style to none (using an !important flag).
27856          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27857          *
27858          * ```html
27859          * <!-- when $scope.myValue is truthy (element is hidden) -->
27860          * <div ng-hide="myValue" class="ng-hide"></div>
27861          *
27862          * <!-- when $scope.myValue is falsy (element is visible) -->
27863          * <div ng-hide="myValue"></div>
27864          * ```
27865          *
27866          * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27867          * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27868          * from the element causing the element not to appear hidden.
27869          *
27870          * ## Why is !important used?
27871          *
27872          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27873          * can be easily overridden by heavier selectors. For example, something as simple
27874          * as changing the display style on a HTML list item would make hidden elements appear visible.
27875          * This also becomes a bigger issue when dealing with CSS frameworks.
27876          *
27877          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27878          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27879          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27880          *
27881          * ### Overriding `.ng-hide`
27882          *
27883          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27884          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27885          * class in CSS:
27886          *
27887          * ```css
27888          * .ng-hide {
27889          *   /&#42; this is just another form of hiding an element &#42;/
27890          *   display: block!important;
27891          *   position: absolute;
27892          *   top: -9999px;
27893          *   left: -9999px;
27894          * }
27895          * ```
27896          *
27897          * By default you don't need to override in CSS anything and the animations will work around the display style.
27898          *
27899          * ## A note about animations with `ngHide`
27900          *
27901          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27902          * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27903          * CSS class is added and removed for you instead of your own CSS class.
27904          *
27905          * ```css
27906          * //
27907          * //a working example can be found at the bottom of this page
27908          * //
27909          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27910          *   transition: 0.5s linear all;
27911          * }
27912          *
27913          * .my-element.ng-hide-add { ... }
27914          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27915          * .my-element.ng-hide-remove { ... }
27916          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27917          * ```
27918          *
27919          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27920          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27921          *
27922          * @animations
27923          * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27924          * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27925          *
27926          * @element ANY
27927          * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27928          *     the element is shown or hidden respectively.
27929          *
27930          * @example
27931           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27932             <file name="index.html">
27933               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27934               <div>
27935                 Show:
27936                 <div class="check-element animate-hide" ng-show="checked">
27937                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27938                 </div>
27939               </div>
27940               <div>
27941                 Hide:
27942                 <div class="check-element animate-hide" ng-hide="checked">
27943                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27944                 </div>
27945               </div>
27946             </file>
27947             <file name="glyphicons.css">
27948               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27949             </file>
27950             <file name="animations.css">
27951               .animate-hide {
27952                 transition: all linear 0.5s;
27953                 line-height: 20px;
27954                 opacity: 1;
27955                 padding: 10px;
27956                 border: 1px solid black;
27957                 background: white;
27958               }
27959
27960               .animate-hide.ng-hide {
27961                 line-height: 0;
27962                 opacity: 0;
27963                 padding: 0 10px;
27964               }
27965
27966               .check-element {
27967                 padding: 10px;
27968                 border: 1px solid black;
27969                 background: white;
27970               }
27971             </file>
27972             <file name="protractor.js" type="protractor">
27973               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27974               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27975
27976               it('should check ng-show / ng-hide', function() {
27977                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27978                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27979
27980                 element(by.model('checked')).click();
27981
27982                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27983                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27984               });
27985             </file>
27986           </example>
27987          */
27988         var ngHideDirective = ['$animate', function($animate) {
27989           return {
27990             restrict: 'A',
27991             multiElement: true,
27992             link: function(scope, element, attr) {
27993               scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27994                 // The comment inside of the ngShowDirective explains why we add and
27995                 // remove a temporary class for the show/hide animation
27996                 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27997                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27998                 });
27999               });
28000             }
28001           };
28002         }];
28003
28004         /**
28005          * @ngdoc directive
28006          * @name ngStyle
28007          * @restrict AC
28008          *
28009          * @description
28010          * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28011          *
28012          * @element ANY
28013          * @param {expression} ngStyle
28014          *
28015          * {@link guide/expression Expression} which evals to an
28016          * object whose keys are CSS style names and values are corresponding values for those CSS
28017          * keys.
28018          *
28019          * Since some CSS style names are not valid keys for an object, they must be quoted.
28020          * See the 'background-color' style in the example below.
28021          *
28022          * @example
28023            <example>
28024              <file name="index.html">
28025                 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28026                 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28027                 <input type="button" value="clear" ng-click="myStyle={}">
28028                 <br/>
28029                 <span ng-style="myStyle">Sample Text</span>
28030                 <pre>myStyle={{myStyle}}</pre>
28031              </file>
28032              <file name="style.css">
28033                span {
28034                  color: black;
28035                }
28036              </file>
28037              <file name="protractor.js" type="protractor">
28038                var colorSpan = element(by.css('span'));
28039
28040                it('should check ng-style', function() {
28041                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28042                  element(by.css('input[value=\'set color\']')).click();
28043                  expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28044                  element(by.css('input[value=clear]')).click();
28045                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28046                });
28047              </file>
28048            </example>
28049          */
28050         var ngStyleDirective = ngDirective(function(scope, element, attr) {
28051           scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28052             if (oldStyles && (newStyles !== oldStyles)) {
28053               forEach(oldStyles, function(val, style) { element.css(style, '');});
28054             }
28055             if (newStyles) element.css(newStyles);
28056           }, true);
28057         });
28058
28059         /**
28060          * @ngdoc directive
28061          * @name ngSwitch
28062          * @restrict EA
28063          *
28064          * @description
28065          * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28066          * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28067          * as specified in the template.
28068          *
28069          * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28070          * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28071          * matches the value obtained from the evaluated expression. In other words, you define a container element
28072          * (where you place the directive), place an expression on the **`on="..."` attribute**
28073          * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28074          * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28075          * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28076          * attribute is displayed.
28077          *
28078          * <div class="alert alert-info">
28079          * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28080          * as literal string values to match against.
28081          * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28082          * value of the expression `$scope.someVal`.
28083          * </div>
28084
28085          * @animations
28086          * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28087          * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28088          *
28089          * @usage
28090          *
28091          * ```
28092          * <ANY ng-switch="expression">
28093          *   <ANY ng-switch-when="matchValue1">...</ANY>
28094          *   <ANY ng-switch-when="matchValue2">...</ANY>
28095          *   <ANY ng-switch-default>...</ANY>
28096          * </ANY>
28097          * ```
28098          *
28099          *
28100          * @scope
28101          * @priority 1200
28102          * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28103          * On child elements add:
28104          *
28105          * * `ngSwitchWhen`: the case statement to match against. If match then this
28106          *   case will be displayed. If the same match appears multiple times, all the
28107          *   elements will be displayed.
28108          * * `ngSwitchDefault`: the default case when no other case match. If there
28109          *   are multiple default cases, all of them will be displayed when no other
28110          *   case match.
28111          *
28112          *
28113          * @example
28114           <example module="switchExample" deps="angular-animate.js" animations="true">
28115             <file name="index.html">
28116               <div ng-controller="ExampleController">
28117                 <select ng-model="selection" ng-options="item for item in items">
28118                 </select>
28119                 <code>selection={{selection}}</code>
28120                 <hr/>
28121                 <div class="animate-switch-container"
28122                   ng-switch on="selection">
28123                     <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28124                     <div class="animate-switch" ng-switch-when="home">Home Span</div>
28125                     <div class="animate-switch" ng-switch-default>default</div>
28126                 </div>
28127               </div>
28128             </file>
28129             <file name="script.js">
28130               angular.module('switchExample', ['ngAnimate'])
28131                 .controller('ExampleController', ['$scope', function($scope) {
28132                   $scope.items = ['settings', 'home', 'other'];
28133                   $scope.selection = $scope.items[0];
28134                 }]);
28135             </file>
28136             <file name="animations.css">
28137               .animate-switch-container {
28138                 position:relative;
28139                 background:white;
28140                 border:1px solid black;
28141                 height:40px;
28142                 overflow:hidden;
28143               }
28144
28145               .animate-switch {
28146                 padding:10px;
28147               }
28148
28149               .animate-switch.ng-animate {
28150                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28151
28152                 position:absolute;
28153                 top:0;
28154                 left:0;
28155                 right:0;
28156                 bottom:0;
28157               }
28158
28159               .animate-switch.ng-leave.ng-leave-active,
28160               .animate-switch.ng-enter {
28161                 top:-50px;
28162               }
28163               .animate-switch.ng-leave,
28164               .animate-switch.ng-enter.ng-enter-active {
28165                 top:0;
28166               }
28167             </file>
28168             <file name="protractor.js" type="protractor">
28169               var switchElem = element(by.css('[ng-switch]'));
28170               var select = element(by.model('selection'));
28171
28172               it('should start in settings', function() {
28173                 expect(switchElem.getText()).toMatch(/Settings Div/);
28174               });
28175               it('should change to home', function() {
28176                 select.all(by.css('option')).get(1).click();
28177                 expect(switchElem.getText()).toMatch(/Home Span/);
28178               });
28179               it('should select default', function() {
28180                 select.all(by.css('option')).get(2).click();
28181                 expect(switchElem.getText()).toMatch(/default/);
28182               });
28183             </file>
28184           </example>
28185          */
28186         var ngSwitchDirective = ['$animate', function($animate) {
28187           return {
28188             require: 'ngSwitch',
28189
28190             // asks for $scope to fool the BC controller module
28191             controller: ['$scope', function ngSwitchController() {
28192              this.cases = {};
28193             }],
28194             link: function(scope, element, attr, ngSwitchController) {
28195               var watchExpr = attr.ngSwitch || attr.on,
28196                   selectedTranscludes = [],
28197                   selectedElements = [],
28198                   previousLeaveAnimations = [],
28199                   selectedScopes = [];
28200
28201               var spliceFactory = function(array, index) {
28202                   return function() { array.splice(index, 1); };
28203               };
28204
28205               scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28206                 var i, ii;
28207                 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28208                   $animate.cancel(previousLeaveAnimations[i]);
28209                 }
28210                 previousLeaveAnimations.length = 0;
28211
28212                 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28213                   var selected = getBlockNodes(selectedElements[i].clone);
28214                   selectedScopes[i].$destroy();
28215                   var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28216                   promise.then(spliceFactory(previousLeaveAnimations, i));
28217                 }
28218
28219                 selectedElements.length = 0;
28220                 selectedScopes.length = 0;
28221
28222                 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28223                   forEach(selectedTranscludes, function(selectedTransclude) {
28224                     selectedTransclude.transclude(function(caseElement, selectedScope) {
28225                       selectedScopes.push(selectedScope);
28226                       var anchor = selectedTransclude.element;
28227                       caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28228                       var block = { clone: caseElement };
28229
28230                       selectedElements.push(block);
28231                       $animate.enter(caseElement, anchor.parent(), anchor);
28232                     });
28233                   });
28234                 }
28235               });
28236             }
28237           };
28238         }];
28239
28240         var ngSwitchWhenDirective = ngDirective({
28241           transclude: 'element',
28242           priority: 1200,
28243           require: '^ngSwitch',
28244           multiElement: true,
28245           link: function(scope, element, attrs, ctrl, $transclude) {
28246             ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28247             ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28248           }
28249         });
28250
28251         var ngSwitchDefaultDirective = ngDirective({
28252           transclude: 'element',
28253           priority: 1200,
28254           require: '^ngSwitch',
28255           multiElement: true,
28256           link: function(scope, element, attr, ctrl, $transclude) {
28257             ctrl.cases['?'] = (ctrl.cases['?'] || []);
28258             ctrl.cases['?'].push({ transclude: $transclude, element: element });
28259            }
28260         });
28261
28262         /**
28263          * @ngdoc directive
28264          * @name ngTransclude
28265          * @restrict EAC
28266          *
28267          * @description
28268          * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28269          *
28270          * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28271          *
28272          * @element ANY
28273          *
28274          * @example
28275            <example module="transcludeExample">
28276              <file name="index.html">
28277                <script>
28278                  angular.module('transcludeExample', [])
28279                   .directive('pane', function(){
28280                      return {
28281                        restrict: 'E',
28282                        transclude: true,
28283                        scope: { title:'@' },
28284                        template: '<div style="border: 1px solid black;">' +
28285                                    '<div style="background-color: gray">{{title}}</div>' +
28286                                    '<ng-transclude></ng-transclude>' +
28287                                  '</div>'
28288                      };
28289                  })
28290                  .controller('ExampleController', ['$scope', function($scope) {
28291                    $scope.title = 'Lorem Ipsum';
28292                    $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28293                  }]);
28294                </script>
28295                <div ng-controller="ExampleController">
28296                  <input ng-model="title" aria-label="title"> <br/>
28297                  <textarea ng-model="text" aria-label="text"></textarea> <br/>
28298                  <pane title="{{title}}">{{text}}</pane>
28299                </div>
28300              </file>
28301              <file name="protractor.js" type="protractor">
28302                 it('should have transcluded', function() {
28303                   var titleElement = element(by.model('title'));
28304                   titleElement.clear();
28305                   titleElement.sendKeys('TITLE');
28306                   var textElement = element(by.model('text'));
28307                   textElement.clear();
28308                   textElement.sendKeys('TEXT');
28309                   expect(element(by.binding('title')).getText()).toEqual('TITLE');
28310                   expect(element(by.binding('text')).getText()).toEqual('TEXT');
28311                 });
28312              </file>
28313            </example>
28314          *
28315          */
28316         var ngTranscludeDirective = ngDirective({
28317           restrict: 'EAC',
28318           link: function($scope, $element, $attrs, controller, $transclude) {
28319             if (!$transclude) {
28320               throw minErr('ngTransclude')('orphan',
28321                'Illegal use of ngTransclude directive in the template! ' +
28322                'No parent directive that requires a transclusion found. ' +
28323                'Element: {0}',
28324                startingTag($element));
28325             }
28326
28327             $transclude(function(clone) {
28328               $element.empty();
28329               $element.append(clone);
28330             });
28331           }
28332         });
28333
28334         /**
28335          * @ngdoc directive
28336          * @name script
28337          * @restrict E
28338          *
28339          * @description
28340          * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28341          * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28342          * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28343          * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28344          * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28345          *
28346          * @param {string} type Must be set to `'text/ng-template'`.
28347          * @param {string} id Cache name of the template.
28348          *
28349          * @example
28350           <example>
28351             <file name="index.html">
28352               <script type="text/ng-template" id="/tpl.html">
28353                 Content of the template.
28354               </script>
28355
28356               <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28357               <div id="tpl-content" ng-include src="currentTpl"></div>
28358             </file>
28359             <file name="protractor.js" type="protractor">
28360               it('should load template defined inside script tag', function() {
28361                 element(by.css('#tpl-link')).click();
28362                 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28363               });
28364             </file>
28365           </example>
28366          */
28367         var scriptDirective = ['$templateCache', function($templateCache) {
28368           return {
28369             restrict: 'E',
28370             terminal: true,
28371             compile: function(element, attr) {
28372               if (attr.type == 'text/ng-template') {
28373                 var templateUrl = attr.id,
28374                     text = element[0].text;
28375
28376                 $templateCache.put(templateUrl, text);
28377               }
28378             }
28379           };
28380         }];
28381
28382         var noopNgModelController = { $setViewValue: noop, $render: noop };
28383
28384         function chromeHack(optionElement) {
28385           // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28386           // Adding an <option selected="selected"> element to a <select required="required"> should
28387           // automatically select the new element
28388           if (optionElement[0].hasAttribute('selected')) {
28389             optionElement[0].selected = true;
28390           }
28391         }
28392
28393         /**
28394          * @ngdoc type
28395          * @name  select.SelectController
28396          * @description
28397          * The controller for the `<select>` directive. This provides support for reading
28398          * and writing the selected value(s) of the control and also coordinates dynamically
28399          * added `<option>` elements, perhaps by an `ngRepeat` directive.
28400          */
28401         var SelectController =
28402                 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28403
28404           var self = this,
28405               optionsMap = new HashMap();
28406
28407           // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28408           self.ngModelCtrl = noopNgModelController;
28409
28410           // The "unknown" option is one that is prepended to the list if the viewValue
28411           // does not match any of the options. When it is rendered the value of the unknown
28412           // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28413           //
28414           // We can't just jqLite('<option>') since jqLite is not smart enough
28415           // to create it in <select> and IE barfs otherwise.
28416           self.unknownOption = jqLite(document.createElement('option'));
28417           self.renderUnknownOption = function(val) {
28418             var unknownVal = '? ' + hashKey(val) + ' ?';
28419             self.unknownOption.val(unknownVal);
28420             $element.prepend(self.unknownOption);
28421             $element.val(unknownVal);
28422           };
28423
28424           $scope.$on('$destroy', function() {
28425             // disable unknown option so that we don't do work when the whole select is being destroyed
28426             self.renderUnknownOption = noop;
28427           });
28428
28429           self.removeUnknownOption = function() {
28430             if (self.unknownOption.parent()) self.unknownOption.remove();
28431           };
28432
28433
28434           // Read the value of the select control, the implementation of this changes depending
28435           // upon whether the select can have multiple values and whether ngOptions is at work.
28436           self.readValue = function readSingleValue() {
28437             self.removeUnknownOption();
28438             return $element.val();
28439           };
28440
28441
28442           // Write the value to the select control, the implementation of this changes depending
28443           // upon whether the select can have multiple values and whether ngOptions is at work.
28444           self.writeValue = function writeSingleValue(value) {
28445             if (self.hasOption(value)) {
28446               self.removeUnknownOption();
28447               $element.val(value);
28448               if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28449             } else {
28450               if (value == null && self.emptyOption) {
28451                 self.removeUnknownOption();
28452                 $element.val('');
28453               } else {
28454                 self.renderUnknownOption(value);
28455               }
28456             }
28457           };
28458
28459
28460           // Tell the select control that an option, with the given value, has been added
28461           self.addOption = function(value, element) {
28462             assertNotHasOwnProperty(value, '"option value"');
28463             if (value === '') {
28464               self.emptyOption = element;
28465             }
28466             var count = optionsMap.get(value) || 0;
28467             optionsMap.put(value, count + 1);
28468             self.ngModelCtrl.$render();
28469             chromeHack(element);
28470           };
28471
28472           // Tell the select control that an option, with the given value, has been removed
28473           self.removeOption = function(value) {
28474             var count = optionsMap.get(value);
28475             if (count) {
28476               if (count === 1) {
28477                 optionsMap.remove(value);
28478                 if (value === '') {
28479                   self.emptyOption = undefined;
28480                 }
28481               } else {
28482                 optionsMap.put(value, count - 1);
28483               }
28484             }
28485           };
28486
28487           // Check whether the select control has an option matching the given value
28488           self.hasOption = function(value) {
28489             return !!optionsMap.get(value);
28490           };
28491
28492
28493           self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28494
28495             if (interpolateValueFn) {
28496               // The value attribute is interpolated
28497               var oldVal;
28498               optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28499                 if (isDefined(oldVal)) {
28500                   self.removeOption(oldVal);
28501                 }
28502                 oldVal = newVal;
28503                 self.addOption(newVal, optionElement);
28504               });
28505             } else if (interpolateTextFn) {
28506               // The text content is interpolated
28507               optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28508                 optionAttrs.$set('value', newVal);
28509                 if (oldVal !== newVal) {
28510                   self.removeOption(oldVal);
28511                 }
28512                 self.addOption(newVal, optionElement);
28513               });
28514             } else {
28515               // The value attribute is static
28516               self.addOption(optionAttrs.value, optionElement);
28517             }
28518
28519             optionElement.on('$destroy', function() {
28520               self.removeOption(optionAttrs.value);
28521               self.ngModelCtrl.$render();
28522             });
28523           };
28524         }];
28525
28526         /**
28527          * @ngdoc directive
28528          * @name select
28529          * @restrict E
28530          *
28531          * @description
28532          * HTML `SELECT` element with angular data-binding.
28533          *
28534          * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28535          * between the scope and the `<select>` control (including setting default values).
28536          * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28537          * {@link ngOptions `ngOptions`} directives.
28538          *
28539          * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28540          * to the model identified by the `ngModel` directive. With static or repeated options, this is
28541          * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28542          * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28543          *
28544          * <div class="alert alert-warning">
28545          * Note that the value of a `select` directive used without `ngOptions` is always a string.
28546          * When the model needs to be bound to a non-string value, you must either explictly convert it
28547          * using a directive (see example below) or use `ngOptions` to specify the set of options.
28548          * This is because an option element can only be bound to string values at present.
28549          * </div>
28550          *
28551          * If the viewValue of `ngModel` does not match any of the options, then the control
28552          * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28553          *
28554          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28555          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28556          * option. See example below for demonstration.
28557          *
28558          * <div class="alert alert-info">
28559          * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28560          * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28561          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28562          * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28563          * a new scope for each repeated instance.
28564          * </div>
28565          *
28566          *
28567          * @param {string} ngModel Assignable angular expression to data-bind to.
28568          * @param {string=} name Property name of the form under which the control is published.
28569          * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28570          *     bound to the model as an array.
28571          * @param {string=} required Sets `required` validation error key if the value is not entered.
28572          * @param {string=} ngRequired Adds required attribute and required validation constraint to
28573          * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28574          * when you want to data-bind to the required attribute.
28575          * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28576          *    interaction with the select element.
28577          * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28578          * set on the model on selection. See {@link ngOptions `ngOptions`}.
28579          *
28580          * @example
28581          * ### Simple `select` elements with static options
28582          *
28583          * <example name="static-select" module="staticSelect">
28584          * <file name="index.html">
28585          * <div ng-controller="ExampleController">
28586          *   <form name="myForm">
28587          *     <label for="singleSelect"> Single select: </label><br>
28588          *     <select name="singleSelect" ng-model="data.singleSelect">
28589          *       <option value="option-1">Option 1</option>
28590          *       <option value="option-2">Option 2</option>
28591          *     </select><br>
28592          *
28593          *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28594          *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28595          *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28596          *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28597          *       <option value="option-2">Option 2</option>
28598          *     </select><br>
28599          *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28600          *     <tt>singleSelect = {{data.singleSelect}}</tt>
28601          *
28602          *     <hr>
28603          *     <label for="multipleSelect"> Multiple select: </label><br>
28604          *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28605          *       <option value="option-1">Option 1</option>
28606          *       <option value="option-2">Option 2</option>
28607          *       <option value="option-3">Option 3</option>
28608          *     </select><br>
28609          *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28610          *   </form>
28611          * </div>
28612          * </file>
28613          * <file name="app.js">
28614          *  angular.module('staticSelect', [])
28615          *    .controller('ExampleController', ['$scope', function($scope) {
28616          *      $scope.data = {
28617          *       singleSelect: null,
28618          *       multipleSelect: [],
28619          *       option1: 'option-1',
28620          *      };
28621          *
28622          *      $scope.forceUnknownOption = function() {
28623          *        $scope.data.singleSelect = 'nonsense';
28624          *      };
28625          *   }]);
28626          * </file>
28627          *</example>
28628          *
28629          * ### Using `ngRepeat` to generate `select` options
28630          * <example name="ngrepeat-select" module="ngrepeatSelect">
28631          * <file name="index.html">
28632          * <div ng-controller="ExampleController">
28633          *   <form name="myForm">
28634          *     <label for="repeatSelect"> Repeat select: </label>
28635          *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28636          *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28637          *     </select>
28638          *   </form>
28639          *   <hr>
28640          *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28641          * </div>
28642          * </file>
28643          * <file name="app.js">
28644          *  angular.module('ngrepeatSelect', [])
28645          *    .controller('ExampleController', ['$scope', function($scope) {
28646          *      $scope.data = {
28647          *       repeatSelect: null,
28648          *       availableOptions: [
28649          *         {id: '1', name: 'Option A'},
28650          *         {id: '2', name: 'Option B'},
28651          *         {id: '3', name: 'Option C'}
28652          *       ],
28653          *      };
28654          *   }]);
28655          * </file>
28656          *</example>
28657          *
28658          *
28659          * ### Using `select` with `ngOptions` and setting a default value
28660          * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28661          *
28662          * <example name="select-with-default-values" module="defaultValueSelect">
28663          * <file name="index.html">
28664          * <div ng-controller="ExampleController">
28665          *   <form name="myForm">
28666          *     <label for="mySelect">Make a choice:</label>
28667          *     <select name="mySelect" id="mySelect"
28668          *       ng-options="option.name for option in data.availableOptions track by option.id"
28669          *       ng-model="data.selectedOption"></select>
28670          *   </form>
28671          *   <hr>
28672          *   <tt>option = {{data.selectedOption}}</tt><br/>
28673          * </div>
28674          * </file>
28675          * <file name="app.js">
28676          *  angular.module('defaultValueSelect', [])
28677          *    .controller('ExampleController', ['$scope', function($scope) {
28678          *      $scope.data = {
28679          *       availableOptions: [
28680          *         {id: '1', name: 'Option A'},
28681          *         {id: '2', name: 'Option B'},
28682          *         {id: '3', name: 'Option C'}
28683          *       ],
28684          *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28685          *       };
28686          *   }]);
28687          * </file>
28688          *</example>
28689          *
28690          *
28691          * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28692          *
28693          * <example name="select-with-non-string-options" module="nonStringSelect">
28694          *   <file name="index.html">
28695          *     <select ng-model="model.id" convert-to-number>
28696          *       <option value="0">Zero</option>
28697          *       <option value="1">One</option>
28698          *       <option value="2">Two</option>
28699          *     </select>
28700          *     {{ model }}
28701          *   </file>
28702          *   <file name="app.js">
28703          *     angular.module('nonStringSelect', [])
28704          *       .run(function($rootScope) {
28705          *         $rootScope.model = { id: 2 };
28706          *       })
28707          *       .directive('convertToNumber', function() {
28708          *         return {
28709          *           require: 'ngModel',
28710          *           link: function(scope, element, attrs, ngModel) {
28711          *             ngModel.$parsers.push(function(val) {
28712          *               return parseInt(val, 10);
28713          *             });
28714          *             ngModel.$formatters.push(function(val) {
28715          *               return '' + val;
28716          *             });
28717          *           }
28718          *         };
28719          *       });
28720          *   </file>
28721          *   <file name="protractor.js" type="protractor">
28722          *     it('should initialize to model', function() {
28723          *       var select = element(by.css('select'));
28724          *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28725          *     });
28726          *   </file>
28727          * </example>
28728          *
28729          */
28730         var selectDirective = function() {
28731
28732           return {
28733             restrict: 'E',
28734             require: ['select', '?ngModel'],
28735             controller: SelectController,
28736             priority: 1,
28737             link: {
28738               pre: selectPreLink
28739             }
28740           };
28741
28742           function selectPreLink(scope, element, attr, ctrls) {
28743
28744               // if ngModel is not defined, we don't need to do anything
28745               var ngModelCtrl = ctrls[1];
28746               if (!ngModelCtrl) return;
28747
28748               var selectCtrl = ctrls[0];
28749
28750               selectCtrl.ngModelCtrl = ngModelCtrl;
28751
28752               // We delegate rendering to the `writeValue` method, which can be changed
28753               // if the select can have multiple selected values or if the options are being
28754               // generated by `ngOptions`
28755               ngModelCtrl.$render = function() {
28756                 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28757               };
28758
28759               // When the selected item(s) changes we delegate getting the value of the select control
28760               // to the `readValue` method, which can be changed if the select can have multiple
28761               // selected values or if the options are being generated by `ngOptions`
28762               element.on('change', function() {
28763                 scope.$apply(function() {
28764                   ngModelCtrl.$setViewValue(selectCtrl.readValue());
28765                 });
28766               });
28767
28768               // If the select allows multiple values then we need to modify how we read and write
28769               // values from and to the control; also what it means for the value to be empty and
28770               // we have to add an extra watch since ngModel doesn't work well with arrays - it
28771               // doesn't trigger rendering if only an item in the array changes.
28772               if (attr.multiple) {
28773
28774                 // Read value now needs to check each option to see if it is selected
28775                 selectCtrl.readValue = function readMultipleValue() {
28776                   var array = [];
28777                   forEach(element.find('option'), function(option) {
28778                     if (option.selected) {
28779                       array.push(option.value);
28780                     }
28781                   });
28782                   return array;
28783                 };
28784
28785                 // Write value now needs to set the selected property of each matching option
28786                 selectCtrl.writeValue = function writeMultipleValue(value) {
28787                   var items = new HashMap(value);
28788                   forEach(element.find('option'), function(option) {
28789                     option.selected = isDefined(items.get(option.value));
28790                   });
28791                 };
28792
28793                 // we have to do it on each watch since ngModel watches reference, but
28794                 // we need to work of an array, so we need to see if anything was inserted/removed
28795                 var lastView, lastViewRef = NaN;
28796                 scope.$watch(function selectMultipleWatch() {
28797                   if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28798                     lastView = shallowCopy(ngModelCtrl.$viewValue);
28799                     ngModelCtrl.$render();
28800                   }
28801                   lastViewRef = ngModelCtrl.$viewValue;
28802                 });
28803
28804                 // If we are a multiple select then value is now a collection
28805                 // so the meaning of $isEmpty changes
28806                 ngModelCtrl.$isEmpty = function(value) {
28807                   return !value || value.length === 0;
28808                 };
28809
28810               }
28811             }
28812         };
28813
28814
28815         // The option directive is purely designed to communicate the existence (or lack of)
28816         // of dynamically created (and destroyed) option elements to their containing select
28817         // directive via its controller.
28818         var optionDirective = ['$interpolate', function($interpolate) {
28819           return {
28820             restrict: 'E',
28821             priority: 100,
28822             compile: function(element, attr) {
28823
28824               if (isDefined(attr.value)) {
28825                 // If the value attribute is defined, check if it contains an interpolation
28826                 var interpolateValueFn = $interpolate(attr.value, true);
28827               } else {
28828                 // If the value attribute is not defined then we fall back to the
28829                 // text content of the option element, which may be interpolated
28830                 var interpolateTextFn = $interpolate(element.text(), true);
28831                 if (!interpolateTextFn) {
28832                   attr.$set('value', element.text());
28833                 }
28834               }
28835
28836               return function(scope, element, attr) {
28837
28838                 // This is an optimization over using ^^ since we don't want to have to search
28839                 // all the way to the root of the DOM for every single option element
28840                 var selectCtrlName = '$selectController',
28841                     parent = element.parent(),
28842                     selectCtrl = parent.data(selectCtrlName) ||
28843                       parent.parent().data(selectCtrlName); // in case we are in optgroup
28844
28845                 if (selectCtrl) {
28846                   selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28847                 }
28848               };
28849             }
28850           };
28851         }];
28852
28853         var styleDirective = valueFn({
28854           restrict: 'E',
28855           terminal: false
28856         });
28857
28858         var requiredDirective = function() {
28859           return {
28860             restrict: 'A',
28861             require: '?ngModel',
28862             link: function(scope, elm, attr, ctrl) {
28863               if (!ctrl) return;
28864               attr.required = true; // force truthy in case we are on non input element
28865
28866               ctrl.$validators.required = function(modelValue, viewValue) {
28867                 return !attr.required || !ctrl.$isEmpty(viewValue);
28868               };
28869
28870               attr.$observe('required', function() {
28871                 ctrl.$validate();
28872               });
28873             }
28874           };
28875         };
28876
28877
28878         var patternDirective = function() {
28879           return {
28880             restrict: 'A',
28881             require: '?ngModel',
28882             link: function(scope, elm, attr, ctrl) {
28883               if (!ctrl) return;
28884
28885               var regexp, patternExp = attr.ngPattern || attr.pattern;
28886               attr.$observe('pattern', function(regex) {
28887                 if (isString(regex) && regex.length > 0) {
28888                   regex = new RegExp('^' + regex + '$');
28889                 }
28890
28891                 if (regex && !regex.test) {
28892                   throw minErr('ngPattern')('noregexp',
28893                     'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28894                     regex, startingTag(elm));
28895                 }
28896
28897                 regexp = regex || undefined;
28898                 ctrl.$validate();
28899               });
28900
28901               ctrl.$validators.pattern = function(modelValue, viewValue) {
28902                 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28903                 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28904               };
28905             }
28906           };
28907         };
28908
28909
28910         var maxlengthDirective = function() {
28911           return {
28912             restrict: 'A',
28913             require: '?ngModel',
28914             link: function(scope, elm, attr, ctrl) {
28915               if (!ctrl) return;
28916
28917               var maxlength = -1;
28918               attr.$observe('maxlength', function(value) {
28919                 var intVal = toInt(value);
28920                 maxlength = isNaN(intVal) ? -1 : intVal;
28921                 ctrl.$validate();
28922               });
28923               ctrl.$validators.maxlength = function(modelValue, viewValue) {
28924                 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28925               };
28926             }
28927           };
28928         };
28929
28930         var minlengthDirective = function() {
28931           return {
28932             restrict: 'A',
28933             require: '?ngModel',
28934             link: function(scope, elm, attr, ctrl) {
28935               if (!ctrl) return;
28936
28937               var minlength = 0;
28938               attr.$observe('minlength', function(value) {
28939                 minlength = toInt(value) || 0;
28940                 ctrl.$validate();
28941               });
28942               ctrl.$validators.minlength = function(modelValue, viewValue) {
28943                 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28944               };
28945             }
28946           };
28947         };
28948
28949         if (window.angular.bootstrap) {
28950           //AngularJS is already loaded, so we can return here...
28951           console.log('WARNING: Tried to load angular more than once.');
28952           return;
28953         }
28954
28955         //try to bind to jquery now so that one can write jqLite(document).ready()
28956         //but we will rebind on bootstrap again.
28957         bindJQuery();
28958
28959         publishExternalAPI(angular);
28960
28961         angular.module("ngLocale", [], ["$provide", function($provide) {
28962         var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28963         function getDecimals(n) {
28964           n = n + '';
28965           var i = n.indexOf('.');
28966           return (i == -1) ? 0 : n.length - i - 1;
28967         }
28968
28969         function getVF(n, opt_precision) {
28970           var v = opt_precision;
28971
28972           if (undefined === v) {
28973             v = Math.min(getDecimals(n), 3);
28974           }
28975
28976           var base = Math.pow(10, v);
28977           var f = ((n * base) | 0) % base;
28978           return {v: v, f: f};
28979         }
28980
28981         $provide.value("$locale", {
28982           "DATETIME_FORMATS": {
28983             "AMPMS": [
28984               "AM",
28985               "PM"
28986             ],
28987             "DAY": [
28988               "Sunday",
28989               "Monday",
28990               "Tuesday",
28991               "Wednesday",
28992               "Thursday",
28993               "Friday",
28994               "Saturday"
28995             ],
28996             "ERANAMES": [
28997               "Before Christ",
28998               "Anno Domini"
28999             ],
29000             "ERAS": [
29001               "BC",
29002               "AD"
29003             ],
29004             "FIRSTDAYOFWEEK": 6,
29005             "MONTH": [
29006               "January",
29007               "February",
29008               "March",
29009               "April",
29010               "May",
29011               "June",
29012               "July",
29013               "August",
29014               "September",
29015               "October",
29016               "November",
29017               "December"
29018             ],
29019             "SHORTDAY": [
29020               "Sun",
29021               "Mon",
29022               "Tue",
29023               "Wed",
29024               "Thu",
29025               "Fri",
29026               "Sat"
29027             ],
29028             "SHORTMONTH": [
29029               "Jan",
29030               "Feb",
29031               "Mar",
29032               "Apr",
29033               "May",
29034               "Jun",
29035               "Jul",
29036               "Aug",
29037               "Sep",
29038               "Oct",
29039               "Nov",
29040               "Dec"
29041             ],
29042             "WEEKENDRANGE": [
29043               5,
29044               6
29045             ],
29046             "fullDate": "EEEE, MMMM d, y",
29047             "longDate": "MMMM d, y",
29048             "medium": "MMM d, y h:mm:ss a",
29049             "mediumDate": "MMM d, y",
29050             "mediumTime": "h:mm:ss a",
29051             "short": "M/d/yy h:mm a",
29052             "shortDate": "M/d/yy",
29053             "shortTime": "h:mm a"
29054           },
29055           "NUMBER_FORMATS": {
29056             "CURRENCY_SYM": "$",
29057             "DECIMAL_SEP": ".",
29058             "GROUP_SEP": ",",
29059             "PATTERNS": [
29060               {
29061                 "gSize": 3,
29062                 "lgSize": 3,
29063                 "maxFrac": 3,
29064                 "minFrac": 0,
29065                 "minInt": 1,
29066                 "negPre": "-",
29067                 "negSuf": "",
29068                 "posPre": "",
29069                 "posSuf": ""
29070               },
29071               {
29072                 "gSize": 3,
29073                 "lgSize": 3,
29074                 "maxFrac": 2,
29075                 "minFrac": 2,
29076                 "minInt": 1,
29077                 "negPre": "-\u00a4",
29078                 "negSuf": "",
29079                 "posPre": "\u00a4",
29080                 "posSuf": ""
29081               }
29082             ]
29083           },
29084           "id": "en-us",
29085           "pluralCat": function(n, opt_precision) {  var i = n | 0;  var vf = getVF(n, opt_precision);  if (i == 1 && vf.v == 0) {    return PLURAL_CATEGORY.ONE;  }  return PLURAL_CATEGORY.OTHER;}
29086         });
29087         }]);
29088
29089           jqLite(document).ready(function() {
29090             angularInit(document, bootstrap);
29091           });
29092
29093         })(window, document);
29094
29095         !window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');
29096
29097 /***/ },
29098 /* 3 */
29099 /***/ function(module, exports) {
29100
29101         /**
29102          * State-based routing for AngularJS
29103          * @version v0.2.15
29104          * @link http://angular-ui.github.com/
29105          * @license MIT License, http://www.opensource.org/licenses/MIT
29106          */
29107
29108         /* commonjs package manager support (eg componentjs) */
29109         if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
29110           module.exports = 'ui.router';
29111         }
29112
29113         (function (window, angular, undefined) {
29114         /*jshint globalstrict:true*/
29115         /*global angular:false*/
29116         'use strict';
29117
29118         var isDefined = angular.isDefined,
29119             isFunction = angular.isFunction,
29120             isString = angular.isString,
29121             isObject = angular.isObject,
29122             isArray = angular.isArray,
29123             forEach = angular.forEach,
29124             extend = angular.extend,
29125             copy = angular.copy;
29126
29127         function inherit(parent, extra) {
29128           return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29129         }
29130
29131         function merge(dst) {
29132           forEach(arguments, function(obj) {
29133             if (obj !== dst) {
29134               forEach(obj, function(value, key) {
29135                 if (!dst.hasOwnProperty(key)) dst[key] = value;
29136               });
29137             }
29138           });
29139           return dst;
29140         }
29141
29142         /**
29143          * Finds the common ancestor path between two states.
29144          *
29145          * @param {Object} first The first state.
29146          * @param {Object} second The second state.
29147          * @return {Array} Returns an array of state names in descending order, not including the root.
29148          */
29149         function ancestors(first, second) {
29150           var path = [];
29151
29152           for (var n in first.path) {
29153             if (first.path[n] !== second.path[n]) break;
29154             path.push(first.path[n]);
29155           }
29156           return path;
29157         }
29158
29159         /**
29160          * IE8-safe wrapper for `Object.keys()`.
29161          *
29162          * @param {Object} object A JavaScript object.
29163          * @return {Array} Returns the keys of the object as an array.
29164          */
29165         function objectKeys(object) {
29166           if (Object.keys) {
29167             return Object.keys(object);
29168           }
29169           var result = [];
29170
29171           forEach(object, function(val, key) {
29172             result.push(key);
29173           });
29174           return result;
29175         }
29176
29177         /**
29178          * IE8-safe wrapper for `Array.prototype.indexOf()`.
29179          *
29180          * @param {Array} array A JavaScript array.
29181          * @param {*} value A value to search the array for.
29182          * @return {Number} Returns the array index value of `value`, or `-1` if not present.
29183          */
29184         function indexOf(array, value) {
29185           if (Array.prototype.indexOf) {
29186             return array.indexOf(value, Number(arguments[2]) || 0);
29187           }
29188           var len = array.length >>> 0, from = Number(arguments[2]) || 0;
29189           from = (from < 0) ? Math.ceil(from) : Math.floor(from);
29190
29191           if (from < 0) from += len;
29192
29193           for (; from < len; from++) {
29194             if (from in array && array[from] === value) return from;
29195           }
29196           return -1;
29197         }
29198
29199         /**
29200          * Merges a set of parameters with all parameters inherited between the common parents of the
29201          * current state and a given destination state.
29202          *
29203          * @param {Object} currentParams The value of the current state parameters ($stateParams).
29204          * @param {Object} newParams The set of parameters which will be composited with inherited params.
29205          * @param {Object} $current Internal definition of object representing the current state.
29206          * @param {Object} $to Internal definition of object representing state to transition to.
29207          */
29208         function inheritParams(currentParams, newParams, $current, $to) {
29209           var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
29210
29211           for (var i in parents) {
29212             if (!parents[i].params) continue;
29213             parentParams = objectKeys(parents[i].params);
29214             if (!parentParams.length) continue;
29215
29216             for (var j in parentParams) {
29217               if (indexOf(inheritList, parentParams[j]) >= 0) continue;
29218               inheritList.push(parentParams[j]);
29219               inherited[parentParams[j]] = currentParams[parentParams[j]];
29220             }
29221           }
29222           return extend({}, inherited, newParams);
29223         }
29224
29225         /**
29226          * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
29227          *
29228          * @param {Object} a The first object.
29229          * @param {Object} b The second object.
29230          * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
29231          *                     it defaults to the list of keys in `a`.
29232          * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
29233          */
29234         function equalForKeys(a, b, keys) {
29235           if (!keys) {
29236             keys = [];
29237             for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
29238           }
29239
29240           for (var i=0; i<keys.length; i++) {
29241             var k = keys[i];
29242             if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
29243           }
29244           return true;
29245         }
29246
29247         /**
29248          * Returns the subset of an object, based on a list of keys.
29249          *
29250          * @param {Array} keys
29251          * @param {Object} values
29252          * @return {Boolean} Returns a subset of `values`.
29253          */
29254         function filterByKeys(keys, values) {
29255           var filtered = {};
29256
29257           forEach(keys, function (name) {
29258             filtered[name] = values[name];
29259           });
29260           return filtered;
29261         }
29262
29263         // like _.indexBy
29264         // when you know that your index values will be unique, or you want last-one-in to win
29265         function indexBy(array, propName) {
29266           var result = {};
29267           forEach(array, function(item) {
29268             result[item[propName]] = item;
29269           });
29270           return result;
29271         }
29272
29273         // extracted from underscore.js
29274         // Return a copy of the object only containing the whitelisted properties.
29275         function pick(obj) {
29276           var copy = {};
29277           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29278           forEach(keys, function(key) {
29279             if (key in obj) copy[key] = obj[key];
29280           });
29281           return copy;
29282         }
29283
29284         // extracted from underscore.js
29285         // Return a copy of the object omitting the blacklisted properties.
29286         function omit(obj) {
29287           var copy = {};
29288           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29289           for (var key in obj) {
29290             if (indexOf(keys, key) == -1) copy[key] = obj[key];
29291           }
29292           return copy;
29293         }
29294
29295         function pluck(collection, key) {
29296           var result = isArray(collection) ? [] : {};
29297
29298           forEach(collection, function(val, i) {
29299             result[i] = isFunction(key) ? key(val) : val[key];
29300           });
29301           return result;
29302         }
29303
29304         function filter(collection, callback) {
29305           var array = isArray(collection);
29306           var result = array ? [] : {};
29307           forEach(collection, function(val, i) {
29308             if (callback(val, i)) {
29309               result[array ? result.length : i] = val;
29310             }
29311           });
29312           return result;
29313         }
29314
29315         function map(collection, callback) {
29316           var result = isArray(collection) ? [] : {};
29317
29318           forEach(collection, function(val, i) {
29319             result[i] = callback(val, i);
29320           });
29321           return result;
29322         }
29323
29324         /**
29325          * @ngdoc overview
29326          * @name ui.router.util
29327          *
29328          * @description
29329          * # ui.router.util sub-module
29330          *
29331          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29332          * in your angular app (use {@link ui.router} module instead).
29333          *
29334          */
29335         angular.module('ui.router.util', ['ng']);
29336
29337         /**
29338          * @ngdoc overview
29339          * @name ui.router.router
29340          * 
29341          * @requires ui.router.util
29342          *
29343          * @description
29344          * # ui.router.router sub-module
29345          *
29346          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29347          * in your angular app (use {@link ui.router} module instead).
29348          */
29349         angular.module('ui.router.router', ['ui.router.util']);
29350
29351         /**
29352          * @ngdoc overview
29353          * @name ui.router.state
29354          * 
29355          * @requires ui.router.router
29356          * @requires ui.router.util
29357          *
29358          * @description
29359          * # ui.router.state sub-module
29360          *
29361          * This module is a dependency of the main ui.router module. Do not include this module as a dependency
29362          * in your angular app (use {@link ui.router} module instead).
29363          * 
29364          */
29365         angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
29366
29367         /**
29368          * @ngdoc overview
29369          * @name ui.router
29370          *
29371          * @requires ui.router.state
29372          *
29373          * @description
29374          * # ui.router
29375          * 
29376          * ## The main module for ui.router 
29377          * There are several sub-modules included with the ui.router module, however only this module is needed
29378          * as a dependency within your angular app. The other modules are for organization purposes. 
29379          *
29380          * The modules are:
29381          * * ui.router - the main "umbrella" module
29382          * * ui.router.router - 
29383          * 
29384          * *You'll need to include **only** this module as the dependency within your angular app.*
29385          * 
29386          * <pre>
29387          * <!doctype html>
29388          * <html ng-app="myApp">
29389          * <head>
29390          *   <script src="js/angular.js"></script>
29391          *   <!-- Include the ui-router script -->
29392          *   <script src="js/angular-ui-router.min.js"></script>
29393          *   <script>
29394          *     // ...and add 'ui.router' as a dependency
29395          *     var myApp = angular.module('myApp', ['ui.router']);
29396          *   </script>
29397          * </head>
29398          * <body>
29399          * </body>
29400          * </html>
29401          * </pre>
29402          */
29403         angular.module('ui.router', ['ui.router.state']);
29404
29405         angular.module('ui.router.compat', ['ui.router']);
29406
29407         /**
29408          * @ngdoc object
29409          * @name ui.router.util.$resolve
29410          *
29411          * @requires $q
29412          * @requires $injector
29413          *
29414          * @description
29415          * Manages resolution of (acyclic) graphs of promises.
29416          */
29417         $Resolve.$inject = ['$q', '$injector'];
29418         function $Resolve(  $q,    $injector) {
29419           
29420           var VISIT_IN_PROGRESS = 1,
29421               VISIT_DONE = 2,
29422               NOTHING = {},
29423               NO_DEPENDENCIES = [],
29424               NO_LOCALS = NOTHING,
29425               NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
29426           
29427
29428           /**
29429            * @ngdoc function
29430            * @name ui.router.util.$resolve#study
29431            * @methodOf ui.router.util.$resolve
29432            *
29433            * @description
29434            * Studies a set of invocables that are likely to be used multiple times.
29435            * <pre>
29436            * $resolve.study(invocables)(locals, parent, self)
29437            * </pre>
29438            * is equivalent to
29439            * <pre>
29440            * $resolve.resolve(invocables, locals, parent, self)
29441            * </pre>
29442            * but the former is more efficient (in fact `resolve` just calls `study` 
29443            * internally).
29444            *
29445            * @param {object} invocables Invocable objects
29446            * @return {function} a function to pass in locals, parent and self
29447            */
29448           this.study = function (invocables) {
29449             if (!isObject(invocables)) throw new Error("'invocables' must be an object");
29450             var invocableKeys = objectKeys(invocables || {});
29451             
29452             // Perform a topological sort of invocables to build an ordered plan
29453             var plan = [], cycle = [], visited = {};
29454             function visit(value, key) {
29455               if (visited[key] === VISIT_DONE) return;
29456               
29457               cycle.push(key);
29458               if (visited[key] === VISIT_IN_PROGRESS) {
29459                 cycle.splice(0, indexOf(cycle, key));
29460                 throw new Error("Cyclic dependency: " + cycle.join(" -> "));
29461               }
29462               visited[key] = VISIT_IN_PROGRESS;
29463               
29464               if (isString(value)) {
29465                 plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
29466               } else {
29467                 var params = $injector.annotate(value);
29468                 forEach(params, function (param) {
29469                   if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
29470                 });
29471                 plan.push(key, value, params);
29472               }
29473               
29474               cycle.pop();
29475               visited[key] = VISIT_DONE;
29476             }
29477             forEach(invocables, visit);
29478             invocables = cycle = visited = null; // plan is all that's required
29479             
29480             function isResolve(value) {
29481               return isObject(value) && value.then && value.$$promises;
29482             }
29483             
29484             return function (locals, parent, self) {
29485               if (isResolve(locals) && self === undefined) {
29486                 self = parent; parent = locals; locals = null;
29487               }
29488               if (!locals) locals = NO_LOCALS;
29489               else if (!isObject(locals)) {
29490                 throw new Error("'locals' must be an object");
29491               }       
29492               if (!parent) parent = NO_PARENT;
29493               else if (!isResolve(parent)) {
29494                 throw new Error("'parent' must be a promise returned by $resolve.resolve()");
29495               }
29496               
29497               // To complete the overall resolution, we have to wait for the parent
29498               // promise and for the promise for each invokable in our plan.
29499               var resolution = $q.defer(),
29500                   result = resolution.promise,
29501                   promises = result.$$promises = {},
29502                   values = extend({}, locals),
29503                   wait = 1 + plan.length/3,
29504                   merged = false;
29505                   
29506               function done() {
29507                 // Merge parent values we haven't got yet and publish our own $$values
29508                 if (!--wait) {
29509                   if (!merged) merge(values, parent.$$values); 
29510                   result.$$values = values;
29511                   result.$$promises = result.$$promises || true; // keep for isResolve()
29512                   delete result.$$inheritedValues;
29513                   resolution.resolve(values);
29514                 }
29515               }
29516               
29517               function fail(reason) {
29518                 result.$$failure = reason;
29519                 resolution.reject(reason);
29520               }
29521
29522               // Short-circuit if parent has already failed
29523               if (isDefined(parent.$$failure)) {
29524                 fail(parent.$$failure);
29525                 return result;
29526               }
29527               
29528               if (parent.$$inheritedValues) {
29529                 merge(values, omit(parent.$$inheritedValues, invocableKeys));
29530               }
29531
29532               // Merge parent values if the parent has already resolved, or merge
29533               // parent promises and wait if the parent resolve is still in progress.
29534               extend(promises, parent.$$promises);
29535               if (parent.$$values) {
29536                 merged = merge(values, omit(parent.$$values, invocableKeys));
29537                 result.$$inheritedValues = omit(parent.$$values, invocableKeys);
29538                 done();
29539               } else {
29540                 if (parent.$$inheritedValues) {
29541                   result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
29542                 }        
29543                 parent.then(done, fail);
29544               }
29545               
29546               // Process each invocable in the plan, but ignore any where a local of the same name exists.
29547               for (var i=0, ii=plan.length; i<ii; i+=3) {
29548                 if (locals.hasOwnProperty(plan[i])) done();
29549                 else invoke(plan[i], plan[i+1], plan[i+2]);
29550               }
29551               
29552               function invoke(key, invocable, params) {
29553                 // Create a deferred for this invocation. Failures will propagate to the resolution as well.
29554                 var invocation = $q.defer(), waitParams = 0;
29555                 function onfailure(reason) {
29556                   invocation.reject(reason);
29557                   fail(reason);
29558                 }
29559                 // Wait for any parameter that we have a promise for (either from parent or from this
29560                 // resolve; in that case study() will have made sure it's ordered before us in the plan).
29561                 forEach(params, function (dep) {
29562                   if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
29563                     waitParams++;
29564                     promises[dep].then(function (result) {
29565                       values[dep] = result;
29566                       if (!(--waitParams)) proceed();
29567                     }, onfailure);
29568                   }
29569                 });
29570                 if (!waitParams) proceed();
29571                 function proceed() {
29572                   if (isDefined(result.$$failure)) return;
29573                   try {
29574                     invocation.resolve($injector.invoke(invocable, self, values));
29575                     invocation.promise.then(function (result) {
29576                       values[key] = result;
29577                       done();
29578                     }, onfailure);
29579                   } catch (e) {
29580                     onfailure(e);
29581                   }
29582                 }
29583                 // Publish promise synchronously; invocations further down in the plan may depend on it.
29584                 promises[key] = invocation.promise;
29585               }
29586               
29587               return result;
29588             };
29589           };
29590           
29591           /**
29592            * @ngdoc function
29593            * @name ui.router.util.$resolve#resolve
29594            * @methodOf ui.router.util.$resolve
29595            *
29596            * @description
29597            * Resolves a set of invocables. An invocable is a function to be invoked via 
29598            * `$injector.invoke()`, and can have an arbitrary number of dependencies. 
29599            * An invocable can either return a value directly,
29600            * or a `$q` promise. If a promise is returned it will be resolved and the 
29601            * resulting value will be used instead. Dependencies of invocables are resolved 
29602            * (in this order of precedence)
29603            *
29604            * - from the specified `locals`
29605            * - from another invocable that is part of this `$resolve` call
29606            * - from an invocable that is inherited from a `parent` call to `$resolve` 
29607            *   (or recursively
29608            * - from any ancestor `$resolve` of that parent).
29609            *
29610            * The return value of `$resolve` is a promise for an object that contains 
29611            * (in this order of precedence)
29612            *
29613            * - any `locals` (if specified)
29614            * - the resolved return values of all injectables
29615            * - any values inherited from a `parent` call to `$resolve` (if specified)
29616            *
29617            * The promise will resolve after the `parent` promise (if any) and all promises 
29618            * returned by injectables have been resolved. If any invocable 
29619            * (or `$injector.invoke`) throws an exception, or if a promise returned by an 
29620            * invocable is rejected, the `$resolve` promise is immediately rejected with the 
29621            * same error. A rejection of a `parent` promise (if specified) will likewise be 
29622            * propagated immediately. Once the `$resolve` promise has been rejected, no 
29623            * further invocables will be called.
29624            * 
29625            * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
29626            * to throw an error. As a special case, an injectable can depend on a parameter 
29627            * with the same name as the injectable, which will be fulfilled from the `parent` 
29628            * injectable of the same name. This allows inherited values to be decorated. 
29629            * Note that in this case any other injectable in the same `$resolve` with the same
29630            * dependency would see the decorated value, not the inherited value.
29631            *
29632            * Note that missing dependencies -- unlike cyclic dependencies -- will cause an 
29633            * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous) 
29634            * exception.
29635            *
29636            * Invocables are invoked eagerly as soon as all dependencies are available. 
29637            * This is true even for dependencies inherited from a `parent` call to `$resolve`.
29638            *
29639            * As a special case, an invocable can be a string, in which case it is taken to 
29640            * be a service name to be passed to `$injector.get()`. This is supported primarily 
29641            * for backwards-compatibility with the `resolve` property of `$routeProvider` 
29642            * routes.
29643            *
29644            * @param {object} invocables functions to invoke or 
29645            * `$injector` services to fetch.
29646            * @param {object} locals  values to make available to the injectables
29647            * @param {object} parent  a promise returned by another call to `$resolve`.
29648            * @param {object} self  the `this` for the invoked methods
29649            * @return {object} Promise for an object that contains the resolved return value
29650            * of all invocables, as well as any inherited and local values.
29651            */
29652           this.resolve = function (invocables, locals, parent, self) {
29653             return this.study(invocables)(locals, parent, self);
29654           };
29655         }
29656
29657         angular.module('ui.router.util').service('$resolve', $Resolve);
29658
29659
29660         /**
29661          * @ngdoc object
29662          * @name ui.router.util.$templateFactory
29663          *
29664          * @requires $http
29665          * @requires $templateCache
29666          * @requires $injector
29667          *
29668          * @description
29669          * Service. Manages loading of templates.
29670          */
29671         $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
29672         function $TemplateFactory(  $http,   $templateCache,   $injector) {
29673
29674           /**
29675            * @ngdoc function
29676            * @name ui.router.util.$templateFactory#fromConfig
29677            * @methodOf ui.router.util.$templateFactory
29678            *
29679            * @description
29680            * Creates a template from a configuration object. 
29681            *
29682            * @param {object} config Configuration object for which to load a template. 
29683            * The following properties are search in the specified order, and the first one 
29684            * that is defined is used to create the template:
29685            *
29686            * @param {string|object} config.template html string template or function to 
29687            * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29688            * @param {string|object} config.templateUrl url to load or a function returning 
29689            * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
29690            * @param {Function} config.templateProvider function to invoke via 
29691            * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
29692            * @param {object} params  Parameters to pass to the template function.
29693            * @param {object} locals Locals to pass to `invoke` if the template is loaded 
29694            * via a `templateProvider`. Defaults to `{ params: params }`.
29695            *
29696            * @return {string|object}  The template html as a string, or a promise for 
29697            * that string,or `null` if no template is configured.
29698            */
29699           this.fromConfig = function (config, params, locals) {
29700             return (
29701               isDefined(config.template) ? this.fromString(config.template, params) :
29702               isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
29703               isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
29704               null
29705             );
29706           };
29707
29708           /**
29709            * @ngdoc function
29710            * @name ui.router.util.$templateFactory#fromString
29711            * @methodOf ui.router.util.$templateFactory
29712            *
29713            * @description
29714            * Creates a template from a string or a function returning a string.
29715            *
29716            * @param {string|object} template html template as a string or function that 
29717            * returns an html template as a string.
29718            * @param {object} params Parameters to pass to the template function.
29719            *
29720            * @return {string|object} The template html as a string, or a promise for that 
29721            * string.
29722            */
29723           this.fromString = function (template, params) {
29724             return isFunction(template) ? template(params) : template;
29725           };
29726
29727           /**
29728            * @ngdoc function
29729            * @name ui.router.util.$templateFactory#fromUrl
29730            * @methodOf ui.router.util.$templateFactory
29731            * 
29732            * @description
29733            * Loads a template from the a URL via `$http` and `$templateCache`.
29734            *
29735            * @param {string|Function} url url of the template to load, or a function 
29736            * that returns a url.
29737            * @param {Object} params Parameters to pass to the url function.
29738            * @return {string|Promise.<string>} The template html as a string, or a promise 
29739            * for that string.
29740            */
29741           this.fromUrl = function (url, params) {
29742             if (isFunction(url)) url = url(params);
29743             if (url == null) return null;
29744             else return $http
29745                 .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
29746                 .then(function(response) { return response.data; });
29747           };
29748
29749           /**
29750            * @ngdoc function
29751            * @name ui.router.util.$templateFactory#fromProvider
29752            * @methodOf ui.router.util.$templateFactory
29753            *
29754            * @description
29755            * Creates a template by invoking an injectable provider function.
29756            *
29757            * @param {Function} provider Function to invoke via `$injector.invoke`
29758            * @param {Object} params Parameters for the template.
29759            * @param {Object} locals Locals to pass to `invoke`. Defaults to 
29760            * `{ params: params }`.
29761            * @return {string|Promise.<string>} The template html as a string, or a promise 
29762            * for that string.
29763            */
29764           this.fromProvider = function (provider, params, locals) {
29765             return $injector.invoke(provider, null, locals || { params: params });
29766           };
29767         }
29768
29769         angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
29770
29771         var $$UMFP; // reference to $UrlMatcherFactoryProvider
29772
29773         /**
29774          * @ngdoc object
29775          * @name ui.router.util.type:UrlMatcher
29776          *
29777          * @description
29778          * Matches URLs against patterns and extracts named parameters from the path or the search
29779          * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
29780          * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
29781          * do not influence whether or not a URL is matched, but their values are passed through into
29782          * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
29783          *
29784          * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
29785          * syntax, which optionally allows a regular expression for the parameter to be specified:
29786          *
29787          * * `':'` name - colon placeholder
29788          * * `'*'` name - catch-all placeholder
29789          * * `'{' name '}'` - curly placeholder
29790          * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
29791          *   regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
29792          *
29793          * Parameter names may contain only word characters (latin letters, digits, and underscore) and
29794          * must be unique within the pattern (across both path and search parameters). For colon
29795          * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
29796          * number of characters other than '/'. For catch-all placeholders the path parameter matches
29797          * any number of characters.
29798          *
29799          * Examples:
29800          *
29801          * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
29802          *   trailing slashes, and patterns have to match the entire path, not just a prefix.
29803          * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
29804          *   '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
29805          * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
29806          * * `'/user/{id:[^/]*}'` - Same as the previous example.
29807          * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
29808          *   parameter consists of 1 to 8 hex digits.
29809          * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
29810          *   path into the parameter 'path'.
29811          * * `'/files/*path'` - ditto.
29812          * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
29813          *   in the built-in  `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
29814          *
29815          * @param {string} pattern  The pattern to compile into a matcher.
29816          * @param {Object} config  A configuration object hash:
29817          * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
29818          *   an existing UrlMatcher
29819          *
29820          * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
29821          * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`.
29822          *
29823          * @property {string} prefix  A static prefix of this pattern. The matcher guarantees that any
29824          *   URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
29825          *   non-null) will start with this prefix.
29826          *
29827          * @property {string} source  The pattern that was passed into the constructor
29828          *
29829          * @property {string} sourcePath  The path portion of the source property
29830          *
29831          * @property {string} sourceSearch  The search portion of the source property
29832          *
29833          * @property {string} regex  The constructed regex that will be used to match against the url when
29834          *   it is time to determine which url will match.
29835          *
29836          * @returns {Object}  New `UrlMatcher` object
29837          */
29838         function UrlMatcher(pattern, config, parentMatcher) {
29839           config = extend({ params: {} }, isObject(config) ? config : {});
29840
29841           // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
29842           //   '*' name
29843           //   ':' name
29844           //   '{' name '}'
29845           //   '{' name ':' regexp '}'
29846           // The regular expression is somewhat complicated due to the need to allow curly braces
29847           // inside the regular expression. The placeholder regexp breaks down as follows:
29848           //    ([:*])([\w\[\]]+)              - classic placeholder ($1 / $2) (search version has - for snake-case)
29849           //    \{([\w\[\]]+)(?:\:( ... ))?\}  - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29850           //    (?: ... | ... | ... )+         - the regexp consists of any number of atoms, an atom being either
29851           //    [^{}\\]+                       - anything other than curly braces or backslash
29852           //    \\.                            - a backslash escape
29853           //    \{(?:[^{}\\]+|\\.)*\}          - a matched set of curly braces containing other atoms
29854           var placeholder       = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29855               searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29856               compiled = '^', last = 0, m,
29857               segments = this.segments = [],
29858               parentParams = parentMatcher ? parentMatcher.params : {},
29859               params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
29860               paramNames = [];
29861
29862           function addParameter(id, type, config, location) {
29863             paramNames.push(id);
29864             if (parentParams[id]) return parentParams[id];
29865             if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29866             if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
29867             params[id] = new $$UMFP.Param(id, type, config, location);
29868             return params[id];
29869           }
29870
29871           function quoteRegExp(string, pattern, squash, optional) {
29872             var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
29873             if (!pattern) return result;
29874             switch(squash) {
29875               case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
29876               case true:  surroundPattern = ['?(', ')?']; break;
29877               default:    surroundPattern = ['(' + squash + "|", ')?']; break;
29878             }
29879             return result + surroundPattern[0] + pattern + surroundPattern[1];
29880           }
29881
29882           this.source = pattern;
29883
29884           // Split into static segments separated by path parameter placeholders.
29885           // The number of segments is always 1 more than the number of parameters.
29886           function matchDetails(m, isSearch) {
29887             var id, regexp, segment, type, cfg, arrayMode;
29888             id          = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
29889             cfg         = config.params[id];
29890             segment     = pattern.substring(last, m.index);
29891             regexp      = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
29892             type        = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29893             return {
29894               id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
29895             };
29896           }
29897
29898           var p, param, segment;
29899           while ((m = placeholder.exec(pattern))) {
29900             p = matchDetails(m, false);
29901             if (p.segment.indexOf('?') >= 0) break; // we're into the search part
29902
29903             param = addParameter(p.id, p.type, p.cfg, "path");
29904             compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
29905             segments.push(p.segment);
29906             last = placeholder.lastIndex;
29907           }
29908           segment = pattern.substring(last);
29909
29910           // Find any search parameter names and remove them from the last segment
29911           var i = segment.indexOf('?');
29912
29913           if (i >= 0) {
29914             var search = this.sourceSearch = segment.substring(i);
29915             segment = segment.substring(0, i);
29916             this.sourcePath = pattern.substring(0, last + i);
29917
29918             if (search.length > 0) {
29919               last = 0;
29920               while ((m = searchPlaceholder.exec(search))) {
29921                 p = matchDetails(m, true);
29922                 param = addParameter(p.id, p.type, p.cfg, "search");
29923                 last = placeholder.lastIndex;
29924                 // check if ?&
29925               }
29926             }
29927           } else {
29928             this.sourcePath = pattern;
29929             this.sourceSearch = '';
29930           }
29931
29932           compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
29933           segments.push(segment);
29934
29935           this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
29936           this.prefix = segments[0];
29937           this.$$paramNames = paramNames;
29938         }
29939
29940         /**
29941          * @ngdoc function
29942          * @name ui.router.util.type:UrlMatcher#concat
29943          * @methodOf ui.router.util.type:UrlMatcher
29944          *
29945          * @description
29946          * Returns a new matcher for a pattern constructed by appending the path part and adding the
29947          * search parameters of the specified pattern to this pattern. The current pattern is not
29948          * modified. This can be understood as creating a pattern for URLs that are relative to (or
29949          * suffixes of) the current pattern.
29950          *
29951          * @example
29952          * The following two matchers are equivalent:
29953          * <pre>
29954          * new UrlMatcher('/user/{id}?q').concat('/details?date');
29955          * new UrlMatcher('/user/{id}/details?q&date');
29956          * </pre>
29957          *
29958          * @param {string} pattern  The pattern to append.
29959          * @param {Object} config  An object hash of the configuration for the matcher.
29960          * @returns {UrlMatcher}  A matcher for the concatenated pattern.
29961          */
29962         UrlMatcher.prototype.concat = function (pattern, config) {
29963           // Because order of search parameters is irrelevant, we can add our own search
29964           // parameters to the end of the new pattern. Parse the new pattern by itself
29965           // and then join the bits together, but it's much easier to do this on a string level.
29966           var defaultConfig = {
29967             caseInsensitive: $$UMFP.caseInsensitive(),
29968             strict: $$UMFP.strictMode(),
29969             squash: $$UMFP.defaultSquashPolicy()
29970           };
29971           return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
29972         };
29973
29974         UrlMatcher.prototype.toString = function () {
29975           return this.source;
29976         };
29977
29978         /**
29979          * @ngdoc function
29980          * @name ui.router.util.type:UrlMatcher#exec
29981          * @methodOf ui.router.util.type:UrlMatcher
29982          *
29983          * @description
29984          * Tests the specified path against this matcher, and returns an object containing the captured
29985          * parameter values, or null if the path does not match. The returned object contains the values
29986          * of any search parameters that are mentioned in the pattern, but their value may be null if
29987          * they are not present in `searchParams`. This means that search parameters are always treated
29988          * as optional.
29989          *
29990          * @example
29991          * <pre>
29992          * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
29993          *   x: '1', q: 'hello'
29994          * });
29995          * // returns { id: 'bob', q: 'hello', r: null }
29996          * </pre>
29997          *
29998          * @param {string} path  The URL path to match, e.g. `$location.path()`.
29999          * @param {Object} searchParams  URL search parameters, e.g. `$location.search()`.
30000          * @returns {Object}  The captured parameter values.
30001          */
30002         UrlMatcher.prototype.exec = function (path, searchParams) {
30003           var m = this.regexp.exec(path);
30004           if (!m) return null;
30005           searchParams = searchParams || {};
30006
30007           var paramNames = this.parameters(), nTotal = paramNames.length,
30008             nPath = this.segments.length - 1,
30009             values = {}, i, j, cfg, paramName;
30010
30011           if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
30012
30013           function decodePathArray(string) {
30014             function reverseString(str) { return str.split("").reverse().join(""); }
30015             function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
30016
30017             var split = reverseString(string).split(/-(?!\\)/);
30018             var allReversed = map(split, reverseString);
30019             return map(allReversed, unquoteDashes).reverse();
30020           }
30021
30022           for (i = 0; i < nPath; i++) {
30023             paramName = paramNames[i];
30024             var param = this.params[paramName];
30025             var paramVal = m[i+1];
30026             // if the param value matches a pre-replace pair, replace the value before decoding.
30027             for (j = 0; j < param.replace; j++) {
30028               if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30029             }
30030             if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30031             values[paramName] = param.value(paramVal);
30032           }
30033           for (/**/; i < nTotal; i++) {
30034             paramName = paramNames[i];
30035             values[paramName] = this.params[paramName].value(searchParams[paramName]);
30036           }
30037
30038           return values;
30039         };
30040
30041         /**
30042          * @ngdoc function
30043          * @name ui.router.util.type:UrlMatcher#parameters
30044          * @methodOf ui.router.util.type:UrlMatcher
30045          *
30046          * @description
30047          * Returns the names of all path and search parameters of this pattern in an unspecified order.
30048          *
30049          * @returns {Array.<string>}  An array of parameter names. Must be treated as read-only. If the
30050          *    pattern has no parameters, an empty array is returned.
30051          */
30052         UrlMatcher.prototype.parameters = function (param) {
30053           if (!isDefined(param)) return this.$$paramNames;
30054           return this.params[param] || null;
30055         };
30056
30057         /**
30058          * @ngdoc function
30059          * @name ui.router.util.type:UrlMatcher#validate
30060          * @methodOf ui.router.util.type:UrlMatcher
30061          *
30062          * @description
30063          * Checks an object hash of parameters to validate their correctness according to the parameter
30064          * types of this `UrlMatcher`.
30065          *
30066          * @param {Object} params The object hash of parameters to validate.
30067          * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
30068          */
30069         UrlMatcher.prototype.validates = function (params) {
30070           return this.params.$$validates(params);
30071         };
30072
30073         /**
30074          * @ngdoc function
30075          * @name ui.router.util.type:UrlMatcher#format
30076          * @methodOf ui.router.util.type:UrlMatcher
30077          *
30078          * @description
30079          * Creates a URL that matches this pattern by substituting the specified values
30080          * for the path and search parameters. Null values for path parameters are
30081          * treated as empty strings.
30082          *
30083          * @example
30084          * <pre>
30085          * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
30086          * // returns '/user/bob?q=yes'
30087          * </pre>
30088          *
30089          * @param {Object} values  the values to substitute for the parameters in this pattern.
30090          * @returns {string}  the formatted URL (path and optionally search part).
30091          */
30092         UrlMatcher.prototype.format = function (values) {
30093           values = values || {};
30094           var segments = this.segments, params = this.parameters(), paramset = this.params;
30095           if (!this.validates(values)) return null;
30096
30097           var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
30098
30099           function encodeDashes(str) { // Replace dashes with encoded "\-"
30100             return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
30101           }
30102
30103           for (i = 0; i < nTotal; i++) {
30104             var isPathParam = i < nPath;
30105             var name = params[i], param = paramset[name], value = param.value(values[name]);
30106             var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
30107             var squash = isDefaultValue ? param.squash : false;
30108             var encoded = param.type.encode(value);
30109
30110             if (isPathParam) {
30111               var nextSegment = segments[i + 1];
30112               if (squash === false) {
30113                 if (encoded != null) {
30114                   if (isArray(encoded)) {
30115                     result += map(encoded, encodeDashes).join("-");
30116                   } else {
30117                     result += encodeURIComponent(encoded);
30118                   }
30119                 }
30120                 result += nextSegment;
30121               } else if (squash === true) {
30122                 var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
30123                 result += nextSegment.match(capture)[1];
30124               } else if (isString(squash)) {
30125                 result += squash + nextSegment;
30126               }
30127             } else {
30128               if (encoded == null || (isDefaultValue && squash !== false)) continue;
30129               if (!isArray(encoded)) encoded = [ encoded ];
30130               encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
30131               result += (search ? '&' : '?') + (name + '=' + encoded);
30132               search = true;
30133             }
30134           }
30135
30136           return result;
30137         };
30138
30139         /**
30140          * @ngdoc object
30141          * @name ui.router.util.type:Type
30142          *
30143          * @description
30144          * Implements an interface to define custom parameter types that can be decoded from and encoded to
30145          * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
30146          * objects when matching or formatting URLs, or comparing or validating parameter values.
30147          *
30148          * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
30149          * information on registering custom types.
30150          *
30151          * @param {Object} config  A configuration object which contains the custom type definition.  The object's
30152          *        properties will override the default methods and/or pattern in `Type`'s public interface.
30153          * @example
30154          * <pre>
30155          * {
30156          *   decode: function(val) { return parseInt(val, 10); },
30157          *   encode: function(val) { return val && val.toString(); },
30158          *   equals: function(a, b) { return this.is(a) && a === b; },
30159          *   is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
30160          *   pattern: /\d+/
30161          * }
30162          * </pre>
30163          *
30164          * @property {RegExp} pattern The regular expression pattern used to match values of this type when
30165          *           coming from a substring of a URL.
30166          *
30167          * @returns {Object}  Returns a new `Type` object.
30168          */
30169         function Type(config) {
30170           extend(this, config);
30171         }
30172
30173         /**
30174          * @ngdoc function
30175          * @name ui.router.util.type:Type#is
30176          * @methodOf ui.router.util.type:Type
30177          *
30178          * @description
30179          * Detects whether a value is of a particular type. Accepts a native (decoded) value
30180          * and determines whether it matches the current `Type` object.
30181          *
30182          * @param {*} val  The value to check.
30183          * @param {string} key  Optional. If the type check is happening in the context of a specific
30184          *        {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
30185          *        parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
30186          * @returns {Boolean}  Returns `true` if the value matches the type, otherwise `false`.
30187          */
30188         Type.prototype.is = function(val, key) {
30189           return true;
30190         };
30191
30192         /**
30193          * @ngdoc function
30194          * @name ui.router.util.type:Type#encode
30195          * @methodOf ui.router.util.type:Type
30196          *
30197          * @description
30198          * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
30199          * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
30200          * only needs to be a representation of `val` that has been coerced to a string.
30201          *
30202          * @param {*} val  The value to encode.
30203          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30204          *        meta-programming of `Type` objects.
30205          * @returns {string}  Returns a string representation of `val` that can be encoded in a URL.
30206          */
30207         Type.prototype.encode = function(val, key) {
30208           return val;
30209         };
30210
30211         /**
30212          * @ngdoc function
30213          * @name ui.router.util.type:Type#decode
30214          * @methodOf ui.router.util.type:Type
30215          *
30216          * @description
30217          * Converts a parameter value (from URL string or transition param) to a custom/native value.
30218          *
30219          * @param {string} val  The URL parameter value to decode.
30220          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30221          *        meta-programming of `Type` objects.
30222          * @returns {*}  Returns a custom representation of the URL parameter value.
30223          */
30224         Type.prototype.decode = function(val, key) {
30225           return val;
30226         };
30227
30228         /**
30229          * @ngdoc function
30230          * @name ui.router.util.type:Type#equals
30231          * @methodOf ui.router.util.type:Type
30232          *
30233          * @description
30234          * Determines whether two decoded values are equivalent.
30235          *
30236          * @param {*} a  A value to compare against.
30237          * @param {*} b  A value to compare against.
30238          * @returns {Boolean}  Returns `true` if the values are equivalent/equal, otherwise `false`.
30239          */
30240         Type.prototype.equals = function(a, b) {
30241           return a == b;
30242         };
30243
30244         Type.prototype.$subPattern = function() {
30245           var sub = this.pattern.toString();
30246           return sub.substr(1, sub.length - 2);
30247         };
30248
30249         Type.prototype.pattern = /.*/;
30250
30251         Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
30252
30253         /** Given an encoded string, or a decoded object, returns a decoded object */
30254         Type.prototype.$normalize = function(val) {
30255           return this.is(val) ? val : this.decode(val);
30256         };
30257
30258         /*
30259          * Wraps an existing custom Type as an array of Type, depending on 'mode'.
30260          * e.g.:
30261          * - urlmatcher pattern "/path?{queryParam[]:int}"
30262          * - url: "/path?queryParam=1&queryParam=2
30263          * - $stateParams.queryParam will be [1, 2]
30264          * if `mode` is "auto", then
30265          * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
30266          * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
30267          */
30268         Type.prototype.$asArray = function(mode, isSearch) {
30269           if (!mode) return this;
30270           if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
30271
30272           function ArrayType(type, mode) {
30273             function bindTo(type, callbackName) {
30274               return function() {
30275                 return type[callbackName].apply(type, arguments);
30276               };
30277             }
30278
30279             // Wrap non-array value as array
30280             function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
30281             // Unwrap array value for "auto" mode. Return undefined for empty array.
30282             function arrayUnwrap(val) {
30283               switch(val.length) {
30284                 case 0: return undefined;
30285                 case 1: return mode === "auto" ? val[0] : val;
30286                 default: return val;
30287               }
30288             }
30289             function falsey(val) { return !val; }
30290
30291             // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
30292             function arrayHandler(callback, allTruthyMode) {
30293               return function handleArray(val) {
30294                 val = arrayWrap(val);
30295                 var result = map(val, callback);
30296                 if (allTruthyMode === true)
30297                   return filter(result, falsey).length === 0;
30298                 return arrayUnwrap(result);
30299               };
30300             }
30301
30302             // Wraps type (.equals) functions to operate on each value of an array
30303             function arrayEqualsHandler(callback) {
30304               return function handleArray(val1, val2) {
30305                 var left = arrayWrap(val1), right = arrayWrap(val2);
30306                 if (left.length !== right.length) return false;
30307                 for (var i = 0; i < left.length; i++) {
30308                   if (!callback(left[i], right[i])) return false;
30309                 }
30310                 return true;
30311               };
30312             }
30313
30314             this.encode = arrayHandler(bindTo(type, 'encode'));
30315             this.decode = arrayHandler(bindTo(type, 'decode'));
30316             this.is     = arrayHandler(bindTo(type, 'is'), true);
30317             this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
30318             this.pattern = type.pattern;
30319             this.$normalize = arrayHandler(bindTo(type, '$normalize'));
30320             this.name = type.name;
30321             this.$arrayMode = mode;
30322           }
30323
30324           return new ArrayType(this, mode);
30325         };
30326
30327
30328
30329         /**
30330          * @ngdoc object
30331          * @name ui.router.util.$urlMatcherFactory
30332          *
30333          * @description
30334          * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
30335          * is also available to providers under the name `$urlMatcherFactoryProvider`.
30336          */
30337         function $UrlMatcherFactory() {
30338           $$UMFP = this;
30339
30340           var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
30341
30342           function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
30343           function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
30344
30345           var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30346             string: {
30347               encode: valToString,
30348               decode: valFromString,
30349               // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
30350               // In 0.2.x, string params are optional by default for backwards compat
30351               is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
30352               pattern: /[^/]*/
30353             },
30354             int: {
30355               encode: valToString,
30356               decode: function(val) { return parseInt(val, 10); },
30357               is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
30358               pattern: /\d+/
30359             },
30360             bool: {
30361               encode: function(val) { return val ? 1 : 0; },
30362               decode: function(val) { return parseInt(val, 10) !== 0; },
30363               is: function(val) { return val === true || val === false; },
30364               pattern: /0|1/
30365             },
30366             date: {
30367               encode: function (val) {
30368                 if (!this.is(val))
30369                   return undefined;
30370                 return [ val.getFullYear(),
30371                   ('0' + (val.getMonth() + 1)).slice(-2),
30372                   ('0' + val.getDate()).slice(-2)
30373                 ].join("-");
30374               },
30375               decode: function (val) {
30376                 if (this.is(val)) return val;
30377                 var match = this.capture.exec(val);
30378                 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
30379               },
30380               is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
30381               equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
30382               pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
30383               capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
30384             },
30385             json: {
30386               encode: angular.toJson,
30387               decode: angular.fromJson,
30388               is: angular.isObject,
30389               equals: angular.equals,
30390               pattern: /[^/]*/
30391             },
30392             any: { // does not encode/decode
30393               encode: angular.identity,
30394               decode: angular.identity,
30395               equals: angular.equals,
30396               pattern: /.*/
30397             }
30398           };
30399
30400           function getDefaultConfig() {
30401             return {
30402               strict: isStrictMode,
30403               caseInsensitive: isCaseInsensitive
30404             };
30405           }
30406
30407           function isInjectable(value) {
30408             return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
30409           }
30410
30411           /**
30412            * [Internal] Get the default value of a parameter, which may be an injectable function.
30413            */
30414           $UrlMatcherFactory.$$getDefaultValue = function(config) {
30415             if (!isInjectable(config.value)) return config.value;
30416             if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30417             return injector.invoke(config.value);
30418           };
30419
30420           /**
30421            * @ngdoc function
30422            * @name ui.router.util.$urlMatcherFactory#caseInsensitive
30423            * @methodOf ui.router.util.$urlMatcherFactory
30424            *
30425            * @description
30426            * Defines whether URL matching should be case sensitive (the default behavior), or not.
30427            *
30428            * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
30429            * @returns {boolean} the current value of caseInsensitive
30430            */
30431           this.caseInsensitive = function(value) {
30432             if (isDefined(value))
30433               isCaseInsensitive = value;
30434             return isCaseInsensitive;
30435           };
30436
30437           /**
30438            * @ngdoc function
30439            * @name ui.router.util.$urlMatcherFactory#strictMode
30440            * @methodOf ui.router.util.$urlMatcherFactory
30441            *
30442            * @description
30443            * Defines whether URLs should match trailing slashes, or not (the default behavior).
30444            *
30445            * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
30446            * @returns {boolean} the current value of strictMode
30447            */
30448           this.strictMode = function(value) {
30449             if (isDefined(value))
30450               isStrictMode = value;
30451             return isStrictMode;
30452           };
30453
30454           /**
30455            * @ngdoc function
30456            * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
30457            * @methodOf ui.router.util.$urlMatcherFactory
30458            *
30459            * @description
30460            * Sets the default behavior when generating or matching URLs with default parameter values.
30461            *
30462            * @param {string} value A string that defines the default parameter URL squashing behavior.
30463            *    `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
30464            *    `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
30465            *             parameter is surrounded by slashes, squash (remove) one slash from the URL
30466            *    any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
30467            *             the parameter value from the URL and replace it with this string.
30468            */
30469           this.defaultSquashPolicy = function(value) {
30470             if (!isDefined(value)) return defaultSquashPolicy;
30471             if (value !== true && value !== false && !isString(value))
30472               throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
30473             defaultSquashPolicy = value;
30474             return value;
30475           };
30476
30477           /**
30478            * @ngdoc function
30479            * @name ui.router.util.$urlMatcherFactory#compile
30480            * @methodOf ui.router.util.$urlMatcherFactory
30481            *
30482            * @description
30483            * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
30484            *
30485            * @param {string} pattern  The URL pattern.
30486            * @param {Object} config  The config object hash.
30487            * @returns {UrlMatcher}  The UrlMatcher.
30488            */
30489           this.compile = function (pattern, config) {
30490             return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
30491           };
30492
30493           /**
30494            * @ngdoc function
30495            * @name ui.router.util.$urlMatcherFactory#isMatcher
30496            * @methodOf ui.router.util.$urlMatcherFactory
30497            *
30498            * @description
30499            * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
30500            *
30501            * @param {Object} object  The object to perform the type check against.
30502            * @returns {Boolean}  Returns `true` if the object matches the `UrlMatcher` interface, by
30503            *          implementing all the same methods.
30504            */
30505           this.isMatcher = function (o) {
30506             if (!isObject(o)) return false;
30507             var result = true;
30508
30509             forEach(UrlMatcher.prototype, function(val, name) {
30510               if (isFunction(val)) {
30511                 result = result && (isDefined(o[name]) && isFunction(o[name]));
30512               }
30513             });
30514             return result;
30515           };
30516
30517           /**
30518            * @ngdoc function
30519            * @name ui.router.util.$urlMatcherFactory#type
30520            * @methodOf ui.router.util.$urlMatcherFactory
30521            *
30522            * @description
30523            * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
30524            * generate URLs with typed parameters.
30525            *
30526            * @param {string} name  The type name.
30527            * @param {Object|Function} definition   The type definition. See
30528            *        {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30529            * @param {Object|Function} definitionFn (optional) A function that is injected before the app
30530            *        runtime starts.  The result of this function is merged into the existing `definition`.
30531            *        See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30532            *
30533            * @returns {Object}  Returns `$urlMatcherFactoryProvider`.
30534            *
30535            * @example
30536            * This is a simple example of a custom type that encodes and decodes items from an
30537            * array, using the array index as the URL-encoded value:
30538            *
30539            * <pre>
30540            * var list = ['John', 'Paul', 'George', 'Ringo'];
30541            *
30542            * $urlMatcherFactoryProvider.type('listItem', {
30543            *   encode: function(item) {
30544            *     // Represent the list item in the URL using its corresponding index
30545            *     return list.indexOf(item);
30546            *   },
30547            *   decode: function(item) {
30548            *     // Look up the list item by index
30549            *     return list[parseInt(item, 10)];
30550            *   },
30551            *   is: function(item) {
30552            *     // Ensure the item is valid by checking to see that it appears
30553            *     // in the list
30554            *     return list.indexOf(item) > -1;
30555            *   }
30556            * });
30557            *
30558            * $stateProvider.state('list', {
30559            *   url: "/list/{item:listItem}",
30560            *   controller: function($scope, $stateParams) {
30561            *     console.log($stateParams.item);
30562            *   }
30563            * });
30564            *
30565            * // ...
30566            *
30567            * // Changes URL to '/list/3', logs "Ringo" to the console
30568            * $state.go('list', { item: "Ringo" });
30569            * </pre>
30570            *
30571            * This is a more complex example of a type that relies on dependency injection to
30572            * interact with services, and uses the parameter name from the URL to infer how to
30573            * handle encoding and decoding parameter values:
30574            *
30575            * <pre>
30576            * // Defines a custom type that gets a value from a service,
30577            * // where each service gets different types of values from
30578            * // a backend API:
30579            * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
30580            *
30581            *   // Matches up services to URL parameter names
30582            *   var services = {
30583            *     user: Users,
30584            *     post: Posts
30585            *   };
30586            *
30587            *   return {
30588            *     encode: function(object) {
30589            *       // Represent the object in the URL using its unique ID
30590            *       return object.id;
30591            *     },
30592            *     decode: function(value, key) {
30593            *       // Look up the object by ID, using the parameter
30594            *       // name (key) to call the correct service
30595            *       return services[key].findById(value);
30596            *     },
30597            *     is: function(object, key) {
30598            *       // Check that object is a valid dbObject
30599            *       return angular.isObject(object) && object.id && services[key];
30600            *     }
30601            *     equals: function(a, b) {
30602            *       // Check the equality of decoded objects by comparing
30603            *       // their unique IDs
30604            *       return a.id === b.id;
30605            *     }
30606            *   };
30607            * });
30608            *
30609            * // In a config() block, you can then attach URLs with
30610            * // type-annotated parameters:
30611            * $stateProvider.state('users', {
30612            *   url: "/users",
30613            *   // ...
30614            * }).state('users.item', {
30615            *   url: "/{user:dbObject}",
30616            *   controller: function($scope, $stateParams) {
30617            *     // $stateParams.user will now be an object returned from
30618            *     // the Users service
30619            *   },
30620            *   // ...
30621            * });
30622            * </pre>
30623            */
30624           this.type = function (name, definition, definitionFn) {
30625             if (!isDefined(definition)) return $types[name];
30626             if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
30627
30628             $types[name] = new Type(extend({ name: name }, definition));
30629             if (definitionFn) {
30630               typeQueue.push({ name: name, def: definitionFn });
30631               if (!enqueue) flushTypeQueue();
30632             }
30633             return this;
30634           };
30635
30636           // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
30637           function flushTypeQueue() {
30638             while(typeQueue.length) {
30639               var type = typeQueue.shift();
30640               if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
30641               angular.extend($types[type.name], injector.invoke(type.def));
30642             }
30643           }
30644
30645           // Register default types. Store them in the prototype of $types.
30646           forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
30647           $types = inherit($types, {});
30648
30649           /* No need to document $get, since it returns this */
30650           this.$get = ['$injector', function ($injector) {
30651             injector = $injector;
30652             enqueue = false;
30653             flushTypeQueue();
30654
30655             forEach(defaultTypes, function(type, name) {
30656               if (!$types[name]) $types[name] = new Type(type);
30657             });
30658             return this;
30659           }];
30660
30661           this.Param = function Param(id, type, config, location) {
30662             var self = this;
30663             config = unwrapShorthand(config);
30664             type = getType(config, type, location);
30665             var arrayMode = getArrayMode();
30666             type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
30667             if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
30668               config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
30669             var isOptional = config.value !== undefined;
30670             var squash = getSquashPolicy(config, isOptional);
30671             var replace = getReplace(config, arrayMode, isOptional, squash);
30672
30673             function unwrapShorthand(config) {
30674               var keys = isObject(config) ? objectKeys(config) : [];
30675               var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
30676                                 indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
30677               if (isShorthand) config = { value: config };
30678               config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
30679               return config;
30680             }
30681
30682             function getType(config, urlType, location) {
30683               if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
30684               if (urlType) return urlType;
30685               if (!config.type) return (location === "config" ? $types.any : $types.string);
30686               return config.type instanceof Type ? config.type : new Type(config.type);
30687             }
30688
30689             // array config: param name (param[]) overrides default settings.  explicit config overrides param name.
30690             function getArrayMode() {
30691               var arrayDefaults = { array: (location === "search" ? "auto" : false) };
30692               var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
30693               return extend(arrayDefaults, arrayParamNomenclature, config).array;
30694             }
30695
30696             /**
30697              * returns false, true, or the squash value to indicate the "default parameter url squash policy".
30698              */
30699             function getSquashPolicy(config, isOptional) {
30700               var squash = config.squash;
30701               if (!isOptional || squash === false) return false;
30702               if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
30703               if (squash === true || isString(squash)) return squash;
30704               throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
30705             }
30706
30707             function getReplace(config, arrayMode, isOptional, squash) {
30708               var replace, configuredKeys, defaultPolicy = [
30709                 { from: "",   to: (isOptional || arrayMode ? undefined : "") },
30710                 { from: null, to: (isOptional || arrayMode ? undefined : "") }
30711               ];
30712               replace = isArray(config.replace) ? config.replace : [];
30713               if (isString(squash))
30714                 replace.push({ from: squash, to: undefined });
30715               configuredKeys = map(replace, function(item) { return item.from; } );
30716               return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
30717             }
30718
30719             /**
30720              * [Internal] Get the default value of a parameter, which may be an injectable function.
30721              */
30722             function $$getDefaultValue() {
30723               if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30724               var defaultValue = injector.invoke(config.$$fn);
30725               if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
30726                 throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
30727               return defaultValue;
30728             }
30729
30730             /**
30731              * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
30732              * default value, which may be the result of an injectable function.
30733              */
30734             function $value(value) {
30735               function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
30736               function $replace(value) {
30737                 var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
30738                 return replacement.length ? replacement[0] : value;
30739               }
30740               value = $replace(value);
30741               return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
30742             }
30743
30744             function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
30745
30746             extend(this, {
30747               id: id,
30748               type: type,
30749               location: location,
30750               array: arrayMode,
30751               squash: squash,
30752               replace: replace,
30753               isOptional: isOptional,
30754               value: $value,
30755               dynamic: undefined,
30756               config: config,
30757               toString: toString
30758             });
30759           };
30760
30761           function ParamSet(params) {
30762             extend(this, params || {});
30763           }
30764
30765           ParamSet.prototype = {
30766             $$new: function() {
30767               return inherit(this, extend(new ParamSet(), { $$parent: this}));
30768             },
30769             $$keys: function () {
30770               var keys = [], chain = [], parent = this,
30771                 ignore = objectKeys(ParamSet.prototype);
30772               while (parent) { chain.push(parent); parent = parent.$$parent; }
30773               chain.reverse();
30774               forEach(chain, function(paramset) {
30775                 forEach(objectKeys(paramset), function(key) {
30776                     if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
30777                 });
30778               });
30779               return keys;
30780             },
30781             $$values: function(paramValues) {
30782               var values = {}, self = this;
30783               forEach(self.$$keys(), function(key) {
30784                 values[key] = self[key].value(paramValues && paramValues[key]);
30785               });
30786               return values;
30787             },
30788             $$equals: function(paramValues1, paramValues2) {
30789               var equal = true, self = this;
30790               forEach(self.$$keys(), function(key) {
30791                 var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
30792                 if (!self[key].type.equals(left, right)) equal = false;
30793               });
30794               return equal;
30795             },
30796             $$validates: function $$validate(paramValues) {
30797               var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
30798               for (i = 0; i < keys.length; i++) {
30799                 param = this[keys[i]];
30800                 rawVal = paramValues[keys[i]];
30801                 if ((rawVal === undefined || rawVal === null) && param.isOptional)
30802                   break; // There was no parameter value, but the param is optional
30803                 normalized = param.type.$normalize(rawVal);
30804                 if (!param.type.is(normalized))
30805                   return false; // The value was not of the correct Type, and could not be decoded to the correct Type
30806                 encoded = param.type.encode(normalized);
30807                 if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
30808                   return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
30809               }
30810               return true;
30811             },
30812             $$parent: undefined
30813           };
30814
30815           this.ParamSet = ParamSet;
30816         }
30817
30818         // Register as a provider so it's available to other providers
30819         angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
30820         angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
30821
30822         /**
30823          * @ngdoc object
30824          * @name ui.router.router.$urlRouterProvider
30825          *
30826          * @requires ui.router.util.$urlMatcherFactoryProvider
30827          * @requires $locationProvider
30828          *
30829          * @description
30830          * `$urlRouterProvider` has the responsibility of watching `$location`. 
30831          * When `$location` changes it runs through a list of rules one by one until a 
30832          * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 
30833          * a url in a state configuration. All urls are compiled into a UrlMatcher object.
30834          *
30835          * There are several methods on `$urlRouterProvider` that make it useful to use directly
30836          * in your module config.
30837          */
30838         $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
30839         function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
30840           var rules = [], otherwise = null, interceptDeferred = false, listener;
30841
30842           // Returns a string that is a prefix of all strings matching the RegExp
30843           function regExpPrefix(re) {
30844             var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
30845             return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
30846           }
30847
30848           // Interpolates matched values into a String.replace()-style pattern
30849           function interpolate(pattern, match) {
30850             return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30851               return match[what === '$' ? 0 : Number(what)];
30852             });
30853           }
30854
30855           /**
30856            * @ngdoc function
30857            * @name ui.router.router.$urlRouterProvider#rule
30858            * @methodOf ui.router.router.$urlRouterProvider
30859            *
30860            * @description
30861            * Defines rules that are used by `$urlRouterProvider` to find matches for
30862            * specific URLs.
30863            *
30864            * @example
30865            * <pre>
30866            * var app = angular.module('app', ['ui.router.router']);
30867            *
30868            * app.config(function ($urlRouterProvider) {
30869            *   // Here's an example of how you might allow case insensitive urls
30870            *   $urlRouterProvider.rule(function ($injector, $location) {
30871            *     var path = $location.path(),
30872            *         normalized = path.toLowerCase();
30873            *
30874            *     if (path !== normalized) {
30875            *       return normalized;
30876            *     }
30877            *   });
30878            * });
30879            * </pre>
30880            *
30881            * @param {object} rule Handler function that takes `$injector` and `$location`
30882            * services as arguments. You can use them to return a valid path as a string.
30883            *
30884            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30885            */
30886           this.rule = function (rule) {
30887             if (!isFunction(rule)) throw new Error("'rule' must be a function");
30888             rules.push(rule);
30889             return this;
30890           };
30891
30892           /**
30893            * @ngdoc object
30894            * @name ui.router.router.$urlRouterProvider#otherwise
30895            * @methodOf ui.router.router.$urlRouterProvider
30896            *
30897            * @description
30898            * Defines a path that is used when an invalid route is requested.
30899            *
30900            * @example
30901            * <pre>
30902            * var app = angular.module('app', ['ui.router.router']);
30903            *
30904            * app.config(function ($urlRouterProvider) {
30905            *   // if the path doesn't match any of the urls you configured
30906            *   // otherwise will take care of routing the user to the
30907            *   // specified url
30908            *   $urlRouterProvider.otherwise('/index');
30909            *
30910            *   // Example of using function rule as param
30911            *   $urlRouterProvider.otherwise(function ($injector, $location) {
30912            *     return '/a/valid/url';
30913            *   });
30914            * });
30915            * </pre>
30916            *
30917            * @param {string|object} rule The url path you want to redirect to or a function 
30918            * rule that returns the url path. The function version is passed two params: 
30919            * `$injector` and `$location` services, and must return a url string.
30920            *
30921            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30922            */
30923           this.otherwise = function (rule) {
30924             if (isString(rule)) {
30925               var redirect = rule;
30926               rule = function () { return redirect; };
30927             }
30928             else if (!isFunction(rule)) throw new Error("'rule' must be a function");
30929             otherwise = rule;
30930             return this;
30931           };
30932
30933
30934           function handleIfMatch($injector, handler, match) {
30935             if (!match) return false;
30936             var result = $injector.invoke(handler, handler, { $match: match });
30937             return isDefined(result) ? result : true;
30938           }
30939
30940           /**
30941            * @ngdoc function
30942            * @name ui.router.router.$urlRouterProvider#when
30943            * @methodOf ui.router.router.$urlRouterProvider
30944            *
30945            * @description
30946            * Registers a handler for a given url matching. if handle is a string, it is
30947            * treated as a redirect, and is interpolated according to the syntax of match
30948            * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
30949            *
30950            * If the handler is a function, it is injectable. It gets invoked if `$location`
30951            * matches. You have the option of inject the match object as `$match`.
30952            *
30953            * The handler can return
30954            *
30955            * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
30956            *   will continue trying to find another one that matches.
30957            * - **string** which is treated as a redirect and passed to `$location.url()`
30958            * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
30959            *
30960            * @example
30961            * <pre>
30962            * var app = angular.module('app', ['ui.router.router']);
30963            *
30964            * app.config(function ($urlRouterProvider) {
30965            *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
30966            *     if ($state.$current.navigable !== state ||
30967            *         !equalForKeys($match, $stateParams) {
30968            *      $state.transitionTo(state, $match, false);
30969            *     }
30970            *   });
30971            * });
30972            * </pre>
30973            *
30974            * @param {string|object} what The incoming path that you want to redirect.
30975            * @param {string|object} handler The path you want to redirect your user to.
30976            */
30977           this.when = function (what, handler) {
30978             var redirect, handlerIsString = isString(handler);
30979             if (isString(what)) what = $urlMatcherFactory.compile(what);
30980
30981             if (!handlerIsString && !isFunction(handler) && !isArray(handler))
30982               throw new Error("invalid 'handler' in when()");
30983
30984             var strategies = {
30985               matcher: function (what, handler) {
30986                 if (handlerIsString) {
30987                   redirect = $urlMatcherFactory.compile(handler);
30988                   handler = ['$match', function ($match) { return redirect.format($match); }];
30989                 }
30990                 return extend(function ($injector, $location) {
30991                   return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
30992                 }, {
30993                   prefix: isString(what.prefix) ? what.prefix : ''
30994                 });
30995               },
30996               regex: function (what, handler) {
30997                 if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
30998
30999                 if (handlerIsString) {
31000                   redirect = handler;
31001                   handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
31002                 }
31003                 return extend(function ($injector, $location) {
31004                   return handleIfMatch($injector, handler, what.exec($location.path()));
31005                 }, {
31006                   prefix: regExpPrefix(what)
31007                 });
31008               }
31009             };
31010
31011             var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
31012
31013             for (var n in check) {
31014               if (check[n]) return this.rule(strategies[n](what, handler));
31015             }
31016
31017             throw new Error("invalid 'what' in when()");
31018           };
31019
31020           /**
31021            * @ngdoc function
31022            * @name ui.router.router.$urlRouterProvider#deferIntercept
31023            * @methodOf ui.router.router.$urlRouterProvider
31024            *
31025            * @description
31026            * Disables (or enables) deferring location change interception.
31027            *
31028            * If you wish to customize the behavior of syncing the URL (for example, if you wish to
31029            * defer a transition but maintain the current URL), call this method at configuration time.
31030            * Then, at run time, call `$urlRouter.listen()` after you have configured your own
31031            * `$locationChangeSuccess` event handler.
31032            *
31033            * @example
31034            * <pre>
31035            * var app = angular.module('app', ['ui.router.router']);
31036            *
31037            * app.config(function ($urlRouterProvider) {
31038            *
31039            *   // Prevent $urlRouter from automatically intercepting URL changes;
31040            *   // this allows you to configure custom behavior in between
31041            *   // location changes and route synchronization:
31042            *   $urlRouterProvider.deferIntercept();
31043            *
31044            * }).run(function ($rootScope, $urlRouter, UserService) {
31045            *
31046            *   $rootScope.$on('$locationChangeSuccess', function(e) {
31047            *     // UserService is an example service for managing user state
31048            *     if (UserService.isLoggedIn()) return;
31049            *
31050            *     // Prevent $urlRouter's default handler from firing
31051            *     e.preventDefault();
31052            *
31053            *     UserService.handleLogin().then(function() {
31054            *       // Once the user has logged in, sync the current URL
31055            *       // to the router:
31056            *       $urlRouter.sync();
31057            *     });
31058            *   });
31059            *
31060            *   // Configures $urlRouter's listener *after* your custom listener
31061            *   $urlRouter.listen();
31062            * });
31063            * </pre>
31064            *
31065            * @param {boolean} defer Indicates whether to defer location change interception. Passing
31066                     no parameter is equivalent to `true`.
31067            */
31068           this.deferIntercept = function (defer) {
31069             if (defer === undefined) defer = true;
31070             interceptDeferred = defer;
31071           };
31072
31073           /**
31074            * @ngdoc object
31075            * @name ui.router.router.$urlRouter
31076            *
31077            * @requires $location
31078            * @requires $rootScope
31079            * @requires $injector
31080            * @requires $browser
31081            *
31082            * @description
31083            *
31084            */
31085           this.$get = $get;
31086           $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
31087           function $get(   $location,   $rootScope,   $injector,   $browser) {
31088
31089             var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
31090
31091             function appendBasePath(url, isHtml5, absolute) {
31092               if (baseHref === '/') return url;
31093               if (isHtml5) return baseHref.slice(0, -1) + url;
31094               if (absolute) return baseHref.slice(1) + url;
31095               return url;
31096             }
31097
31098             // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
31099             function update(evt) {
31100               if (evt && evt.defaultPrevented) return;
31101               var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
31102               lastPushedUrl = undefined;
31103               // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
31104               //if (ignoreUpdate) return true;
31105
31106               function check(rule) {
31107                 var handled = rule($injector, $location);
31108
31109                 if (!handled) return false;
31110                 if (isString(handled)) $location.replace().url(handled);
31111                 return true;
31112               }
31113               var n = rules.length, i;
31114
31115               for (i = 0; i < n; i++) {
31116                 if (check(rules[i])) return;
31117               }
31118               // always check otherwise last to allow dynamic updates to the set of rules
31119               if (otherwise) check(otherwise);
31120             }
31121
31122             function listen() {
31123               listener = listener || $rootScope.$on('$locationChangeSuccess', update);
31124               return listener;
31125             }
31126
31127             if (!interceptDeferred) listen();
31128
31129             return {
31130               /**
31131                * @ngdoc function
31132                * @name ui.router.router.$urlRouter#sync
31133                * @methodOf ui.router.router.$urlRouter
31134                *
31135                * @description
31136                * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
31137                * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
31138                * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
31139                * with the transition by calling `$urlRouter.sync()`.
31140                *
31141                * @example
31142                * <pre>
31143                * angular.module('app', ['ui.router'])
31144                *   .run(function($rootScope, $urlRouter) {
31145                *     $rootScope.$on('$locationChangeSuccess', function(evt) {
31146                *       // Halt state change from even starting
31147                *       evt.preventDefault();
31148                *       // Perform custom logic
31149                *       var meetsRequirement = ...
31150                *       // Continue with the update and state transition if logic allows
31151                *       if (meetsRequirement) $urlRouter.sync();
31152                *     });
31153                * });
31154                * </pre>
31155                */
31156               sync: function() {
31157                 update();
31158               },
31159
31160               listen: function() {
31161                 return listen();
31162               },
31163
31164               update: function(read) {
31165                 if (read) {
31166                   location = $location.url();
31167                   return;
31168                 }
31169                 if ($location.url() === location) return;
31170
31171                 $location.url(location);
31172                 $location.replace();
31173               },
31174
31175               push: function(urlMatcher, params, options) {
31176                  var url = urlMatcher.format(params || {});
31177
31178                 // Handle the special hash param, if needed
31179                 if (url !== null && params && params['#']) {
31180                     url += '#' + params['#'];
31181                 }
31182
31183                 $location.url(url);
31184                 lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
31185                 if (options && options.replace) $location.replace();
31186               },
31187
31188               /**
31189                * @ngdoc function
31190                * @name ui.router.router.$urlRouter#href
31191                * @methodOf ui.router.router.$urlRouter
31192                *
31193                * @description
31194                * A URL generation method that returns the compiled URL for a given
31195                * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
31196                *
31197                * @example
31198                * <pre>
31199                * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
31200                *   person: "bob"
31201                * });
31202                * // $bob == "/about/bob";
31203                * </pre>
31204                *
31205                * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
31206                * @param {object=} params An object of parameter values to fill the matcher's required parameters.
31207                * @param {object=} options Options object. The options are:
31208                *
31209                * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
31210                *
31211                * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
31212                */
31213               href: function(urlMatcher, params, options) {
31214                 if (!urlMatcher.validates(params)) return null;
31215
31216                 var isHtml5 = $locationProvider.html5Mode();
31217                 if (angular.isObject(isHtml5)) {
31218                   isHtml5 = isHtml5.enabled;
31219                 }
31220                 
31221                 var url = urlMatcher.format(params);
31222                 options = options || {};
31223
31224                 if (!isHtml5 && url !== null) {
31225                   url = "#" + $locationProvider.hashPrefix() + url;
31226                 }
31227
31228                 // Handle special hash param, if needed
31229                 if (url !== null && params && params['#']) {
31230                   url += '#' + params['#'];
31231                 }
31232
31233                 url = appendBasePath(url, isHtml5, options.absolute);
31234
31235                 if (!options.absolute || !url) {
31236                   return url;
31237                 }
31238
31239                 var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
31240                 port = (port === 80 || port === 443 ? '' : ':' + port);
31241
31242                 return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
31243               }
31244             };
31245           }
31246         }
31247
31248         angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
31249
31250         /**
31251          * @ngdoc object
31252          * @name ui.router.state.$stateProvider
31253          *
31254          * @requires ui.router.router.$urlRouterProvider
31255          * @requires ui.router.util.$urlMatcherFactoryProvider
31256          *
31257          * @description
31258          * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
31259          * on state.
31260          *
31261          * A state corresponds to a "place" in the application in terms of the overall UI and
31262          * navigation. A state describes (via the controller / template / view properties) what
31263          * the UI looks like and does at that place.
31264          *
31265          * States often have things in common, and the primary way of factoring out these
31266          * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
31267          * nested states.
31268          *
31269          * The `$stateProvider` provides interfaces to declare these states for your app.
31270          */
31271         $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
31272         function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
31273
31274           var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
31275
31276           // Builds state properties from definition passed to registerState()
31277           var stateBuilder = {
31278
31279             // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31280             // state.children = [];
31281             // if (parent) parent.children.push(state);
31282             parent: function(state) {
31283               if (isDefined(state.parent) && state.parent) return findState(state.parent);
31284               // regex matches any valid composite state name
31285               // would match "contact.list" but not "contacts"
31286               var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
31287               return compositeName ? findState(compositeName[1]) : root;
31288             },
31289
31290             // inherit 'data' from parent and override by own values (if any)
31291             data: function(state) {
31292               if (state.parent && state.parent.data) {
31293                 state.data = state.self.data = extend({}, state.parent.data, state.data);
31294               }
31295               return state.data;
31296             },
31297
31298             // Build a URLMatcher if necessary, either via a relative or absolute URL
31299             url: function(state) {
31300               var url = state.url, config = { params: state.params || {} };
31301
31302               if (isString(url)) {
31303                 if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
31304                 return (state.parent.navigable || root).url.concat(url, config);
31305               }
31306
31307               if (!url || $urlMatcherFactory.isMatcher(url)) return url;
31308               throw new Error("Invalid url '" + url + "' in state '" + state + "'");
31309             },
31310
31311             // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
31312             navigable: function(state) {
31313               return state.url ? state : (state.parent ? state.parent.navigable : null);
31314             },
31315
31316             // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
31317             ownParams: function(state) {
31318               var params = state.url && state.url.params || new $$UMFP.ParamSet();
31319               forEach(state.params || {}, function(config, id) {
31320                 if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
31321               });
31322               return params;
31323             },
31324
31325             // Derive parameters for this state and ensure they're a super-set of parent's parameters
31326             params: function(state) {
31327               return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
31328             },
31329
31330             // If there is no explicit multi-view configuration, make one up so we don't have
31331             // to handle both cases in the view directive later. Note that having an explicit
31332             // 'views' property will mean the default unnamed view properties are ignored. This
31333             // is also a good time to resolve view names to absolute names, so everything is a
31334             // straight lookup at link time.
31335             views: function(state) {
31336               var views = {};
31337
31338               forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
31339                 if (name.indexOf('@') < 0) name += '@' + state.parent.name;
31340                 views[name] = view;
31341               });
31342               return views;
31343             },
31344
31345             // Keep a full path from the root down to this state as this is needed for state activation.
31346             path: function(state) {
31347               return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
31348             },
31349
31350             // Speed up $state.contains() as it's used a lot
31351             includes: function(state) {
31352               var includes = state.parent ? extend({}, state.parent.includes) : {};
31353               includes[state.name] = true;
31354               return includes;
31355             },
31356
31357             $delegates: {}
31358           };
31359
31360           function isRelative(stateName) {
31361             return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
31362           }
31363
31364           function findState(stateOrName, base) {
31365             if (!stateOrName) return undefined;
31366
31367             var isStr = isString(stateOrName),
31368                 name  = isStr ? stateOrName : stateOrName.name,
31369                 path  = isRelative(name);
31370
31371             if (path) {
31372               if (!base) throw new Error("No reference point given for path '"  + name + "'");
31373               base = findState(base);
31374               
31375               var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
31376
31377               for (; i < pathLength; i++) {
31378                 if (rel[i] === "" && i === 0) {
31379                   current = base;
31380                   continue;
31381                 }
31382                 if (rel[i] === "^") {
31383                   if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
31384                   current = current.parent;
31385                   continue;
31386                 }
31387                 break;
31388               }
31389               rel = rel.slice(i).join(".");
31390               name = current.name + (current.name && rel ? "." : "") + rel;
31391             }
31392             var state = states[name];
31393
31394             if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
31395               return state;
31396             }
31397             return undefined;
31398           }
31399
31400           function queueState(parentName, state) {
31401             if (!queue[parentName]) {
31402               queue[parentName] = [];
31403             }
31404             queue[parentName].push(state);
31405           }
31406
31407           function flushQueuedChildren(parentName) {
31408             var queued = queue[parentName] || [];
31409             while(queued.length) {
31410               registerState(queued.shift());
31411             }
31412           }
31413
31414           function registerState(state) {
31415             // Wrap a new object around the state so we can store our private details easily.
31416             state = inherit(state, {
31417               self: state,
31418               resolve: state.resolve || {},
31419               toString: function() { return this.name; }
31420             });
31421
31422             var name = state.name;
31423             if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
31424             if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
31425
31426             // Get parent name
31427             var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
31428                 : (isString(state.parent)) ? state.parent
31429                 : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
31430                 : '';
31431
31432             // If parent is not registered yet, add state to queue and register later
31433             if (parentName && !states[parentName]) {
31434               return queueState(parentName, state.self);
31435             }
31436
31437             for (var key in stateBuilder) {
31438               if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
31439             }
31440             states[name] = state;
31441
31442             // Register the state in the global state list and with $urlRouter if necessary.
31443             if (!state[abstractKey] && state.url) {
31444               $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
31445                 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
31446                   $state.transitionTo(state, $match, { inherit: true, location: false });
31447                 }
31448               }]);
31449             }
31450
31451             // Register any queued children
31452             flushQueuedChildren(name);
31453
31454             return state;
31455           }
31456
31457           // Checks text to see if it looks like a glob.
31458           function isGlob (text) {
31459             return text.indexOf('*') > -1;
31460           }
31461
31462           // Returns true if glob matches current $state name.
31463           function doesStateMatchGlob (glob) {
31464             var globSegments = glob.split('.'),
31465                 segments = $state.$current.name.split('.');
31466
31467             //match single stars
31468             for (var i = 0, l = globSegments.length; i < l; i++) {
31469               if (globSegments[i] === '*') {
31470                 segments[i] = '*';
31471               }
31472             }
31473
31474             //match greedy starts
31475             if (globSegments[0] === '**') {
31476                segments = segments.slice(indexOf(segments, globSegments[1]));
31477                segments.unshift('**');
31478             }
31479             //match greedy ends
31480             if (globSegments[globSegments.length - 1] === '**') {
31481                segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
31482                segments.push('**');
31483             }
31484
31485             if (globSegments.length != segments.length) {
31486               return false;
31487             }
31488
31489             return segments.join('') === globSegments.join('');
31490           }
31491
31492
31493           // Implicit root state that is always active
31494           root = registerState({
31495             name: '',
31496             url: '^',
31497             views: null,
31498             'abstract': true
31499           });
31500           root.navigable = null;
31501
31502
31503           /**
31504            * @ngdoc function
31505            * @name ui.router.state.$stateProvider#decorator
31506            * @methodOf ui.router.state.$stateProvider
31507            *
31508            * @description
31509            * Allows you to extend (carefully) or override (at your own peril) the 
31510            * `stateBuilder` object used internally by `$stateProvider`. This can be used 
31511            * to add custom functionality to ui-router, for example inferring templateUrl 
31512            * based on the state name.
31513            *
31514            * When passing only a name, it returns the current (original or decorated) builder
31515            * function that matches `name`.
31516            *
31517            * The builder functions that can be decorated are listed below. Though not all
31518            * necessarily have a good use case for decoration, that is up to you to decide.
31519            *
31520            * In addition, users can attach custom decorators, which will generate new 
31521            * properties within the state's internal definition. There is currently no clear 
31522            * use-case for this beyond accessing internal states (i.e. $state.$current), 
31523            * however, expect this to become increasingly relevant as we introduce additional 
31524            * meta-programming features.
31525            *
31526            * **Warning**: Decorators should not be interdependent because the order of 
31527            * execution of the builder functions in non-deterministic. Builder functions 
31528            * should only be dependent on the state definition object and super function.
31529            *
31530            *
31531            * Existing builder functions and current return values:
31532            *
31533            * - **parent** `{object}` - returns the parent state object.
31534            * - **data** `{object}` - returns state data, including any inherited data that is not
31535            *   overridden by own values (if any).
31536            * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
31537            *   or `null`.
31538            * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is 
31539            *   navigable).
31540            * - **params** `{object}` - returns an array of state params that are ensured to 
31541            *   be a super-set of parent's params.
31542            * - **views** `{object}` - returns a views object where each key is an absolute view 
31543            *   name (i.e. "viewName@stateName") and each value is the config object 
31544            *   (template, controller) for the view. Even when you don't use the views object 
31545            *   explicitly on a state config, one is still created for you internally.
31546            *   So by decorating this builder function you have access to decorating template 
31547            *   and controller properties.
31548            * - **ownParams** `{object}` - returns an array of params that belong to the state, 
31549            *   not including any params defined by ancestor states.
31550            * - **path** `{string}` - returns the full path from the root down to this state. 
31551            *   Needed for state activation.
31552            * - **includes** `{object}` - returns an object that includes every state that 
31553            *   would pass a `$state.includes()` test.
31554            *
31555            * @example
31556            * <pre>
31557            * // Override the internal 'views' builder with a function that takes the state
31558            * // definition, and a reference to the internal function being overridden:
31559            * $stateProvider.decorator('views', function (state, parent) {
31560            *   var result = {},
31561            *       views = parent(state);
31562            *
31563            *   angular.forEach(views, function (config, name) {
31564            *     var autoName = (state.name + '.' + name).replace('.', '/');
31565            *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
31566            *     result[name] = config;
31567            *   });
31568            *   return result;
31569            * });
31570            *
31571            * $stateProvider.state('home', {
31572            *   views: {
31573            *     'contact.list': { controller: 'ListController' },
31574            *     'contact.item': { controller: 'ItemController' }
31575            *   }
31576            * });
31577            *
31578            * // ...
31579            *
31580            * $state.go('home');
31581            * // Auto-populates list and item views with /partials/home/contact/list.html,
31582            * // and /partials/home/contact/item.html, respectively.
31583            * </pre>
31584            *
31585            * @param {string} name The name of the builder function to decorate. 
31586            * @param {object} func A function that is responsible for decorating the original 
31587            * builder function. The function receives two parameters:
31588            *
31589            *   - `{object}` - state - The state config object.
31590            *   - `{object}` - super - The original builder function.
31591            *
31592            * @return {object} $stateProvider - $stateProvider instance
31593            */
31594           this.decorator = decorator;
31595           function decorator(name, func) {
31596             /*jshint validthis: true */
31597             if (isString(name) && !isDefined(func)) {
31598               return stateBuilder[name];
31599             }
31600             if (!isFunction(func) || !isString(name)) {
31601               return this;
31602             }
31603             if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
31604               stateBuilder.$delegates[name] = stateBuilder[name];
31605             }
31606             stateBuilder[name] = func;
31607             return this;
31608           }
31609
31610           /**
31611            * @ngdoc function
31612            * @name ui.router.state.$stateProvider#state
31613            * @methodOf ui.router.state.$stateProvider
31614            *
31615            * @description
31616            * Registers a state configuration under a given state name. The stateConfig object
31617            * has the following acceptable properties.
31618            *
31619            * @param {string} name A unique state name, e.g. "home", "about", "contacts".
31620            * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
31621            * @param {object} stateConfig State configuration object.
31622            * @param {string|function=} stateConfig.template
31623            * <a id='template'></a>
31624            *   html template as a string or a function that returns
31625            *   an html template as a string which should be used by the uiView directives. This property 
31626            *   takes precedence over templateUrl.
31627            *   
31628            *   If `template` is a function, it will be called with the following parameters:
31629            *
31630            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
31631            *     applying the current state
31632            *
31633            * <pre>template:
31634            *   "<h1>inline template definition</h1>" +
31635            *   "<div ui-view></div>"</pre>
31636            * <pre>template: function(params) {
31637            *       return "<h1>generated template</h1>"; }</pre>
31638            * </div>
31639            *
31640            * @param {string|function=} stateConfig.templateUrl
31641            * <a id='templateUrl'></a>
31642            *
31643            *   path or function that returns a path to an html
31644            *   template that should be used by uiView.
31645            *   
31646            *   If `templateUrl` is a function, it will be called with the following parameters:
31647            *
31648            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by 
31649            *     applying the current state
31650            *
31651            * <pre>templateUrl: "home.html"</pre>
31652            * <pre>templateUrl: function(params) {
31653            *     return myTemplates[params.pageId]; }</pre>
31654            *
31655            * @param {function=} stateConfig.templateProvider
31656            * <a id='templateProvider'></a>
31657            *    Provider function that returns HTML content string.
31658            * <pre> templateProvider:
31659            *       function(MyTemplateService, params) {
31660            *         return MyTemplateService.getTemplate(params.pageId);
31661            *       }</pre>
31662            *
31663            * @param {string|function=} stateConfig.controller
31664            * <a id='controller'></a>
31665            *
31666            *  Controller fn that should be associated with newly
31667            *   related scope or the name of a registered controller if passed as a string.
31668            *   Optionally, the ControllerAs may be declared here.
31669            * <pre>controller: "MyRegisteredController"</pre>
31670            * <pre>controller:
31671            *     "MyRegisteredController as fooCtrl"}</pre>
31672            * <pre>controller: function($scope, MyService) {
31673            *     $scope.data = MyService.getData(); }</pre>
31674            *
31675            * @param {function=} stateConfig.controllerProvider
31676            * <a id='controllerProvider'></a>
31677            *
31678            * Injectable provider function that returns the actual controller or string.
31679            * <pre>controllerProvider:
31680            *   function(MyResolveData) {
31681            *     if (MyResolveData.foo)
31682            *       return "FooCtrl"
31683            *     else if (MyResolveData.bar)
31684            *       return "BarCtrl";
31685            *     else return function($scope) {
31686            *       $scope.baz = "Qux";
31687            *     }
31688            *   }</pre>
31689            *
31690            * @param {string=} stateConfig.controllerAs
31691            * <a id='controllerAs'></a>
31692            * 
31693            * A controller alias name. If present the controller will be
31694            *   published to scope under the controllerAs name.
31695            * <pre>controllerAs: "myCtrl"</pre>
31696            *
31697            * @param {string|object=} stateConfig.parent
31698            * <a id='parent'></a>
31699            * Optionally specifies the parent state of this state.
31700            *
31701            * <pre>parent: 'parentState'</pre>
31702            * <pre>parent: parentState // JS variable</pre>
31703            *
31704            * @param {object=} stateConfig.resolve
31705            * <a id='resolve'></a>
31706            *
31707            * An optional map&lt;string, function&gt; of dependencies which
31708            *   should be injected into the controller. If any of these dependencies are promises, 
31709            *   the router will wait for them all to be resolved before the controller is instantiated.
31710            *   If all the promises are resolved successfully, the $stateChangeSuccess event is fired
31711            *   and the values of the resolved promises are injected into any controllers that reference them.
31712            *   If any  of the promises are rejected the $stateChangeError event is fired.
31713            *
31714            *   The map object is:
31715            *   
31716            *   - key - {string}: name of dependency to be injected into controller
31717            *   - factory - {string|function}: If string then it is alias for service. Otherwise if function, 
31718            *     it is injected and return value it treated as dependency. If result is a promise, it is 
31719            *     resolved before its value is injected into controller.
31720            *
31721            * <pre>resolve: {
31722            *     myResolve1:
31723            *       function($http, $stateParams) {
31724            *         return $http.get("/api/foos/"+stateParams.fooID);
31725            *       }
31726            *     }</pre>
31727            *
31728            * @param {string=} stateConfig.url
31729            * <a id='url'></a>
31730            *
31731            *   A url fragment with optional parameters. When a state is navigated or
31732            *   transitioned to, the `$stateParams` service will be populated with any 
31733            *   parameters that were passed.
31734            *
31735            *   (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
31736            *   more details on acceptable patterns )
31737            *
31738            * examples:
31739            * <pre>url: "/home"
31740            * url: "/users/:userid"
31741            * url: "/books/{bookid:[a-zA-Z_-]}"
31742            * url: "/books/{categoryid:int}"
31743            * url: "/books/{publishername:string}/{categoryid:int}"
31744            * url: "/messages?before&after"
31745            * url: "/messages?{before:date}&{after:date}"
31746            * url: "/messages/:mailboxid?{before:date}&{after:date}"
31747            * </pre>
31748            *
31749            * @param {object=} stateConfig.views
31750            * <a id='views'></a>
31751            * an optional map&lt;string, object&gt; which defined multiple views, or targets views
31752            * manually/explicitly.
31753            *
31754            * Examples:
31755            *
31756            * Targets three named `ui-view`s in the parent state's template
31757            * <pre>views: {
31758            *     header: {
31759            *       controller: "headerCtrl",
31760            *       templateUrl: "header.html"
31761            *     }, body: {
31762            *       controller: "bodyCtrl",
31763            *       templateUrl: "body.html"
31764            *     }, footer: {
31765            *       controller: "footCtrl",
31766            *       templateUrl: "footer.html"
31767            *     }
31768            *   }</pre>
31769            *
31770            * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
31771            * <pre>views: {
31772            *     'header@top': {
31773            *       controller: "msgHeaderCtrl",
31774            *       templateUrl: "msgHeader.html"
31775            *     }, 'body': {
31776            *       controller: "messagesCtrl",
31777            *       templateUrl: "messages.html"
31778            *     }
31779            *   }</pre>
31780            *
31781            * @param {boolean=} [stateConfig.abstract=false]
31782            * <a id='abstract'></a>
31783            * An abstract state will never be directly activated,
31784            *   but can provide inherited properties to its common children states.
31785            * <pre>abstract: true</pre>
31786            *
31787            * @param {function=} stateConfig.onEnter
31788            * <a id='onEnter'></a>
31789            *
31790            * Callback function for when a state is entered. Good way
31791            *   to trigger an action or dispatch an event, such as opening a dialog.
31792            * If minifying your scripts, make sure to explictly annotate this function,
31793            * because it won't be automatically annotated by your build tools.
31794            *
31795            * <pre>onEnter: function(MyService, $stateParams) {
31796            *     MyService.foo($stateParams.myParam);
31797            * }</pre>
31798            *
31799            * @param {function=} stateConfig.onExit
31800            * <a id='onExit'></a>
31801            *
31802            * Callback function for when a state is exited. Good way to
31803            *   trigger an action or dispatch an event, such as opening a dialog.
31804            * If minifying your scripts, make sure to explictly annotate this function,
31805            * because it won't be automatically annotated by your build tools.
31806            *
31807            * <pre>onExit: function(MyService, $stateParams) {
31808            *     MyService.cleanup($stateParams.myParam);
31809            * }</pre>
31810            *
31811            * @param {boolean=} [stateConfig.reloadOnSearch=true]
31812            * <a id='reloadOnSearch'></a>
31813            *
31814            * If `false`, will not retrigger the same state
31815            *   just because a search/query parameter has changed (via $location.search() or $location.hash()). 
31816            *   Useful for when you'd like to modify $location.search() without triggering a reload.
31817            * <pre>reloadOnSearch: false</pre>
31818            *
31819            * @param {object=} stateConfig.data
31820            * <a id='data'></a>
31821            *
31822            * Arbitrary data object, useful for custom configuration.  The parent state's `data` is
31823            *   prototypally inherited.  In other words, adding a data property to a state adds it to
31824            *   the entire subtree via prototypal inheritance.
31825            *
31826            * <pre>data: {
31827            *     requiredRole: 'foo'
31828            * } </pre>
31829            *
31830            * @param {object=} stateConfig.params
31831            * <a id='params'></a>
31832            *
31833            * A map which optionally configures parameters declared in the `url`, or
31834            *   defines additional non-url parameters.  For each parameter being
31835            *   configured, add a configuration object keyed to the name of the parameter.
31836            *
31837            *   Each parameter configuration object may contain the following properties:
31838            *
31839            *   - ** value ** - {object|function=}: specifies the default value for this
31840            *     parameter.  This implicitly sets this parameter as optional.
31841            *
31842            *     When UI-Router routes to a state and no value is
31843            *     specified for this parameter in the URL or transition, the
31844            *     default value will be used instead.  If `value` is a function,
31845            *     it will be injected and invoked, and the return value used.
31846            *
31847            *     *Note*: `undefined` is treated as "no default value" while `null`
31848            *     is treated as "the default value is `null`".
31849            *
31850            *     *Shorthand*: If you only need to configure the default value of the
31851            *     parameter, you may use a shorthand syntax.   In the **`params`**
31852            *     map, instead mapping the param name to a full parameter configuration
31853            *     object, simply set map it to the default parameter value, e.g.:
31854            *
31855            * <pre>// define a parameter's default value
31856            * params: {
31857            *     param1: { value: "defaultValue" }
31858            * }
31859            * // shorthand default values
31860            * params: {
31861            *     param1: "defaultValue",
31862            *     param2: "param2Default"
31863            * }</pre>
31864            *
31865            *   - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
31866            *     treated as an array of values.  If you specified a Type, the value will be
31867            *     treated as an array of the specified Type.  Note: query parameter values
31868            *     default to a special `"auto"` mode.
31869            *
31870            *     For query parameters in `"auto"` mode, if multiple  values for a single parameter
31871            *     are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
31872            *     are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`).  However, if
31873            *     only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
31874            *     value (e.g.: `{ foo: '1' }`).
31875            *
31876            * <pre>params: {
31877            *     param1: { array: true }
31878            * }</pre>
31879            *
31880            *   - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
31881            *     the current parameter value is the same as the default value. If `squash` is not set, it uses the
31882            *     configured default squash policy.
31883            *     (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
31884            *
31885            *   There are three squash settings:
31886            *
31887            *     - false: The parameter's default value is not squashed.  It is encoded and included in the URL
31888            *     - true: The parameter's default value is omitted from the URL.  If the parameter is preceeded and followed
31889            *       by slashes in the state's `url` declaration, then one of those slashes are omitted.
31890            *       This can allow for cleaner looking URLs.
31891            *     - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of  your choice.
31892            *
31893            * <pre>params: {
31894            *     param1: {
31895            *       value: "defaultId",
31896            *       squash: true
31897            * } }
31898            * // squash "defaultValue" to "~"
31899            * params: {
31900            *     param1: {
31901            *       value: "defaultValue",
31902            *       squash: "~"
31903            * } }
31904            * </pre>
31905            *
31906            *
31907            * @example
31908            * <pre>
31909            * // Some state name examples
31910            *
31911            * // stateName can be a single top-level name (must be unique).
31912            * $stateProvider.state("home", {});
31913            *
31914            * // Or it can be a nested state name. This state is a child of the
31915            * // above "home" state.
31916            * $stateProvider.state("home.newest", {});
31917            *
31918            * // Nest states as deeply as needed.
31919            * $stateProvider.state("home.newest.abc.xyz.inception", {});
31920            *
31921            * // state() returns $stateProvider, so you can chain state declarations.
31922            * $stateProvider
31923            *   .state("home", {})
31924            *   .state("about", {})
31925            *   .state("contacts", {});
31926            * </pre>
31927            *
31928            */
31929           this.state = state;
31930           function state(name, definition) {
31931             /*jshint validthis: true */
31932             if (isObject(name)) definition = name;
31933             else definition.name = name;
31934             registerState(definition);
31935             return this;
31936           }
31937
31938           /**
31939            * @ngdoc object
31940            * @name ui.router.state.$state
31941            *
31942            * @requires $rootScope
31943            * @requires $q
31944            * @requires ui.router.state.$view
31945            * @requires $injector
31946            * @requires ui.router.util.$resolve
31947            * @requires ui.router.state.$stateParams
31948            * @requires ui.router.router.$urlRouter
31949            *
31950            * @property {object} params A param object, e.g. {sectionId: section.id)}, that 
31951            * you'd like to test against the current active state.
31952            * @property {object} current A reference to the state's config object. However 
31953            * you passed it in. Useful for accessing custom data.
31954            * @property {object} transition Currently pending transition. A promise that'll 
31955            * resolve or reject.
31956            *
31957            * @description
31958            * `$state` service is responsible for representing states as well as transitioning
31959            * between them. It also provides interfaces to ask for current state or even states
31960            * you're coming from.
31961            */
31962           this.$get = $get;
31963           $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
31964           function $get(   $rootScope,   $q,   $view,   $injector,   $resolve,   $stateParams,   $urlRouter,   $location,   $urlMatcherFactory) {
31965
31966             var TransitionSuperseded = $q.reject(new Error('transition superseded'));
31967             var TransitionPrevented = $q.reject(new Error('transition prevented'));
31968             var TransitionAborted = $q.reject(new Error('transition aborted'));
31969             var TransitionFailed = $q.reject(new Error('transition failed'));
31970
31971             // Handles the case where a state which is the target of a transition is not found, and the user
31972             // can optionally retry or defer the transition
31973             function handleRedirect(redirect, state, params, options) {
31974               /**
31975                * @ngdoc event
31976                * @name ui.router.state.$state#$stateNotFound
31977                * @eventOf ui.router.state.$state
31978                * @eventType broadcast on root scope
31979                * @description
31980                * Fired when a requested state **cannot be found** using the provided state name during transition.
31981                * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
31982                * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
31983                * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
31984                * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
31985                *
31986                * @param {Object} event Event object.
31987                * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
31988                * @param {State} fromState Current state object.
31989                * @param {Object} fromParams Current state params.
31990                *
31991                * @example
31992                *
31993                * <pre>
31994                * // somewhere, assume lazy.state has not been defined
31995                * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
31996                *
31997                * // somewhere else
31998                * $scope.$on('$stateNotFound',
31999                * function(event, unfoundState, fromState, fromParams){
32000                *     console.log(unfoundState.to); // "lazy.state"
32001                *     console.log(unfoundState.toParams); // {a:1, b:2}
32002                *     console.log(unfoundState.options); // {inherit:false} + default options
32003                * })
32004                * </pre>
32005                */
32006               var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
32007
32008               if (evt.defaultPrevented) {
32009                 $urlRouter.update();
32010                 return TransitionAborted;
32011               }
32012
32013               if (!evt.retry) {
32014                 return null;
32015               }
32016
32017               // Allow the handler to return a promise to defer state lookup retry
32018               if (options.$retry) {
32019                 $urlRouter.update();
32020                 return TransitionFailed;
32021               }
32022               var retryTransition = $state.transition = $q.when(evt.retry);
32023
32024               retryTransition.then(function() {
32025                 if (retryTransition !== $state.transition) return TransitionSuperseded;
32026                 redirect.options.$retry = true;
32027                 return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
32028               }, function() {
32029                 return TransitionAborted;
32030               });
32031               $urlRouter.update();
32032
32033               return retryTransition;
32034             }
32035
32036             root.locals = { resolve: null, globals: { $stateParams: {} } };
32037
32038             $state = {
32039               params: {},
32040               current: root.self,
32041               $current: root,
32042               transition: null
32043             };
32044
32045             /**
32046              * @ngdoc function
32047              * @name ui.router.state.$state#reload
32048              * @methodOf ui.router.state.$state
32049              *
32050              * @description
32051              * A method that force reloads the current state. All resolves are re-resolved,
32052              * controllers reinstantiated, and events re-fired.
32053              *
32054              * @example
32055              * <pre>
32056              * var app angular.module('app', ['ui.router']);
32057              *
32058              * app.controller('ctrl', function ($scope, $state) {
32059              *   $scope.reload = function(){
32060              *     $state.reload();
32061              *   }
32062              * });
32063              * </pre>
32064              *
32065              * `reload()` is just an alias for:
32066              * <pre>
32067              * $state.transitionTo($state.current, $stateParams, { 
32068              *   reload: true, inherit: false, notify: true
32069              * });
32070              * </pre>
32071              *
32072              * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
32073              * @example
32074              * <pre>
32075              * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
32076              * //and current state is 'contacts.detail.item'
32077              * var app angular.module('app', ['ui.router']);
32078              *
32079              * app.controller('ctrl', function ($scope, $state) {
32080              *   $scope.reload = function(){
32081              *     //will reload 'contact.detail' and 'contact.detail.item' states
32082              *     $state.reload('contact.detail');
32083              *   }
32084              * });
32085              * </pre>
32086              *
32087              * `reload()` is just an alias for:
32088              * <pre>
32089              * $state.transitionTo($state.current, $stateParams, { 
32090              *   reload: true, inherit: false, notify: true
32091              * });
32092              * </pre>
32093
32094              * @returns {promise} A promise representing the state of the new transition. See
32095              * {@link ui.router.state.$state#methods_go $state.go}.
32096              */
32097             $state.reload = function reload(state) {
32098               return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
32099             };
32100
32101             /**
32102              * @ngdoc function
32103              * @name ui.router.state.$state#go
32104              * @methodOf ui.router.state.$state
32105              *
32106              * @description
32107              * Convenience method for transitioning to a new state. `$state.go` calls 
32108              * `$state.transitionTo` internally but automatically sets options to 
32109              * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. 
32110              * This allows you to easily use an absolute or relative to path and specify 
32111              * only the parameters you'd like to update (while letting unspecified parameters 
32112              * inherit from the currently active ancestor states).
32113              *
32114              * @example
32115              * <pre>
32116              * var app = angular.module('app', ['ui.router']);
32117              *
32118              * app.controller('ctrl', function ($scope, $state) {
32119              *   $scope.changeState = function () {
32120              *     $state.go('contact.detail');
32121              *   };
32122              * });
32123              * </pre>
32124              * <img src='../ngdoc_assets/StateGoExamples.png'/>
32125              *
32126              * @param {string} to Absolute state name or relative state path. Some examples:
32127              *
32128              * - `$state.go('contact.detail')` - will go to the `contact.detail` state
32129              * - `$state.go('^')` - will go to a parent state
32130              * - `$state.go('^.sibling')` - will go to a sibling state
32131              * - `$state.go('.child.grandchild')` - will go to grandchild state
32132              *
32133              * @param {object=} params A map of the parameters that will be sent to the state, 
32134              * will populate $stateParams. Any parameters that are not specified will be inherited from currently 
32135              * defined parameters. This allows, for example, going to a sibling state that shares parameters
32136              * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
32137              * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
32138              * will get you all current parameters, etc.
32139              * @param {object=} options Options object. The options are:
32140              *
32141              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32142              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32143              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32144              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32145              *    defines which state to be relative from.
32146              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32147              * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params 
32148              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32149              *    use this when you want to force a reload when *everything* is the same, including search params.
32150              *
32151              * @returns {promise} A promise representing the state of the new transition.
32152              *
32153              * Possible success values:
32154              *
32155              * - $state.current
32156              *
32157              * <br/>Possible rejection values:
32158              *
32159              * - 'transition superseded' - when a newer transition has been started after this one
32160              * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
32161              * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
32162              *   when a `$stateNotFound` `event.retry` promise errors.
32163              * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
32164              * - *resolve error* - when an error has occurred with a `resolve`
32165              *
32166              */
32167             $state.go = function go(to, params, options) {
32168               return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
32169             };
32170
32171             /**
32172              * @ngdoc function
32173              * @name ui.router.state.$state#transitionTo
32174              * @methodOf ui.router.state.$state
32175              *
32176              * @description
32177              * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
32178              * uses `transitionTo` internally. `$state.go` is recommended in most situations.
32179              *
32180              * @example
32181              * <pre>
32182              * var app = angular.module('app', ['ui.router']);
32183              *
32184              * app.controller('ctrl', function ($scope, $state) {
32185              *   $scope.changeState = function () {
32186              *     $state.transitionTo('contact.detail');
32187              *   };
32188              * });
32189              * </pre>
32190              *
32191              * @param {string} to State name.
32192              * @param {object=} toParams A map of the parameters that will be sent to the state,
32193              * will populate $stateParams.
32194              * @param {object=} options Options object. The options are:
32195              *
32196              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32197              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32198              * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
32199              * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), 
32200              *    defines which state to be relative from.
32201              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32202              * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params 
32203              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32204              *    use this when you want to force a reload when *everything* is the same, including search params.
32205              *    if String, then will reload the state with the name given in reload, and any children.
32206              *    if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
32207              *
32208              * @returns {promise} A promise representing the state of the new transition. See
32209              * {@link ui.router.state.$state#methods_go $state.go}.
32210              */
32211             $state.transitionTo = function transitionTo(to, toParams, options) {
32212               toParams = toParams || {};
32213               options = extend({
32214                 location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
32215               }, options || {});
32216
32217               var from = $state.$current, fromParams = $state.params, fromPath = from.path;
32218               var evt, toState = findState(to, options.relative);
32219
32220               // Store the hash param for later (since it will be stripped out by various methods)
32221               var hash = toParams['#'];
32222
32223               if (!isDefined(toState)) {
32224                 var redirect = { to: to, toParams: toParams, options: options };
32225                 var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
32226
32227                 if (redirectResult) {
32228                   return redirectResult;
32229                 }
32230
32231                 // Always retry once if the $stateNotFound was not prevented
32232                 // (handles either redirect changed or state lazy-definition)
32233                 to = redirect.to;
32234                 toParams = redirect.toParams;
32235                 options = redirect.options;
32236                 toState = findState(to, options.relative);
32237
32238                 if (!isDefined(toState)) {
32239                   if (!options.relative) throw new Error("No such state '" + to + "'");
32240                   throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
32241                 }
32242               }
32243               if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
32244               if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
32245               if (!toState.params.$$validates(toParams)) return TransitionFailed;
32246
32247               toParams = toState.params.$$values(toParams);
32248               to = toState;
32249
32250               var toPath = to.path;
32251
32252               // Starting from the root of the path, keep all levels that haven't changed
32253               var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
32254
32255               if (!options.reload) {
32256                 while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
32257                   locals = toLocals[keep] = state.locals;
32258                   keep++;
32259                   state = toPath[keep];
32260                 }
32261               } else if (isString(options.reload) || isObject(options.reload)) {
32262                 if (isObject(options.reload) && !options.reload.name) {
32263                   throw new Error('Invalid reload state object');
32264                 }
32265                 
32266                 var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
32267                 if (options.reload && !reloadState) {
32268                   throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
32269                 }
32270
32271                 while (state && state === fromPath[keep] && state !== reloadState) {
32272                   locals = toLocals[keep] = state.locals;
32273                   keep++;
32274                   state = toPath[keep];
32275                 }
32276               }
32277
32278               // If we're going to the same state and all locals are kept, we've got nothing to do.
32279               // But clear 'transition', as we still want to cancel any other pending transitions.
32280               // TODO: We may not want to bump 'transition' if we're called from a location change
32281               // that we've initiated ourselves, because we might accidentally abort a legitimate
32282               // transition initiated from code?
32283               if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
32284                 if (hash) toParams['#'] = hash;
32285                 $state.params = toParams;
32286                 copy($state.params, $stateParams);
32287                 if (options.location && to.navigable && to.navigable.url) {
32288                   $urlRouter.push(to.navigable.url, toParams, {
32289                     $$avoidResync: true, replace: options.location === 'replace'
32290                   });
32291                   $urlRouter.update(true);
32292                 }
32293                 $state.transition = null;
32294                 return $q.when($state.current);
32295               }
32296
32297               // Filter parameters before we pass them to event handlers etc.
32298               toParams = filterByKeys(to.params.$$keys(), toParams || {});
32299
32300               // Broadcast start event and cancel the transition if requested
32301               if (options.notify) {
32302                 /**
32303                  * @ngdoc event
32304                  * @name ui.router.state.$state#$stateChangeStart
32305                  * @eventOf ui.router.state.$state
32306                  * @eventType broadcast on root scope
32307                  * @description
32308                  * Fired when the state transition **begins**. You can use `event.preventDefault()`
32309                  * to prevent the transition from happening and then the transition promise will be
32310                  * rejected with a `'transition prevented'` value.
32311                  *
32312                  * @param {Object} event Event object.
32313                  * @param {State} toState The state being transitioned to.
32314                  * @param {Object} toParams The params supplied to the `toState`.
32315                  * @param {State} fromState The current state, pre-transition.
32316                  * @param {Object} fromParams The params supplied to the `fromState`.
32317                  *
32318                  * @example
32319                  *
32320                  * <pre>
32321                  * $rootScope.$on('$stateChangeStart',
32322                  * function(event, toState, toParams, fromState, fromParams){
32323                  *     event.preventDefault();
32324                  *     // transitionTo() promise will be rejected with
32325                  *     // a 'transition prevented' error
32326                  * })
32327                  * </pre>
32328                  */
32329                 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
32330                   $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32331                   $urlRouter.update();
32332                   return TransitionPrevented;
32333                 }
32334               }
32335
32336               // Resolve locals for the remaining states, but don't update any global state just
32337               // yet -- if anything fails to resolve the current state needs to remain untouched.
32338               // We also set up an inheritance chain for the locals here. This allows the view directive
32339               // to quickly look up the correct definition for each view in the current state. Even
32340               // though we create the locals object itself outside resolveState(), it is initially
32341               // empty and gets filled asynchronously. We need to keep track of the promise for the
32342               // (fully resolved) current locals, and pass this down the chain.
32343               var resolved = $q.when(locals);
32344
32345               for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
32346                 locals = toLocals[l] = inherit(locals);
32347                 resolved = resolveState(state, toParams, state === to, resolved, locals, options);
32348               }
32349
32350               // Once everything is resolved, we are ready to perform the actual transition
32351               // and return a promise for the new state. We also keep track of what the
32352               // current promise is, so that we can detect overlapping transitions and
32353               // keep only the outcome of the last transition.
32354               var transition = $state.transition = resolved.then(function () {
32355                 var l, entering, exiting;
32356
32357                 if ($state.transition !== transition) return TransitionSuperseded;
32358
32359                 // Exit 'from' states not kept
32360                 for (l = fromPath.length - 1; l >= keep; l--) {
32361                   exiting = fromPath[l];
32362                   if (exiting.self.onExit) {
32363                     $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
32364                   }
32365                   exiting.locals = null;
32366                 }
32367
32368                 // Enter 'to' states not kept
32369                 for (l = keep; l < toPath.length; l++) {
32370                   entering = toPath[l];
32371                   entering.locals = toLocals[l];
32372                   if (entering.self.onEnter) {
32373                     $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
32374                   }
32375                 }
32376
32377                 // Re-add the saved hash before we start returning things
32378                 if (hash) toParams['#'] = hash;
32379
32380                 // Run it again, to catch any transitions in callbacks
32381                 if ($state.transition !== transition) return TransitionSuperseded;
32382
32383                 // Update globals in $state
32384                 $state.$current = to;
32385                 $state.current = to.self;
32386                 $state.params = toParams;
32387                 copy($state.params, $stateParams);
32388                 $state.transition = null;
32389
32390                 if (options.location && to.navigable) {
32391                   $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
32392                     $$avoidResync: true, replace: options.location === 'replace'
32393                   });
32394                 }
32395
32396                 if (options.notify) {
32397                 /**
32398                  * @ngdoc event
32399                  * @name ui.router.state.$state#$stateChangeSuccess
32400                  * @eventOf ui.router.state.$state
32401                  * @eventType broadcast on root scope
32402                  * @description
32403                  * Fired once the state transition is **complete**.
32404                  *
32405                  * @param {Object} event Event object.
32406                  * @param {State} toState The state being transitioned to.
32407                  * @param {Object} toParams The params supplied to the `toState`.
32408                  * @param {State} fromState The current state, pre-transition.
32409                  * @param {Object} fromParams The params supplied to the `fromState`.
32410                  */
32411                   $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
32412                 }
32413                 $urlRouter.update(true);
32414
32415                 return $state.current;
32416               }, function (error) {
32417                 if ($state.transition !== transition) return TransitionSuperseded;
32418
32419                 $state.transition = null;
32420                 /**
32421                  * @ngdoc event
32422                  * @name ui.router.state.$state#$stateChangeError
32423                  * @eventOf ui.router.state.$state
32424                  * @eventType broadcast on root scope
32425                  * @description
32426                  * Fired when an **error occurs** during transition. It's important to note that if you
32427                  * have any errors in your resolve functions (javascript errors, non-existent services, etc)
32428                  * they will not throw traditionally. You must listen for this $stateChangeError event to
32429                  * catch **ALL** errors.
32430                  *
32431                  * @param {Object} event Event object.
32432                  * @param {State} toState The state being transitioned to.
32433                  * @param {Object} toParams The params supplied to the `toState`.
32434                  * @param {State} fromState The current state, pre-transition.
32435                  * @param {Object} fromParams The params supplied to the `fromState`.
32436                  * @param {Error} error The resolve error object.
32437                  */
32438                 evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
32439
32440                 if (!evt.defaultPrevented) {
32441                     $urlRouter.update();
32442                 }
32443
32444                 return $q.reject(error);
32445               });
32446
32447               return transition;
32448             };
32449
32450             /**
32451              * @ngdoc function
32452              * @name ui.router.state.$state#is
32453              * @methodOf ui.router.state.$state
32454              *
32455              * @description
32456              * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
32457              * but only checks for the full state name. If params is supplied then it will be
32458              * tested for strict equality against the current active params object, so all params
32459              * must match with none missing and no extras.
32460              *
32461              * @example
32462              * <pre>
32463              * $state.$current.name = 'contacts.details.item';
32464              *
32465              * // absolute name
32466              * $state.is('contact.details.item'); // returns true
32467              * $state.is(contactDetailItemStateObject); // returns true
32468              *
32469              * // relative name (. and ^), typically from a template
32470              * // E.g. from the 'contacts.details' template
32471              * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
32472              * </pre>
32473              *
32474              * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
32475              * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
32476              * to test against the current active state.
32477              * @param {object=} options An options object.  The options are:
32478              *
32479              * - **`relative`** - {string|object} -  If `stateOrName` is a relative state name and `options.relative` is set, .is will
32480              * test relative to `options.relative` state (or name).
32481              *
32482              * @returns {boolean} Returns true if it is the state.
32483              */
32484             $state.is = function is(stateOrName, params, options) {
32485               options = extend({ relative: $state.$current }, options || {});
32486               var state = findState(stateOrName, options.relative);
32487
32488               if (!isDefined(state)) { return undefined; }
32489               if ($state.$current !== state) { return false; }
32490               return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
32491             };
32492
32493             /**
32494              * @ngdoc function
32495              * @name ui.router.state.$state#includes
32496              * @methodOf ui.router.state.$state
32497              *
32498              * @description
32499              * A method to determine if the current active state is equal to or is the child of the
32500              * state stateName. If any params are passed then they will be tested for a match as well.
32501              * Not all the parameters need to be passed, just the ones you'd like to test for equality.
32502              *
32503              * @example
32504              * Partial and relative names
32505              * <pre>
32506              * $state.$current.name = 'contacts.details.item';
32507              *
32508              * // Using partial names
32509              * $state.includes("contacts"); // returns true
32510              * $state.includes("contacts.details"); // returns true
32511              * $state.includes("contacts.details.item"); // returns true
32512              * $state.includes("contacts.list"); // returns false
32513              * $state.includes("about"); // returns false
32514              *
32515              * // Using relative names (. and ^), typically from a template
32516              * // E.g. from the 'contacts.details' template
32517              * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
32518              * </pre>
32519              *
32520              * Basic globbing patterns
32521              * <pre>
32522              * $state.$current.name = 'contacts.details.item.url';
32523              *
32524              * $state.includes("*.details.*.*"); // returns true
32525              * $state.includes("*.details.**"); // returns true
32526              * $state.includes("**.item.**"); // returns true
32527              * $state.includes("*.details.item.url"); // returns true
32528              * $state.includes("*.details.*.url"); // returns true
32529              * $state.includes("*.details.*"); // returns false
32530              * $state.includes("item.**"); // returns false
32531              * </pre>
32532              *
32533              * @param {string} stateOrName A partial name, relative name, or glob pattern
32534              * to be searched for within the current state name.
32535              * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
32536              * that you'd like to test against the current active state.
32537              * @param {object=} options An options object.  The options are:
32538              *
32539              * - **`relative`** - {string|object=} -  If `stateOrName` is a relative state reference and `options.relative` is set,
32540              * .includes will test relative to `options.relative` state (or name).
32541              *
32542              * @returns {boolean} Returns true if it does include the state
32543              */
32544             $state.includes = function includes(stateOrName, params, options) {
32545               options = extend({ relative: $state.$current }, options || {});
32546               if (isString(stateOrName) && isGlob(stateOrName)) {
32547                 if (!doesStateMatchGlob(stateOrName)) {
32548                   return false;
32549                 }
32550                 stateOrName = $state.$current.name;
32551               }
32552
32553               var state = findState(stateOrName, options.relative);
32554               if (!isDefined(state)) { return undefined; }
32555               if (!isDefined($state.$current.includes[state.name])) { return false; }
32556               return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
32557             };
32558
32559
32560             /**
32561              * @ngdoc function
32562              * @name ui.router.state.$state#href
32563              * @methodOf ui.router.state.$state
32564              *
32565              * @description
32566              * A url generation method that returns the compiled url for the given state populated with the given params.
32567              *
32568              * @example
32569              * <pre>
32570              * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
32571              * </pre>
32572              *
32573              * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
32574              * @param {object=} params An object of parameter values to fill the state's required parameters.
32575              * @param {object=} options Options object. The options are:
32576              *
32577              * - **`lossy`** - {boolean=true} -  If true, and if there is no url associated with the state provided in the
32578              *    first parameter, then the constructed href url will be built from the first navigable ancestor (aka
32579              *    ancestor with a valid url).
32580              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32581              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32582              *    defines which state to be relative from.
32583              * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
32584              * 
32585              * @returns {string} compiled state url
32586              */
32587             $state.href = function href(stateOrName, params, options) {
32588               options = extend({
32589                 lossy:    true,
32590                 inherit:  true,
32591                 absolute: false,
32592                 relative: $state.$current
32593               }, options || {});
32594
32595               var state = findState(stateOrName, options.relative);
32596
32597               if (!isDefined(state)) return null;
32598               if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
32599               
32600               var nav = (state && options.lossy) ? state.navigable : state;
32601
32602               if (!nav || nav.url === undefined || nav.url === null) {
32603                 return null;
32604               }
32605               return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
32606                 absolute: options.absolute
32607               });
32608             };
32609
32610             /**
32611              * @ngdoc function
32612              * @name ui.router.state.$state#get
32613              * @methodOf ui.router.state.$state
32614              *
32615              * @description
32616              * Returns the state configuration object for any specific state or all states.
32617              *
32618              * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
32619              * the requested state. If not provided, returns an array of ALL state configs.
32620              * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
32621              * @returns {Object|Array} State configuration object or array of all objects.
32622              */
32623             $state.get = function (stateOrName, context) {
32624               if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
32625               var state = findState(stateOrName, context || $state.$current);
32626               return (state && state.self) ? state.self : null;
32627             };
32628
32629             function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
32630               // Make a restricted $stateParams with only the parameters that apply to this state if
32631               // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
32632               // we also need $stateParams to be available for any $injector calls we make during the
32633               // dependency resolution process.
32634               var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
32635               var locals = { $stateParams: $stateParams };
32636
32637               // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
32638               // We're also including $stateParams in this; that way the parameters are restricted
32639               // to the set that should be visible to the state, and are independent of when we update
32640               // the global $state and $stateParams values.
32641               dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
32642               var promises = [dst.resolve.then(function (globals) {
32643                 dst.globals = globals;
32644               })];
32645               if (inherited) promises.push(inherited);
32646
32647               function resolveViews() {
32648                 var viewsPromises = [];
32649
32650                 // Resolve template and dependencies for all views.
32651                 forEach(state.views, function (view, name) {
32652                   var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
32653                   injectables.$template = [ function () {
32654                     return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
32655                   }];
32656
32657                   viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
32658                     // References to the controller (only instantiated at link time)
32659                     if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
32660                       var injectLocals = angular.extend({}, injectables, dst.globals);
32661                       result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
32662                     } else {
32663                       result.$$controller = view.controller;
32664                     }
32665                     // Provide access to the state itself for internal use
32666                     result.$$state = state;
32667                     result.$$controllerAs = view.controllerAs;
32668                     dst[name] = result;
32669                   }));
32670                 });
32671
32672                 return $q.all(viewsPromises).then(function(){
32673                   return dst.globals;
32674                 });
32675               }
32676
32677               // Wait for all the promises and then return the activation object
32678               return $q.all(promises).then(resolveViews).then(function (values) {
32679                 return dst;
32680               });
32681             }
32682
32683             return $state;
32684           }
32685
32686           function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
32687             // Return true if there are no differences in non-search (path/object) params, false if there are differences
32688             function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
32689               // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
32690               function notSearchParam(key) {
32691                 return fromAndToState.params[key].location != "search";
32692               }
32693               var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
32694               var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
32695               var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
32696               return nonQueryParamSet.$$equals(fromParams, toParams);
32697             }
32698
32699             // If reload was not explicitly requested
32700             // and we're transitioning to the same state we're already in
32701             // and    the locals didn't change
32702             //     or they changed in a way that doesn't merit reloading
32703             //        (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
32704             // Then return true.
32705             if (!options.reload && to === from &&
32706               (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
32707               return true;
32708             }
32709           }
32710         }
32711
32712         angular.module('ui.router.state')
32713           .value('$stateParams', {})
32714           .provider('$state', $StateProvider);
32715
32716
32717         $ViewProvider.$inject = [];
32718         function $ViewProvider() {
32719
32720           this.$get = $get;
32721           /**
32722            * @ngdoc object
32723            * @name ui.router.state.$view
32724            *
32725            * @requires ui.router.util.$templateFactory
32726            * @requires $rootScope
32727            *
32728            * @description
32729            *
32730            */
32731           $get.$inject = ['$rootScope', '$templateFactory'];
32732           function $get(   $rootScope,   $templateFactory) {
32733             return {
32734               // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
32735               /**
32736                * @ngdoc function
32737                * @name ui.router.state.$view#load
32738                * @methodOf ui.router.state.$view
32739                *
32740                * @description
32741                *
32742                * @param {string} name name
32743                * @param {object} options option object.
32744                */
32745               load: function load(name, options) {
32746                 var result, defaults = {
32747                   template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
32748                 };
32749                 options = extend(defaults, options);
32750
32751                 if (options.view) {
32752                   result = $templateFactory.fromConfig(options.view, options.params, options.locals);
32753                 }
32754                 if (result && options.notify) {
32755                 /**
32756                  * @ngdoc event
32757                  * @name ui.router.state.$state#$viewContentLoading
32758                  * @eventOf ui.router.state.$view
32759                  * @eventType broadcast on root scope
32760                  * @description
32761                  *
32762                  * Fired once the view **begins loading**, *before* the DOM is rendered.
32763                  *
32764                  * @param {Object} event Event object.
32765                  * @param {Object} viewConfig The view config properties (template, controller, etc).
32766                  *
32767                  * @example
32768                  *
32769                  * <pre>
32770                  * $scope.$on('$viewContentLoading',
32771                  * function(event, viewConfig){
32772                  *     // Access to all the view config properties.
32773                  *     // and one special property 'targetView'
32774                  *     // viewConfig.targetView
32775                  * });
32776                  * </pre>
32777                  */
32778                   $rootScope.$broadcast('$viewContentLoading', options);
32779                 }
32780                 return result;
32781               }
32782             };
32783           }
32784         }
32785
32786         angular.module('ui.router.state').provider('$view', $ViewProvider);
32787
32788         /**
32789          * @ngdoc object
32790          * @name ui.router.state.$uiViewScrollProvider
32791          *
32792          * @description
32793          * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
32794          */
32795         function $ViewScrollProvider() {
32796
32797           var useAnchorScroll = false;
32798
32799           /**
32800            * @ngdoc function
32801            * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
32802            * @methodOf ui.router.state.$uiViewScrollProvider
32803            *
32804            * @description
32805            * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
32806            * scrolling based on the url anchor.
32807            */
32808           this.useAnchorScroll = function () {
32809             useAnchorScroll = true;
32810           };
32811
32812           /**
32813            * @ngdoc object
32814            * @name ui.router.state.$uiViewScroll
32815            *
32816            * @requires $anchorScroll
32817            * @requires $timeout
32818            *
32819            * @description
32820            * When called with a jqLite element, it scrolls the element into view (after a
32821            * `$timeout` so the DOM has time to refresh).
32822            *
32823            * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
32824            * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
32825            */
32826           this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
32827             if (useAnchorScroll) {
32828               return $anchorScroll;
32829             }
32830
32831             return function ($element) {
32832               return $timeout(function () {
32833                 $element[0].scrollIntoView();
32834               }, 0, false);
32835             };
32836           }];
32837         }
32838
32839         angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
32840
32841         /**
32842          * @ngdoc directive
32843          * @name ui.router.state.directive:ui-view
32844          *
32845          * @requires ui.router.state.$state
32846          * @requires $compile
32847          * @requires $controller
32848          * @requires $injector
32849          * @requires ui.router.state.$uiViewScroll
32850          * @requires $document
32851          *
32852          * @restrict ECA
32853          *
32854          * @description
32855          * The ui-view directive tells $state where to place your templates.
32856          *
32857          * @param {string=} name A view name. The name should be unique amongst the other views in the
32858          * same state. You can have views of the same name that live in different states.
32859          *
32860          * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
32861          * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
32862          * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
32863          * scroll ui-view elements into view when they are populated during a state activation.
32864          *
32865          * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
32866          * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
32867          *
32868          * @param {string=} onload Expression to evaluate whenever the view updates.
32869          * 
32870          * @example
32871          * A view can be unnamed or named. 
32872          * <pre>
32873          * <!-- Unnamed -->
32874          * <div ui-view></div> 
32875          * 
32876          * <!-- Named -->
32877          * <div ui-view="viewName"></div>
32878          * </pre>
32879          *
32880          * You can only have one unnamed view within any template (or root html). If you are only using a 
32881          * single view and it is unnamed then you can populate it like so:
32882          * <pre>
32883          * <div ui-view></div> 
32884          * $stateProvider.state("home", {
32885          *   template: "<h1>HELLO!</h1>"
32886          * })
32887          * </pre>
32888          * 
32889          * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
32890          * config property, by name, in this case an empty name:
32891          * <pre>
32892          * $stateProvider.state("home", {
32893          *   views: {
32894          *     "": {
32895          *       template: "<h1>HELLO!</h1>"
32896          *     }
32897          *   }    
32898          * })
32899          * </pre>
32900          * 
32901          * But typically you'll only use the views property if you name your view or have more than one view 
32902          * in the same template. There's not really a compelling reason to name a view if its the only one, 
32903          * but you could if you wanted, like so:
32904          * <pre>
32905          * <div ui-view="main"></div>
32906          * </pre> 
32907          * <pre>
32908          * $stateProvider.state("home", {
32909          *   views: {
32910          *     "main": {
32911          *       template: "<h1>HELLO!</h1>"
32912          *     }
32913          *   }    
32914          * })
32915          * </pre>
32916          * 
32917          * Really though, you'll use views to set up multiple views:
32918          * <pre>
32919          * <div ui-view></div>
32920          * <div ui-view="chart"></div> 
32921          * <div ui-view="data"></div> 
32922          * </pre>
32923          * 
32924          * <pre>
32925          * $stateProvider.state("home", {
32926          *   views: {
32927          *     "": {
32928          *       template: "<h1>HELLO!</h1>"
32929          *     },
32930          *     "chart": {
32931          *       template: "<chart_thing/>"
32932          *     },
32933          *     "data": {
32934          *       template: "<data_thing/>"
32935          *     }
32936          *   }    
32937          * })
32938          * </pre>
32939          *
32940          * Examples for `autoscroll`:
32941          *
32942          * <pre>
32943          * <!-- If autoscroll present with no expression,
32944          *      then scroll ui-view into view -->
32945          * <ui-view autoscroll/>
32946          *
32947          * <!-- If autoscroll present with valid expression,
32948          *      then scroll ui-view into view if expression evaluates to true -->
32949          * <ui-view autoscroll='true'/>
32950          * <ui-view autoscroll='false'/>
32951          * <ui-view autoscroll='scopeVariable'/>
32952          * </pre>
32953          */
32954         $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
32955         function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {
32956
32957           function getService() {
32958             return ($injector.has) ? function(service) {
32959               return $injector.has(service) ? $injector.get(service) : null;
32960             } : function(service) {
32961               try {
32962                 return $injector.get(service);
32963               } catch (e) {
32964                 return null;
32965               }
32966             };
32967           }
32968
32969           var service = getService(),
32970               $animator = service('$animator'),
32971               $animate = service('$animate');
32972
32973           // Returns a set of DOM manipulation functions based on which Angular version
32974           // it should use
32975           function getRenderer(attrs, scope) {
32976             var statics = function() {
32977               return {
32978                 enter: function (element, target, cb) { target.after(element); cb(); },
32979                 leave: function (element, cb) { element.remove(); cb(); }
32980               };
32981             };
32982
32983             if ($animate) {
32984               return {
32985                 enter: function(element, target, cb) {
32986                   var promise = $animate.enter(element, null, target, cb);
32987                   if (promise && promise.then) promise.then(cb);
32988                 },
32989                 leave: function(element, cb) {
32990                   var promise = $animate.leave(element, cb);
32991                   if (promise && promise.then) promise.then(cb);
32992                 }
32993               };
32994             }
32995
32996             if ($animator) {
32997               var animate = $animator && $animator(scope, attrs);
32998
32999               return {
33000                 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
33001                 leave: function(element, cb) { animate.leave(element); cb(); }
33002               };
33003             }
33004
33005             return statics();
33006           }
33007
33008           var directive = {
33009             restrict: 'ECA',
33010             terminal: true,
33011             priority: 400,
33012             transclude: 'element',
33013             compile: function (tElement, tAttrs, $transclude) {
33014               return function (scope, $element, attrs) {
33015                 var previousEl, currentEl, currentScope, latestLocals,
33016                     onloadExp     = attrs.onload || '',
33017                     autoScrollExp = attrs.autoscroll,
33018                     renderer      = getRenderer(attrs, scope);
33019
33020                 scope.$on('$stateChangeSuccess', function() {
33021                   updateView(false);
33022                 });
33023                 scope.$on('$viewContentLoading', function() {
33024                   updateView(false);
33025                 });
33026
33027                 updateView(true);
33028
33029                 function cleanupLastView() {
33030                   if (previousEl) {
33031                     previousEl.remove();
33032                     previousEl = null;
33033                   }
33034
33035                   if (currentScope) {
33036                     currentScope.$destroy();
33037                     currentScope = null;
33038                   }
33039
33040                   if (currentEl) {
33041                     renderer.leave(currentEl, function() {
33042                       previousEl = null;
33043                     });
33044
33045                     previousEl = currentEl;
33046                     currentEl = null;
33047                   }
33048                 }
33049
33050                 function updateView(firstTime) {
33051                   var newScope,
33052                       name            = getUiViewName(scope, attrs, $element, $interpolate),
33053                       previousLocals  = name && $state.$current && $state.$current.locals[name];
33054
33055                   if (!firstTime && previousLocals === latestLocals) return; // nothing to do
33056                   newScope = scope.$new();
33057                   latestLocals = $state.$current.locals[name];
33058
33059                   var clone = $transclude(newScope, function(clone) {
33060                     renderer.enter(clone, $element, function onUiViewEnter() {
33061                       if(currentScope) {
33062                         currentScope.$emit('$viewContentAnimationEnded');
33063                       }
33064
33065                       if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
33066                         $uiViewScroll(clone);
33067                       }
33068                     });
33069                     cleanupLastView();
33070                   });
33071
33072                   currentEl = clone;
33073                   currentScope = newScope;
33074                   /**
33075                    * @ngdoc event
33076                    * @name ui.router.state.directive:ui-view#$viewContentLoaded
33077                    * @eventOf ui.router.state.directive:ui-view
33078                    * @eventType emits on ui-view directive scope
33079                    * @description           *
33080                    * Fired once the view is **loaded**, *after* the DOM is rendered.
33081                    *
33082                    * @param {Object} event Event object.
33083                    */
33084                   currentScope.$emit('$viewContentLoaded');
33085                   currentScope.$eval(onloadExp);
33086                 }
33087               };
33088             }
33089           };
33090
33091           return directive;
33092         }
33093
33094         $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
33095         function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
33096           return {
33097             restrict: 'ECA',
33098             priority: -400,
33099             compile: function (tElement) {
33100               var initial = tElement.html();
33101               return function (scope, $element, attrs) {
33102                 var current = $state.$current,
33103                     name = getUiViewName(scope, attrs, $element, $interpolate),
33104                     locals  = current && current.locals[name];
33105
33106                 if (! locals) {
33107                   return;
33108                 }
33109
33110                 $element.data('$uiView', { name: name, state: locals.$$state });
33111                 $element.html(locals.$template ? locals.$template : initial);
33112
33113                 var link = $compile($element.contents());
33114
33115                 if (locals.$$controller) {
33116                   locals.$scope = scope;
33117                   locals.$element = $element;
33118                   var controller = $controller(locals.$$controller, locals);
33119                   if (locals.$$controllerAs) {
33120                     scope[locals.$$controllerAs] = controller;
33121                   }
33122                   $element.data('$ngControllerController', controller);
33123                   $element.children().data('$ngControllerController', controller);
33124                 }
33125
33126                 link(scope);
33127               };
33128             }
33129           };
33130         }
33131
33132         /**
33133          * Shared ui-view code for both directives:
33134          * Given scope, element, and its attributes, return the view's name
33135          */
33136         function getUiViewName(scope, attrs, element, $interpolate) {
33137           var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
33138           var inherited = element.inheritedData('$uiView');
33139           return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
33140         }
33141
33142         angular.module('ui.router.state').directive('uiView', $ViewDirective);
33143         angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
33144
33145         function parseStateRef(ref, current) {
33146           var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
33147           if (preparsed) ref = current + '(' + preparsed[1] + ')';
33148           parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
33149           if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
33150           return { state: parsed[1], paramExpr: parsed[3] || null };
33151         }
33152
33153         function stateContext(el) {
33154           var stateData = el.parent().inheritedData('$uiView');
33155
33156           if (stateData && stateData.state && stateData.state.name) {
33157             return stateData.state;
33158           }
33159         }
33160
33161         /**
33162          * @ngdoc directive
33163          * @name ui.router.state.directive:ui-sref
33164          *
33165          * @requires ui.router.state.$state
33166          * @requires $timeout
33167          *
33168          * @restrict A
33169          *
33170          * @description
33171          * A directive that binds a link (`<a>` tag) to a state. If the state has an associated 
33172          * URL, the directive will automatically generate & update the `href` attribute via 
33173          * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 
33174          * the link will trigger a state transition with optional parameters. 
33175          *
33176          * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 
33177          * handled natively by the browser.
33178          *
33179          * You can also use relative state paths within ui-sref, just like the relative 
33180          * paths passed to `$state.go()`. You just need to be aware that the path is relative
33181          * to the state that the link lives in, in other words the state that loaded the 
33182          * template containing the link.
33183          *
33184          * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
33185          * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
33186          * and `reload`.
33187          *
33188          * @example
33189          * Here's an example of how you'd use ui-sref and how it would compile. If you have the 
33190          * following template:
33191          * <pre>
33192          * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33193          * 
33194          * <ul>
33195          *     <li ng-repeat="contact in contacts">
33196          *         <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
33197          *     </li>
33198          * </ul>
33199          * </pre>
33200          * 
33201          * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33202          * <pre>
33203          * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
33204          * 
33205          * <ul>
33206          *     <li ng-repeat="contact in contacts">
33207          *         <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
33208          *     </li>
33209          *     <li ng-repeat="contact in contacts">
33210          *         <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
33211          *     </li>
33212          *     <li ng-repeat="contact in contacts">
33213          *         <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
33214          *     </li>
33215          * </ul>
33216          *
33217          * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
33218          * </pre>
33219          *
33220          * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
33221          * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33222          */
33223         $StateRefDirective.$inject = ['$state', '$timeout'];
33224         function $StateRefDirective($state, $timeout) {
33225           var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
33226
33227           return {
33228             restrict: 'A',
33229             require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33230             link: function(scope, element, attrs, uiSrefActive) {
33231               var ref = parseStateRef(attrs.uiSref, $state.current.name);
33232               var params = null, url = null, base = stateContext(element) || $state.$current;
33233               // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33234               var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
33235                          'xlink:href' : 'href';
33236               var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
33237               var isForm = element[0].nodeName === "FORM";
33238               var attr = isForm ? "action" : hrefKind, nav = true;
33239
33240               var options = { relative: base, inherit: true };
33241               var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
33242
33243               angular.forEach(allowedOptions, function(option) {
33244                 if (option in optionsOverride) {
33245                   options[option] = optionsOverride[option];
33246                 }
33247               });
33248
33249               var update = function(newVal) {
33250                 if (newVal) params = angular.copy(newVal);
33251                 if (!nav) return;
33252
33253                 newHref = $state.href(ref.state, params, options);
33254
33255                 var activeDirective = uiSrefActive[1] || uiSrefActive[0];
33256                 if (activeDirective) {
33257                   activeDirective.$$addStateInfo(ref.state, params);
33258                 }
33259                 if (newHref === null) {
33260                   nav = false;
33261                   return false;
33262                 }
33263                 attrs.$set(attr, newHref);
33264               };
33265
33266               if (ref.paramExpr) {
33267                 scope.$watch(ref.paramExpr, function(newVal, oldVal) {
33268                   if (newVal !== params) update(newVal);
33269                 }, true);
33270                 params = angular.copy(scope.$eval(ref.paramExpr));
33271               }
33272               update();
33273
33274               if (isForm) return;
33275
33276               element.bind("click", function(e) {
33277                 var button = e.which || e.button;
33278                 if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
33279                   // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33280                   var transition = $timeout(function() {
33281                     $state.go(ref.state, params, options);
33282                   });
33283                   e.preventDefault();
33284
33285                   // if the state has no URL, ignore one preventDefault from the <a> directive.
33286                   var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
33287                   e.preventDefault = function() {
33288                     if (ignorePreventDefaultCount-- <= 0)
33289                       $timeout.cancel(transition);
33290                   };
33291                 }
33292               });
33293             }
33294           };
33295         }
33296
33297         /**
33298          * @ngdoc directive
33299          * @name ui.router.state.directive:ui-sref-active
33300          *
33301          * @requires ui.router.state.$state
33302          * @requires ui.router.state.$stateParams
33303          * @requires $interpolate
33304          *
33305          * @restrict A
33306          *
33307          * @description
33308          * A directive working alongside ui-sref to add classes to an element when the
33309          * related ui-sref directive's state is active, and removing them when it is inactive.
33310          * The primary use-case is to simplify the special appearance of navigation menus
33311          * relying on `ui-sref`, by having the "active" state's menu button appear different,
33312          * distinguishing it from the inactive menu items.
33313          *
33314          * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
33315          * ui-sref-active found at the same level or above the ui-sref will be used.
33316          *
33317          * Will activate when the ui-sref's target state or any child state is active. If you
33318          * need to activate only when the ui-sref target state is active and *not* any of
33319          * it's children, then you will use
33320          * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
33321          *
33322          * @example
33323          * Given the following template:
33324          * <pre>
33325          * <ul>
33326          *   <li ui-sref-active="active" class="item">
33327          *     <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
33328          *   </li>
33329          * </ul>
33330          * </pre>
33331          *
33332          *
33333          * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
33334          * the resulting HTML will appear as (note the 'active' class):
33335          * <pre>
33336          * <ul>
33337          *   <li ui-sref-active="active" class="item active">
33338          *     <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
33339          *   </li>
33340          * </ul>
33341          * </pre>
33342          *
33343          * The class name is interpolated **once** during the directives link time (any further changes to the
33344          * interpolated value are ignored).
33345          *
33346          * Multiple classes may be specified in a space-separated format:
33347          * <pre>
33348          * <ul>
33349          *   <li ui-sref-active='class1 class2 class3'>
33350          *     <a ui-sref="app.user">link</a>
33351          *   </li>
33352          * </ul>
33353          * </pre>
33354          */
33355
33356         /**
33357          * @ngdoc directive
33358          * @name ui.router.state.directive:ui-sref-active-eq
33359          *
33360          * @requires ui.router.state.$state
33361          * @requires ui.router.state.$stateParams
33362          * @requires $interpolate
33363          *
33364          * @restrict A
33365          *
33366          * @description
33367          * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
33368          * when the exact target state used in the `ui-sref` is active; no child states.
33369          *
33370          */
33371         $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
33372         function $StateRefActiveDirective($state, $stateParams, $interpolate) {
33373           return  {
33374             restrict: "A",
33375             controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
33376               var states = [], activeClass;
33377
33378               // There probably isn't much point in $observing this
33379               // uiSrefActive and uiSrefActiveEq share the same directive object with some
33380               // slight difference in logic routing
33381               activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
33382
33383               // Allow uiSref to communicate with uiSrefActive[Equals]
33384               this.$$addStateInfo = function (newState, newParams) {
33385                 var state = $state.get(newState, stateContext($element));
33386
33387                 states.push({
33388                   state: state || { name: newState },
33389                   params: newParams
33390                 });
33391
33392                 update();
33393               };
33394
33395               $scope.$on('$stateChangeSuccess', update);
33396
33397               // Update route state
33398               function update() {
33399                 if (anyMatch()) {
33400                   $element.addClass(activeClass);
33401                 } else {
33402                   $element.removeClass(activeClass);
33403                 }
33404               }
33405
33406               function anyMatch() {
33407                 for (var i = 0; i < states.length; i++) {
33408                   if (isMatch(states[i].state, states[i].params)) {
33409                     return true;
33410                   }
33411                 }
33412                 return false;
33413               }
33414
33415               function isMatch(state, params) {
33416                 if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
33417                   return $state.is(state.name, params);
33418                 } else {
33419                   return $state.includes(state.name, params);
33420                 }
33421               }
33422             }]
33423           };
33424         }
33425
33426         angular.module('ui.router.state')
33427           .directive('uiSref', $StateRefDirective)
33428           .directive('uiSrefActive', $StateRefActiveDirective)
33429           .directive('uiSrefActiveEq', $StateRefActiveDirective);
33430
33431         /**
33432          * @ngdoc filter
33433          * @name ui.router.state.filter:isState
33434          *
33435          * @requires ui.router.state.$state
33436          *
33437          * @description
33438          * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
33439          */
33440         $IsStateFilter.$inject = ['$state'];
33441         function $IsStateFilter($state) {
33442           var isFilter = function (state) {
33443             return $state.is(state);
33444           };
33445           isFilter.$stateful = true;
33446           return isFilter;
33447         }
33448
33449         /**
33450          * @ngdoc filter
33451          * @name ui.router.state.filter:includedByState
33452          *
33453          * @requires ui.router.state.$state
33454          *
33455          * @description
33456          * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
33457          */
33458         $IncludedByStateFilter.$inject = ['$state'];
33459         function $IncludedByStateFilter($state) {
33460           var includesFilter = function (state) {
33461             return $state.includes(state);
33462           };
33463           includesFilter.$stateful = true;
33464           return  includesFilter;
33465         }
33466
33467         angular.module('ui.router.state')
33468           .filter('isState', $IsStateFilter)
33469           .filter('includedByState', $IncludedByStateFilter);
33470         })(window, window.angular);
33471
33472 /***/ },
33473 /* 4 */
33474 /***/ function(module, exports, __webpack_require__) {
33475
33476         __webpack_require__(5);
33477         module.exports = 'ngResource';
33478
33479
33480 /***/ },
33481 /* 5 */
33482 /***/ function(module, exports) {
33483
33484         /**
33485          * @license AngularJS v1.4.8
33486          * (c) 2010-2015 Google, Inc. http://angularjs.org
33487          * License: MIT
33488          */
33489         (function(window, angular, undefined) {'use strict';
33490
33491         var $resourceMinErr = angular.$$minErr('$resource');
33492
33493         // Helper functions and regex to lookup a dotted path on an object
33494         // stopping at undefined/null.  The path must be composed of ASCII
33495         // identifiers (just like $parse)
33496         var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
33497
33498         function isValidDottedPath(path) {
33499           return (path != null && path !== '' && path !== 'hasOwnProperty' &&
33500               MEMBER_NAME_REGEX.test('.' + path));
33501         }
33502
33503         function lookupDottedPath(obj, path) {
33504           if (!isValidDottedPath(path)) {
33505             throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
33506           }
33507           var keys = path.split('.');
33508           for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
33509             var key = keys[i];
33510             obj = (obj !== null) ? obj[key] : undefined;
33511           }
33512           return obj;
33513         }
33514
33515         /**
33516          * Create a shallow copy of an object and clear other fields from the destination
33517          */
33518         function shallowClearAndCopy(src, dst) {
33519           dst = dst || {};
33520
33521           angular.forEach(dst, function(value, key) {
33522             delete dst[key];
33523           });
33524
33525           for (var key in src) {
33526             if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
33527               dst[key] = src[key];
33528             }
33529           }
33530
33531           return dst;
33532         }
33533
33534         /**
33535          * @ngdoc module
33536          * @name ngResource
33537          * @description
33538          *
33539          * # ngResource
33540          *
33541          * The `ngResource` module provides interaction support with RESTful services
33542          * via the $resource service.
33543          *
33544          *
33545          * <div doc-module-components="ngResource"></div>
33546          *
33547          * See {@link ngResource.$resource `$resource`} for usage.
33548          */
33549
33550         /**
33551          * @ngdoc service
33552          * @name $resource
33553          * @requires $http
33554          *
33555          * @description
33556          * A factory which creates a resource object that lets you interact with
33557          * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
33558          *
33559          * The returned resource object has action methods which provide high-level behaviors without
33560          * the need to interact with the low level {@link ng.$http $http} service.
33561          *
33562          * Requires the {@link ngResource `ngResource`} module to be installed.
33563          *
33564          * By default, trailing slashes will be stripped from the calculated URLs,
33565          * which can pose problems with server backends that do not expect that
33566          * behavior.  This can be disabled by configuring the `$resourceProvider` like
33567          * this:
33568          *
33569          * ```js
33570              app.config(['$resourceProvider', function($resourceProvider) {
33571                // Don't strip trailing slashes from calculated URLs
33572                $resourceProvider.defaults.stripTrailingSlashes = false;
33573              }]);
33574          * ```
33575          *
33576          * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
33577          *   `/user/:username`. If you are using a URL with a port number (e.g.
33578          *   `http://example.com:8080/api`), it will be respected.
33579          *
33580          *   If you are using a url with a suffix, just add the suffix, like this:
33581          *   `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
33582          *   or even `$resource('http://example.com/resource/:resource_id.:format')`
33583          *   If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
33584          *   collapsed down to a single `.`.  If you need this sequence to appear and not collapse then you
33585          *   can escape it with `/\.`.
33586          *
33587          * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
33588          *   `actions` methods. If any of the parameter value is a function, it will be executed every time
33589          *   when a param value needs to be obtained for a request (unless the param was overridden).
33590          *
33591          *   Each key value in the parameter object is first bound to url template if present and then any
33592          *   excess keys are appended to the url search query after the `?`.
33593          *
33594          *   Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
33595          *   URL `/path/greet?salutation=Hello`.
33596          *
33597          *   If the parameter value is prefixed with `@` then the value for that parameter will be extracted
33598          *   from the corresponding property on the `data` object (provided when calling an action method).  For
33599          *   example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
33600          *   will be `data.someProp`.
33601          *
33602          * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
33603          *   the default set of resource actions. The declaration should be created in the format of {@link
33604          *   ng.$http#usage $http.config}:
33605          *
33606          *       {action1: {method:?, params:?, isArray:?, headers:?, ...},
33607          *        action2: {method:?, params:?, isArray:?, headers:?, ...},
33608          *        ...}
33609          *
33610          *   Where:
33611          *
33612          *   - **`action`** – {string} – The name of action. This name becomes the name of the method on
33613          *     your resource object.
33614          *   - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
33615          *     `DELETE`, `JSONP`, etc).
33616          *   - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
33617          *     the parameter value is a function, it will be executed every time when a param value needs to
33618          *     be obtained for a request (unless the param was overridden).
33619          *   - **`url`** – {string} – action specific `url` override. The url templating is supported just
33620          *     like for the resource-level urls.
33621          *   - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
33622          *     see `returns` section.
33623          *   - **`transformRequest`** –
33624          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33625          *     transform function or an array of such functions. The transform function takes the http
33626          *     request body and headers and returns its transformed (typically serialized) version.
33627          *     By default, transformRequest will contain one function that checks if the request data is
33628          *     an object and serializes to using `angular.toJson`. To prevent this behavior, set
33629          *     `transformRequest` to an empty array: `transformRequest: []`
33630          *   - **`transformResponse`** –
33631          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33632          *     transform function or an array of such functions. The transform function takes the http
33633          *     response body and headers and returns its transformed (typically deserialized) version.
33634          *     By default, transformResponse will contain one function that checks if the response looks like
33635          *     a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
33636          *     `transformResponse` to an empty array: `transformResponse: []`
33637          *   - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
33638          *     GET request, otherwise if a cache instance built with
33639          *     {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
33640          *     caching.
33641          *   - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
33642          *     should abort the request when resolved.
33643          *   - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
33644          *     XHR object. See
33645          *     [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
33646          *     for more information.
33647          *   - **`responseType`** - `{string}` - see
33648          *     [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
33649          *   - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
33650          *     `response` and `responseError`. Both `response` and `responseError` interceptors get called
33651          *     with `http response` object. See {@link ng.$http $http interceptors}.
33652          *
33653          * @param {Object} options Hash with custom settings that should extend the
33654          *   default `$resourceProvider` behavior.  The only supported option is
33655          *
33656          *   Where:
33657          *
33658          *   - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
33659          *   slashes from any calculated URL will be stripped. (Defaults to true.)
33660          *
33661          * @returns {Object} A resource "class" object with methods for the default set of resource actions
33662          *   optionally extended with custom `actions`. The default set contains these actions:
33663          *   ```js
33664          *   { 'get':    {method:'GET'},
33665          *     'save':   {method:'POST'},
33666          *     'query':  {method:'GET', isArray:true},
33667          *     'remove': {method:'DELETE'},
33668          *     'delete': {method:'DELETE'} };
33669          *   ```
33670          *
33671          *   Calling these methods invoke an {@link ng.$http} with the specified http method,
33672          *   destination and parameters. When the data is returned from the server then the object is an
33673          *   instance of the resource class. The actions `save`, `remove` and `delete` are available on it
33674          *   as  methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
33675          *   read, update, delete) on server-side data like this:
33676          *   ```js
33677          *   var User = $resource('/user/:userId', {userId:'@id'});
33678          *   var user = User.get({userId:123}, function() {
33679          *     user.abc = true;
33680          *     user.$save();
33681          *   });
33682          *   ```
33683          *
33684          *   It is important to realize that invoking a $resource object method immediately returns an
33685          *   empty reference (object or array depending on `isArray`). Once the data is returned from the
33686          *   server the existing reference is populated with the actual data. This is a useful trick since
33687          *   usually the resource is assigned to a model which is then rendered by the view. Having an empty
33688          *   object results in no rendering, once the data arrives from the server then the object is
33689          *   populated with the data and the view automatically re-renders itself showing the new data. This
33690          *   means that in most cases one never has to write a callback function for the action methods.
33691          *
33692          *   The action methods on the class object or instance object can be invoked with the following
33693          *   parameters:
33694          *
33695          *   - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
33696          *   - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
33697          *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
33698          *
33699          *
33700          *   Success callback is called with (value, responseHeaders) arguments, where the value is
33701          *   the populated resource instance or collection object. The error callback is called
33702          *   with (httpResponse) argument.
33703          *
33704          *   Class actions return empty instance (with additional properties below).
33705          *   Instance actions return promise of the action.
33706          *
33707          *   The Resource instances and collection have these additional properties:
33708          *
33709          *   - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
33710          *     instance or collection.
33711          *
33712          *     On success, the promise is resolved with the same resource instance or collection object,
33713          *     updated with data from server. This makes it easy to use in
33714          *     {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
33715          *     rendering until the resource(s) are loaded.
33716          *
33717          *     On failure, the promise is resolved with the {@link ng.$http http response} object, without
33718          *     the `resource` property.
33719          *
33720          *     If an interceptor object was provided, the promise will instead be resolved with the value
33721          *     returned by the interceptor.
33722          *
33723          *   - `$resolved`: `true` after first server interaction is completed (either with success or
33724          *      rejection), `false` before that. Knowing if the Resource has been resolved is useful in
33725          *      data-binding.
33726          *
33727          * @example
33728          *
33729          * # Credit card resource
33730          *
33731          * ```js
33732              // Define CreditCard class
33733              var CreditCard = $resource('/user/:userId/card/:cardId',
33734               {userId:123, cardId:'@id'}, {
33735                charge: {method:'POST', params:{charge:true}}
33736               });
33737
33738              // We can retrieve a collection from the server
33739              var cards = CreditCard.query(function() {
33740                // GET: /user/123/card
33741                // server returns: [ {id:456, number:'1234', name:'Smith'} ];
33742
33743                var card = cards[0];
33744                // each item is an instance of CreditCard
33745                expect(card instanceof CreditCard).toEqual(true);
33746                card.name = "J. Smith";
33747                // non GET methods are mapped onto the instances
33748                card.$save();
33749                // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
33750                // server returns: {id:456, number:'1234', name: 'J. Smith'};
33751
33752                // our custom method is mapped as well.
33753                card.$charge({amount:9.99});
33754                // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
33755              });
33756
33757              // we can create an instance as well
33758              var newCard = new CreditCard({number:'0123'});
33759              newCard.name = "Mike Smith";
33760              newCard.$save();
33761              // POST: /user/123/card {number:'0123', name:'Mike Smith'}
33762              // server returns: {id:789, number:'0123', name: 'Mike Smith'};
33763              expect(newCard.id).toEqual(789);
33764          * ```
33765          *
33766          * The object returned from this function execution is a resource "class" which has "static" method
33767          * for each action in the definition.
33768          *
33769          * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
33770          * `headers`.
33771          * When the data is returned from the server then the object is an instance of the resource type and
33772          * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
33773          * operations (create, read, update, delete) on server-side data.
33774
33775            ```js
33776              var User = $resource('/user/:userId', {userId:'@id'});
33777              User.get({userId:123}, function(user) {
33778                user.abc = true;
33779                user.$save();
33780              });
33781            ```
33782          *
33783          * It's worth noting that the success callback for `get`, `query` and other methods gets passed
33784          * in the response that came from the server as well as $http header getter function, so one
33785          * could rewrite the above example and get access to http headers as:
33786          *
33787            ```js
33788              var User = $resource('/user/:userId', {userId:'@id'});
33789              User.get({userId:123}, function(u, getResponseHeaders){
33790                u.abc = true;
33791                u.$save(function(u, putResponseHeaders) {
33792                  //u => saved user object
33793                  //putResponseHeaders => $http header getter
33794                });
33795              });
33796            ```
33797          *
33798          * You can also access the raw `$http` promise via the `$promise` property on the object returned
33799          *
33800            ```
33801              var User = $resource('/user/:userId', {userId:'@id'});
33802              User.get({userId:123})
33803                  .$promise.then(function(user) {
33804                    $scope.user = user;
33805                  });
33806            ```
33807
33808          * # Creating a custom 'PUT' request
33809          * In this example we create a custom method on our resource to make a PUT request
33810          * ```js
33811          *    var app = angular.module('app', ['ngResource', 'ngRoute']);
33812          *
33813          *    // Some APIs expect a PUT request in the format URL/object/ID
33814          *    // Here we are creating an 'update' method
33815          *    app.factory('Notes', ['$resource', function($resource) {
33816          *    return $resource('/notes/:id', null,
33817          *        {
33818          *            'update': { method:'PUT' }
33819          *        });
33820          *    }]);
33821          *
33822          *    // In our controller we get the ID from the URL using ngRoute and $routeParams
33823          *    // We pass in $routeParams and our Notes factory along with $scope
33824          *    app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
33825                                               function($scope, $routeParams, Notes) {
33826          *    // First get a note object from the factory
33827          *    var note = Notes.get({ id:$routeParams.id });
33828          *    $id = note.id;
33829          *
33830          *    // Now call update passing in the ID first then the object you are updating
33831          *    Notes.update({ id:$id }, note);
33832          *
33833          *    // This will PUT /notes/ID with the note object in the request payload
33834          *    }]);
33835          * ```
33836          */
33837         angular.module('ngResource', ['ng']).
33838           provider('$resource', function() {
33839             var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
33840             var provider = this;
33841
33842             this.defaults = {
33843               // Strip slashes by default
33844               stripTrailingSlashes: true,
33845
33846               // Default actions configuration
33847               actions: {
33848                 'get': {method: 'GET'},
33849                 'save': {method: 'POST'},
33850                 'query': {method: 'GET', isArray: true},
33851                 'remove': {method: 'DELETE'},
33852                 'delete': {method: 'DELETE'}
33853               }
33854             };
33855
33856             this.$get = ['$http', '$q', function($http, $q) {
33857
33858               var noop = angular.noop,
33859                 forEach = angular.forEach,
33860                 extend = angular.extend,
33861                 copy = angular.copy,
33862                 isFunction = angular.isFunction;
33863
33864               /**
33865                * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
33866                * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
33867                * (pchar) allowed in path segments:
33868                *    segment       = *pchar
33869                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33870                *    pct-encoded   = "%" HEXDIG HEXDIG
33871                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33872                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33873                *                     / "*" / "+" / "," / ";" / "="
33874                */
33875               function encodeUriSegment(val) {
33876                 return encodeUriQuery(val, true).
33877                   replace(/%26/gi, '&').
33878                   replace(/%3D/gi, '=').
33879                   replace(/%2B/gi, '+');
33880               }
33881
33882
33883               /**
33884                * This method is intended for encoding *key* or *value* parts of query component. We need a
33885                * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
33886                * have to be encoded per http://tools.ietf.org/html/rfc3986:
33887                *    query       = *( pchar / "/" / "?" )
33888                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33889                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33890                *    pct-encoded   = "%" HEXDIG HEXDIG
33891                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33892                *                     / "*" / "+" / "," / ";" / "="
33893                */
33894               function encodeUriQuery(val, pctEncodeSpaces) {
33895                 return encodeURIComponent(val).
33896                   replace(/%40/gi, '@').
33897                   replace(/%3A/gi, ':').
33898                   replace(/%24/g, '$').
33899                   replace(/%2C/gi, ',').
33900                   replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
33901               }
33902
33903               function Route(template, defaults) {
33904                 this.template = template;
33905                 this.defaults = extend({}, provider.defaults, defaults);
33906                 this.urlParams = {};
33907               }
33908
33909               Route.prototype = {
33910                 setUrlParams: function(config, params, actionUrl) {
33911                   var self = this,
33912                     url = actionUrl || self.template,
33913                     val,
33914                     encodedVal,
33915                     protocolAndDomain = '';
33916
33917                   var urlParams = self.urlParams = {};
33918                   forEach(url.split(/\W/), function(param) {
33919                     if (param === 'hasOwnProperty') {
33920                       throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
33921                     }
33922                     if (!(new RegExp("^\\d+$").test(param)) && param &&
33923                       (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
33924                       urlParams[param] = true;
33925                     }
33926                   });
33927                   url = url.replace(/\\:/g, ':');
33928                   url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
33929                     protocolAndDomain = match;
33930                     return '';
33931                   });
33932
33933                   params = params || {};
33934                   forEach(self.urlParams, function(_, urlParam) {
33935                     val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
33936                     if (angular.isDefined(val) && val !== null) {
33937                       encodedVal = encodeUriSegment(val);
33938                       url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
33939                         return encodedVal + p1;
33940                       });
33941                     } else {
33942                       url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
33943                           leadingSlashes, tail) {
33944                         if (tail.charAt(0) == '/') {
33945                           return tail;
33946                         } else {
33947                           return leadingSlashes + tail;
33948                         }
33949                       });
33950                     }
33951                   });
33952
33953                   // strip trailing slashes and set the url (unless this behavior is specifically disabled)
33954                   if (self.defaults.stripTrailingSlashes) {
33955                     url = url.replace(/\/+$/, '') || '/';
33956                   }
33957
33958                   // then replace collapse `/.` if found in the last URL path segment before the query
33959                   // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
33960                   url = url.replace(/\/\.(?=\w+($|\?))/, '.');
33961                   // replace escaped `/\.` with `/.`
33962                   config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
33963
33964
33965                   // set params - delegate param encoding to $http
33966                   forEach(params, function(value, key) {
33967                     if (!self.urlParams[key]) {
33968                       config.params = config.params || {};
33969                       config.params[key] = value;
33970                     }
33971                   });
33972                 }
33973               };
33974
33975
33976               function resourceFactory(url, paramDefaults, actions, options) {
33977                 var route = new Route(url, options);
33978
33979                 actions = extend({}, provider.defaults.actions, actions);
33980
33981                 function extractParams(data, actionParams) {
33982                   var ids = {};
33983                   actionParams = extend({}, paramDefaults, actionParams);
33984                   forEach(actionParams, function(value, key) {
33985                     if (isFunction(value)) { value = value(); }
33986                     ids[key] = value && value.charAt && value.charAt(0) == '@' ?
33987                       lookupDottedPath(data, value.substr(1)) : value;
33988                   });
33989                   return ids;
33990                 }
33991
33992                 function defaultResponseInterceptor(response) {
33993                   return response.resource;
33994                 }
33995
33996                 function Resource(value) {
33997                   shallowClearAndCopy(value || {}, this);
33998                 }
33999
34000                 Resource.prototype.toJSON = function() {
34001                   var data = extend({}, this);
34002                   delete data.$promise;
34003                   delete data.$resolved;
34004                   return data;
34005                 };
34006
34007                 forEach(actions, function(action, name) {
34008                   var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
34009
34010                   Resource[name] = function(a1, a2, a3, a4) {
34011                     var params = {}, data, success, error;
34012
34013                     /* jshint -W086 */ /* (purposefully fall through case statements) */
34014                     switch (arguments.length) {
34015                       case 4:
34016                         error = a4;
34017                         success = a3;
34018                       //fallthrough
34019                       case 3:
34020                       case 2:
34021                         if (isFunction(a2)) {
34022                           if (isFunction(a1)) {
34023                             success = a1;
34024                             error = a2;
34025                             break;
34026                           }
34027
34028                           success = a2;
34029                           error = a3;
34030                           //fallthrough
34031                         } else {
34032                           params = a1;
34033                           data = a2;
34034                           success = a3;
34035                           break;
34036                         }
34037                       case 1:
34038                         if (isFunction(a1)) success = a1;
34039                         else if (hasBody) data = a1;
34040                         else params = a1;
34041                         break;
34042                       case 0: break;
34043                       default:
34044                         throw $resourceMinErr('badargs',
34045                           "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
34046                           arguments.length);
34047                     }
34048                     /* jshint +W086 */ /* (purposefully fall through case statements) */
34049
34050                     var isInstanceCall = this instanceof Resource;
34051                     var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
34052                     var httpConfig = {};
34053                     var responseInterceptor = action.interceptor && action.interceptor.response ||
34054                       defaultResponseInterceptor;
34055                     var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
34056                       undefined;
34057
34058                     forEach(action, function(value, key) {
34059                       switch (key) {
34060                         default:
34061                           httpConfig[key] = copy(value);
34062                           break;
34063                         case 'params':
34064                         case 'isArray':
34065                         case 'interceptor':
34066                           break;
34067                         case 'timeout':
34068                           httpConfig[key] = value;
34069                           break;
34070                       }
34071                     });
34072
34073                     if (hasBody) httpConfig.data = data;
34074                     route.setUrlParams(httpConfig,
34075                       extend({}, extractParams(data, action.params || {}), params),
34076                       action.url);
34077
34078                     var promise = $http(httpConfig).then(function(response) {
34079                       var data = response.data,
34080                         promise = value.$promise;
34081
34082                       if (data) {
34083                         // Need to convert action.isArray to boolean in case it is undefined
34084                         // jshint -W018
34085                         if (angular.isArray(data) !== (!!action.isArray)) {
34086                           throw $resourceMinErr('badcfg',
34087                               'Error in resource configuration for action `{0}`. Expected response to ' +
34088                               'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
34089                             angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
34090                         }
34091                         // jshint +W018
34092                         if (action.isArray) {
34093                           value.length = 0;
34094                           forEach(data, function(item) {
34095                             if (typeof item === "object") {
34096                               value.push(new Resource(item));
34097                             } else {
34098                               // Valid JSON values may be string literals, and these should not be converted
34099                               // into objects. These items will not have access to the Resource prototype
34100                               // methods, but unfortunately there
34101                               value.push(item);
34102                             }
34103                           });
34104                         } else {
34105                           shallowClearAndCopy(data, value);
34106                           value.$promise = promise;
34107                         }
34108                       }
34109
34110                       value.$resolved = true;
34111
34112                       response.resource = value;
34113
34114                       return response;
34115                     }, function(response) {
34116                       value.$resolved = true;
34117
34118                       (error || noop)(response);
34119
34120                       return $q.reject(response);
34121                     });
34122
34123                     promise = promise.then(
34124                       function(response) {
34125                         var value = responseInterceptor(response);
34126                         (success || noop)(value, response.headers);
34127                         return value;
34128                       },
34129                       responseErrorInterceptor);
34130
34131                     if (!isInstanceCall) {
34132                       // we are creating instance / collection
34133                       // - set the initial promise
34134                       // - return the instance / collection
34135                       value.$promise = promise;
34136                       value.$resolved = false;
34137
34138                       return value;
34139                     }
34140
34141                     // instance call
34142                     return promise;
34143                   };
34144
34145
34146                   Resource.prototype['$' + name] = function(params, success, error) {
34147                     if (isFunction(params)) {
34148                       error = success; success = params; params = {};
34149                     }
34150                     var result = Resource[name].call(this, params, this, success, error);
34151                     return result.$promise || result;
34152                   };
34153                 });
34154
34155                 Resource.bind = function(additionalParamDefaults) {
34156                   return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
34157                 };
34158
34159                 return Resource;
34160               }
34161
34162               return resourceFactory;
34163             }];
34164           });
34165
34166
34167         })(window, window.angular);
34168
34169
34170 /***/ },
34171 /* 6 */
34172 /***/ function(module, exports, __webpack_require__) {
34173
34174         __webpack_require__(7);
34175
34176         module.exports = 'ui.bootstrap';
34177
34178
34179 /***/ },
34180 /* 7 */
34181 /***/ function(module, exports) {
34182
34183         /*
34184          * angular-ui-bootstrap
34185          * http://angular-ui.github.io/bootstrap/
34186
34187          * Version: 1.0.0 - 2016-01-08
34188          * License: MIT
34189          */
34190         angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
34191         angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/popup.html","uib/template/datepicker/year.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
34192         angular.module('ui.bootstrap.collapse', [])
34193
34194           .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
34195             var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
34196             return {
34197               link: function(scope, element, attrs) {
34198                 if (!scope.$eval(attrs.uibCollapse)) {
34199                   element.addClass('in')
34200                     .addClass('collapse')
34201                     .css({height: 'auto'});
34202                 }
34203
34204                 function expand() {
34205                   element.removeClass('collapse')
34206                     .addClass('collapsing')
34207                     .attr('aria-expanded', true)
34208                     .attr('aria-hidden', false);
34209
34210                   if ($animateCss) {
34211                     $animateCss(element, {
34212                       addClass: 'in',
34213                       easing: 'ease',
34214                       to: { height: element[0].scrollHeight + 'px' }
34215                     }).start()['finally'](expandDone);
34216                   } else {
34217                     $animate.addClass(element, 'in', {
34218                       to: { height: element[0].scrollHeight + 'px' }
34219                     }).then(expandDone);
34220                   }
34221                 }
34222
34223                 function expandDone() {
34224                   element.removeClass('collapsing')
34225                     .addClass('collapse')
34226                     .css({height: 'auto'});
34227                 }
34228
34229                 function collapse() {
34230                   if (!element.hasClass('collapse') && !element.hasClass('in')) {
34231                     return collapseDone();
34232                   }
34233
34234                   element
34235                     // IMPORTANT: The height must be set before adding "collapsing" class.
34236                     // Otherwise, the browser attempts to animate from height 0 (in
34237                     // collapsing class) to the given height here.
34238                     .css({height: element[0].scrollHeight + 'px'})
34239                     // initially all panel collapse have the collapse class, this removal
34240                     // prevents the animation from jumping to collapsed state
34241                     .removeClass('collapse')
34242                     .addClass('collapsing')
34243                     .attr('aria-expanded', false)
34244                     .attr('aria-hidden', true);
34245
34246                   if ($animateCss) {
34247                     $animateCss(element, {
34248                       removeClass: 'in',
34249                       to: {height: '0'}
34250                     }).start()['finally'](collapseDone);
34251                   } else {
34252                     $animate.removeClass(element, 'in', {
34253                       to: {height: '0'}
34254                     }).then(collapseDone);
34255                   }
34256                 }
34257
34258                 function collapseDone() {
34259                   element.css({height: '0'}); // Required so that collapse works when animation is disabled
34260                   element.removeClass('collapsing')
34261                     .addClass('collapse');
34262                 }
34263
34264                 scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
34265                   if (shouldCollapse) {
34266                     collapse();
34267                   } else {
34268                     expand();
34269                   }
34270                 });
34271               }
34272             };
34273           }]);
34274
34275         angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
34276
34277         .constant('uibAccordionConfig', {
34278           closeOthers: true
34279         })
34280
34281         .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
34282           // This array keeps track of the accordion groups
34283           this.groups = [];
34284
34285           // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
34286           this.closeOthers = function(openGroup) {
34287             var closeOthers = angular.isDefined($attrs.closeOthers) ?
34288               $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
34289             if (closeOthers) {
34290               angular.forEach(this.groups, function(group) {
34291                 if (group !== openGroup) {
34292                   group.isOpen = false;
34293                 }
34294               });
34295             }
34296           };
34297
34298           // This is called from the accordion-group directive to add itself to the accordion
34299           this.addGroup = function(groupScope) {
34300             var that = this;
34301             this.groups.push(groupScope);
34302
34303             groupScope.$on('$destroy', function(event) {
34304               that.removeGroup(groupScope);
34305             });
34306           };
34307
34308           // This is called from the accordion-group directive when to remove itself
34309           this.removeGroup = function(group) {
34310             var index = this.groups.indexOf(group);
34311             if (index !== -1) {
34312               this.groups.splice(index, 1);
34313             }
34314           };
34315         }])
34316
34317         // The accordion directive simply sets up the directive controller
34318         // and adds an accordion CSS class to itself element.
34319         .directive('uibAccordion', function() {
34320           return {
34321             controller: 'UibAccordionController',
34322             controllerAs: 'accordion',
34323             transclude: true,
34324             templateUrl: function(element, attrs) {
34325               return attrs.templateUrl || 'uib/template/accordion/accordion.html';
34326             }
34327           };
34328         })
34329
34330         // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
34331         .directive('uibAccordionGroup', function() {
34332           return {
34333             require: '^uibAccordion',         // We need this directive to be inside an accordion
34334             transclude: true,              // It transcludes the contents of the directive into the template
34335             replace: true,                // The element containing the directive will be replaced with the template
34336             templateUrl: function(element, attrs) {
34337               return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
34338             },
34339             scope: {
34340               heading: '@',               // Interpolate the heading attribute onto this scope
34341               isOpen: '=?',
34342               isDisabled: '=?'
34343             },
34344             controller: function() {
34345               this.setHeading = function(element) {
34346                 this.heading = element;
34347               };
34348             },
34349             link: function(scope, element, attrs, accordionCtrl) {
34350               accordionCtrl.addGroup(scope);
34351
34352               scope.openClass = attrs.openClass || 'panel-open';
34353               scope.panelClass = attrs.panelClass || 'panel-default';
34354               scope.$watch('isOpen', function(value) {
34355                 element.toggleClass(scope.openClass, !!value);
34356                 if (value) {
34357                   accordionCtrl.closeOthers(scope);
34358                 }
34359               });
34360
34361               scope.toggleOpen = function($event) {
34362                 if (!scope.isDisabled) {
34363                   if (!$event || $event.which === 32) {
34364                     scope.isOpen = !scope.isOpen;
34365                   }
34366                 }
34367               };
34368             }
34369           };
34370         })
34371
34372         // Use accordion-heading below an accordion-group to provide a heading containing HTML
34373         .directive('uibAccordionHeading', function() {
34374           return {
34375             transclude: true,   // Grab the contents to be used as the heading
34376             template: '',       // In effect remove this element!
34377             replace: true,
34378             require: '^uibAccordionGroup',
34379             link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
34380               // Pass the heading to the accordion-group controller
34381               // so that it can be transcluded into the right place in the template
34382               // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
34383               accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
34384             }
34385           };
34386         })
34387
34388         // Use in the accordion-group template to indicate where you want the heading to be transcluded
34389         // You must provide the property on the accordion-group controller that will hold the transcluded element
34390         .directive('uibAccordionTransclude', function() {
34391           return {
34392             require: '^uibAccordionGroup',
34393             link: function(scope, element, attrs, controller) {
34394               scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
34395                 if (heading) {
34396                   element.find('span').html('');
34397                   element.find('span').append(heading);
34398                 }
34399               });
34400             }
34401           };
34402         });
34403
34404         angular.module('ui.bootstrap.alert', [])
34405
34406         .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
34407           $scope.closeable = !!$attrs.close;
34408
34409           var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
34410             $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
34411
34412           if (dismissOnTimeout) {
34413             $timeout(function() {
34414               $scope.close();
34415             }, parseInt(dismissOnTimeout, 10));
34416           }
34417         }])
34418
34419         .directive('uibAlert', function() {
34420           return {
34421             controller: 'UibAlertController',
34422             controllerAs: 'alert',
34423             templateUrl: function(element, attrs) {
34424               return attrs.templateUrl || 'uib/template/alert/alert.html';
34425             },
34426             transclude: true,
34427             replace: true,
34428             scope: {
34429               type: '@',
34430               close: '&'
34431             }
34432           };
34433         });
34434
34435         angular.module('ui.bootstrap.buttons', [])
34436
34437         .constant('uibButtonConfig', {
34438           activeClass: 'active',
34439           toggleEvent: 'click'
34440         })
34441
34442         .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
34443           this.activeClass = buttonConfig.activeClass || 'active';
34444           this.toggleEvent = buttonConfig.toggleEvent || 'click';
34445         }])
34446
34447         .directive('uibBtnRadio', ['$parse', function($parse) {
34448           return {
34449             require: ['uibBtnRadio', 'ngModel'],
34450             controller: 'UibButtonsController',
34451             controllerAs: 'buttons',
34452             link: function(scope, element, attrs, ctrls) {
34453               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34454               var uncheckableExpr = $parse(attrs.uibUncheckable);
34455
34456               element.find('input').css({display: 'none'});
34457
34458               //model -> UI
34459               ngModelCtrl.$render = function() {
34460                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
34461               };
34462
34463               //ui->model
34464               element.on(buttonsCtrl.toggleEvent, function() {
34465                 if (attrs.disabled) {
34466                   return;
34467                 }
34468
34469                 var isActive = element.hasClass(buttonsCtrl.activeClass);
34470
34471                 if (!isActive || angular.isDefined(attrs.uncheckable)) {
34472                   scope.$apply(function() {
34473                     ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
34474                     ngModelCtrl.$render();
34475                   });
34476                 }
34477               });
34478
34479               if (attrs.uibUncheckable) {
34480                 scope.$watch(uncheckableExpr, function(uncheckable) {
34481                   attrs.$set('uncheckable', uncheckable ? '' : null);
34482                 });
34483               }
34484             }
34485           };
34486         }])
34487
34488         .directive('uibBtnCheckbox', function() {
34489           return {
34490             require: ['uibBtnCheckbox', 'ngModel'],
34491             controller: 'UibButtonsController',
34492             controllerAs: 'button',
34493             link: function(scope, element, attrs, ctrls) {
34494               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34495
34496               element.find('input').css({display: 'none'});
34497
34498               function getTrueValue() {
34499                 return getCheckboxValue(attrs.btnCheckboxTrue, true);
34500               }
34501
34502               function getFalseValue() {
34503                 return getCheckboxValue(attrs.btnCheckboxFalse, false);
34504               }
34505
34506               function getCheckboxValue(attribute, defaultValue) {
34507                 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
34508               }
34509
34510               //model -> UI
34511               ngModelCtrl.$render = function() {
34512                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
34513               };
34514
34515               //ui->model
34516               element.on(buttonsCtrl.toggleEvent, function() {
34517                 if (attrs.disabled) {
34518                   return;
34519                 }
34520
34521                 scope.$apply(function() {
34522                   ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
34523                   ngModelCtrl.$render();
34524                 });
34525               });
34526             }
34527           };
34528         });
34529
34530         angular.module('ui.bootstrap.carousel', [])
34531
34532         .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
34533           var self = this,
34534             slides = self.slides = $scope.slides = [],
34535             SLIDE_DIRECTION = 'uib-slideDirection',
34536             currentIndex = -1,
34537             currentInterval, isPlaying, bufferedTransitions = [];
34538           self.currentSlide = null;
34539
34540           var destroyed = false;
34541
34542           self.addSlide = function(slide, element) {
34543             slide.$element = element;
34544             slides.push(slide);
34545             //if this is the first slide or the slide is set to active, select it
34546             if (slides.length === 1 || slide.active) {
34547               if ($scope.$currentTransition) {
34548                 $scope.$currentTransition = null;
34549               }
34550
34551               self.select(slides[slides.length - 1]);
34552               if (slides.length === 1) {
34553                 $scope.play();
34554               }
34555             } else {
34556               slide.active = false;
34557             }
34558           };
34559
34560           self.getCurrentIndex = function() {
34561             if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
34562               return +self.currentSlide.index;
34563             }
34564             return currentIndex;
34565           };
34566
34567           self.next = $scope.next = function() {
34568             var newIndex = (self.getCurrentIndex() + 1) % slides.length;
34569
34570             if (newIndex === 0 && $scope.noWrap()) {
34571               $scope.pause();
34572               return;
34573             }
34574
34575             return self.select(getSlideByIndex(newIndex), 'next');
34576           };
34577
34578           self.prev = $scope.prev = function() {
34579             var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
34580
34581             if ($scope.noWrap() && newIndex === slides.length - 1) {
34582               $scope.pause();
34583               return;
34584             }
34585
34586             return self.select(getSlideByIndex(newIndex), 'prev');
34587           };
34588
34589           self.removeSlide = function(slide) {
34590             if (angular.isDefined(slide.index)) {
34591               slides.sort(function(a, b) {
34592                 return +a.index > +b.index;
34593               });
34594             }
34595
34596             var bufferedIndex = bufferedTransitions.indexOf(slide);
34597             if (bufferedIndex !== -1) {
34598               bufferedTransitions.splice(bufferedIndex, 1);
34599             }
34600             //get the index of the slide inside the carousel
34601             var index = slides.indexOf(slide);
34602             slides.splice(index, 1);
34603             $timeout(function() {
34604               if (slides.length > 0 && slide.active) {
34605                 if (index >= slides.length) {
34606                   self.select(slides[index - 1]);
34607                 } else {
34608                   self.select(slides[index]);
34609                 }
34610               } else if (currentIndex > index) {
34611                 currentIndex--;
34612               }
34613             });
34614
34615             //clean the currentSlide when no more slide
34616             if (slides.length === 0) {
34617               self.currentSlide = null;
34618               clearBufferedTransitions();
34619             }
34620           };
34621
34622           /* direction: "prev" or "next" */
34623           self.select = $scope.select = function(nextSlide, direction) {
34624             var nextIndex = $scope.indexOfSlide(nextSlide);
34625             //Decide direction if it's not given
34626             if (direction === undefined) {
34627               direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34628             }
34629             //Prevent this user-triggered transition from occurring if there is already one in progress
34630             if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
34631               goNext(nextSlide, nextIndex, direction);
34632             } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
34633               bufferedTransitions.push(nextSlide);
34634             }
34635           };
34636
34637           /* Allow outside people to call indexOf on slides array */
34638           $scope.indexOfSlide = function(slide) {
34639             return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
34640           };
34641
34642           $scope.isActive = function(slide) {
34643             return self.currentSlide === slide;
34644           };
34645
34646           $scope.pause = function() {
34647             if (!$scope.noPause) {
34648               isPlaying = false;
34649               resetTimer();
34650             }
34651           };
34652
34653           $scope.play = function() {
34654             if (!isPlaying) {
34655               isPlaying = true;
34656               restartTimer();
34657             }
34658           };
34659
34660           $scope.$on('$destroy', function() {
34661             destroyed = true;
34662             resetTimer();
34663           });
34664
34665           $scope.$watch('noTransition', function(noTransition) {
34666             $animate.enabled($element, !noTransition);
34667           });
34668
34669           $scope.$watch('interval', restartTimer);
34670
34671           $scope.$watchCollection('slides', resetTransition);
34672
34673           function clearBufferedTransitions() {
34674             while (bufferedTransitions.length) {
34675               bufferedTransitions.shift();
34676             }
34677           }
34678
34679           function getSlideByIndex(index) {
34680             if (angular.isUndefined(slides[index].index)) {
34681               return slides[index];
34682             }
34683             for (var i = 0, l = slides.length; i < l; ++i) {
34684               if (slides[i].index === index) {
34685                 return slides[i];
34686               }
34687             }
34688           }
34689
34690           function goNext(slide, index, direction) {
34691             if (destroyed) { return; }
34692
34693             angular.extend(slide, {direction: direction, active: true});
34694             angular.extend(self.currentSlide || {}, {direction: direction, active: false});
34695             if ($animate.enabled($element) && !$scope.$currentTransition &&
34696               slide.$element && self.slides.length > 1) {
34697               slide.$element.data(SLIDE_DIRECTION, slide.direction);
34698               if (self.currentSlide && self.currentSlide.$element) {
34699                 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
34700               }
34701
34702               $scope.$currentTransition = true;
34703               $animate.on('addClass', slide.$element, function(element, phase) {
34704                 if (phase === 'close') {
34705                   $scope.$currentTransition = null;
34706                   $animate.off('addClass', element);
34707                   if (bufferedTransitions.length) {
34708                     var nextSlide = bufferedTransitions.pop();
34709                     var nextIndex = $scope.indexOfSlide(nextSlide);
34710                     var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34711                     clearBufferedTransitions();
34712
34713                     goNext(nextSlide, nextIndex, nextDirection);
34714                   }
34715                 }
34716               });
34717             }
34718
34719             self.currentSlide = slide;
34720             currentIndex = index;
34721
34722             //every time you change slides, reset the timer
34723             restartTimer();
34724           }
34725
34726           function resetTimer() {
34727             if (currentInterval) {
34728               $interval.cancel(currentInterval);
34729               currentInterval = null;
34730             }
34731           }
34732
34733           function resetTransition(slides) {
34734             if (!slides.length) {
34735               $scope.$currentTransition = null;
34736               clearBufferedTransitions();
34737             }
34738           }
34739
34740           function restartTimer() {
34741             resetTimer();
34742             var interval = +$scope.interval;
34743             if (!isNaN(interval) && interval > 0) {
34744               currentInterval = $interval(timerFn, interval);
34745             }
34746           }
34747
34748           function timerFn() {
34749             var interval = +$scope.interval;
34750             if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
34751               $scope.next();
34752             } else {
34753               $scope.pause();
34754             }
34755           }
34756         }])
34757
34758         .directive('uibCarousel', function() {
34759           return {
34760             transclude: true,
34761             replace: true,
34762             controller: 'UibCarouselController',
34763             controllerAs: 'carousel',
34764             templateUrl: function(element, attrs) {
34765               return attrs.templateUrl || 'uib/template/carousel/carousel.html';
34766             },
34767             scope: {
34768               interval: '=',
34769               noTransition: '=',
34770               noPause: '=',
34771               noWrap: '&'
34772             }
34773           };
34774         })
34775
34776         .directive('uibSlide', function() {
34777           return {
34778             require: '^uibCarousel',
34779             transclude: true,
34780             replace: true,
34781             templateUrl: function(element, attrs) {
34782               return attrs.templateUrl || 'uib/template/carousel/slide.html';
34783             },
34784             scope: {
34785               active: '=?',
34786               actual: '=?',
34787               index: '=?'
34788             },
34789             link: function (scope, element, attrs, carouselCtrl) {
34790               carouselCtrl.addSlide(scope, element);
34791               //when the scope is destroyed then remove the slide from the current slides array
34792               scope.$on('$destroy', function() {
34793                 carouselCtrl.removeSlide(scope);
34794               });
34795
34796               scope.$watch('active', function(active) {
34797                 if (active) {
34798                   carouselCtrl.select(scope);
34799                 }
34800               });
34801             }
34802           };
34803         })
34804
34805         .animation('.item', ['$animateCss',
34806         function($animateCss) {
34807           var SLIDE_DIRECTION = 'uib-slideDirection';
34808
34809           function removeClass(element, className, callback) {
34810             element.removeClass(className);
34811             if (callback) {
34812               callback();
34813             }
34814           }
34815
34816           return {
34817             beforeAddClass: function(element, className, done) {
34818               if (className === 'active') {
34819                 var stopped = false;
34820                 var direction = element.data(SLIDE_DIRECTION);
34821                 var directionClass = direction === 'next' ? 'left' : 'right';
34822                 var removeClassFn = removeClass.bind(this, element,
34823                   directionClass + ' ' + direction, done);
34824                 element.addClass(direction);
34825
34826                 $animateCss(element, {addClass: directionClass})
34827                   .start()
34828                   .done(removeClassFn);
34829
34830                 return function() {
34831                   stopped = true;
34832                 };
34833               }
34834               done();
34835             },
34836             beforeRemoveClass: function (element, className, done) {
34837               if (className === 'active') {
34838                 var stopped = false;
34839                 var direction = element.data(SLIDE_DIRECTION);
34840                 var directionClass = direction === 'next' ? 'left' : 'right';
34841                 var removeClassFn = removeClass.bind(this, element, directionClass, done);
34842
34843                 $animateCss(element, {addClass: directionClass})
34844                   .start()
34845                   .done(removeClassFn);
34846
34847                 return function() {
34848                   stopped = true;
34849                 };
34850               }
34851               done();
34852             }
34853           };
34854         }]);
34855
34856         angular.module('ui.bootstrap.dateparser', [])
34857
34858         .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
34859           // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
34860           var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
34861
34862           var localeId;
34863           var formatCodeToRegex;
34864
34865           this.init = function() {
34866             localeId = $locale.id;
34867
34868             this.parsers = {};
34869
34870             formatCodeToRegex = [
34871               {
34872                 key: 'yyyy',
34873                 regex: '\\d{4}',
34874                 apply: function(value) { this.year = +value; }
34875               },
34876               {
34877                 key: 'yy',
34878                 regex: '\\d{2}',
34879                 apply: function(value) { this.year = +value + 2000; }
34880               },
34881               {
34882                 key: 'y',
34883                 regex: '\\d{1,4}',
34884                 apply: function(value) { this.year = +value; }
34885               },
34886               {
34887                 key: 'M!',
34888                 regex: '0?[1-9]|1[0-2]',
34889                 apply: function(value) { this.month = value - 1; }
34890               },
34891               {
34892                 key: 'MMMM',
34893                 regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
34894                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
34895               },
34896               {
34897                 key: 'MMM',
34898                 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
34899                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
34900               },
34901               {
34902                 key: 'MM',
34903                 regex: '0[1-9]|1[0-2]',
34904                 apply: function(value) { this.month = value - 1; }
34905               },
34906               {
34907                 key: 'M',
34908                 regex: '[1-9]|1[0-2]',
34909                 apply: function(value) { this.month = value - 1; }
34910               },
34911               {
34912                 key: 'd!',
34913                 regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
34914                 apply: function(value) { this.date = +value; }
34915               },
34916               {
34917                 key: 'dd',
34918                 regex: '[0-2][0-9]{1}|3[0-1]{1}',
34919                 apply: function(value) { this.date = +value; }
34920               },
34921               {
34922                 key: 'd',
34923                 regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
34924                 apply: function(value) { this.date = +value; }
34925               },
34926               {
34927                 key: 'EEEE',
34928                 regex: $locale.DATETIME_FORMATS.DAY.join('|')
34929               },
34930               {
34931                 key: 'EEE',
34932                 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
34933               },
34934               {
34935                 key: 'HH',
34936                 regex: '(?:0|1)[0-9]|2[0-3]',
34937                 apply: function(value) { this.hours = +value; }
34938               },
34939               {
34940                 key: 'hh',
34941                 regex: '0[0-9]|1[0-2]',
34942                 apply: function(value) { this.hours = +value; }
34943               },
34944               {
34945                 key: 'H',
34946                 regex: '1?[0-9]|2[0-3]',
34947                 apply: function(value) { this.hours = +value; }
34948               },
34949               {
34950                 key: 'h',
34951                 regex: '[0-9]|1[0-2]',
34952                 apply: function(value) { this.hours = +value; }
34953               },
34954               {
34955                 key: 'mm',
34956                 regex: '[0-5][0-9]',
34957                 apply: function(value) { this.minutes = +value; }
34958               },
34959               {
34960                 key: 'm',
34961                 regex: '[0-9]|[1-5][0-9]',
34962                 apply: function(value) { this.minutes = +value; }
34963               },
34964               {
34965                 key: 'sss',
34966                 regex: '[0-9][0-9][0-9]',
34967                 apply: function(value) { this.milliseconds = +value; }
34968               },
34969               {
34970                 key: 'ss',
34971                 regex: '[0-5][0-9]',
34972                 apply: function(value) { this.seconds = +value; }
34973               },
34974               {
34975                 key: 's',
34976                 regex: '[0-9]|[1-5][0-9]',
34977                 apply: function(value) { this.seconds = +value; }
34978               },
34979               {
34980                 key: 'a',
34981                 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
34982                 apply: function(value) {
34983                   if (this.hours === 12) {
34984                     this.hours = 0;
34985                   }
34986
34987                   if (value === 'PM') {
34988                     this.hours += 12;
34989                   }
34990                 }
34991               },
34992               {
34993                 key: 'Z',
34994                 regex: '[+-]\\d{4}',
34995                 apply: function(value) {
34996                   var matches = value.match(/([+-])(\d{2})(\d{2})/),
34997                     sign = matches[1],
34998                     hours = matches[2],
34999                     minutes = matches[3];
35000                   this.hours += toInt(sign + hours);
35001                   this.minutes += toInt(sign + minutes);
35002                 }
35003               },
35004               {
35005                 key: 'ww',
35006                 regex: '[0-4][0-9]|5[0-3]'
35007               },
35008               {
35009                 key: 'w',
35010                 regex: '[0-9]|[1-4][0-9]|5[0-3]'
35011               },
35012               {
35013                 key: 'GGGG',
35014                 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s')
35015               },
35016               {
35017                 key: 'GGG',
35018                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35019               },
35020               {
35021                 key: 'GG',
35022                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35023               },
35024               {
35025                 key: 'G',
35026                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35027               }
35028             ];
35029           };
35030
35031           this.init();
35032
35033           function createParser(format) {
35034             var map = [], regex = format.split('');
35035
35036             // check for literal values
35037             var quoteIndex = format.indexOf('\'');
35038             if (quoteIndex > -1) {
35039               var inLiteral = false;
35040               format = format.split('');
35041               for (var i = quoteIndex; i < format.length; i++) {
35042                 if (inLiteral) {
35043                   if (format[i] === '\'') {
35044                     if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
35045                       format[i+1] = '$';
35046                       regex[i+1] = '';
35047                     } else { // end of literal
35048                       regex[i] = '';
35049                       inLiteral = false;
35050                     }
35051                   }
35052                   format[i] = '$';
35053                 } else {
35054                   if (format[i] === '\'') { // start of literal
35055                     format[i] = '$';
35056                     regex[i] = '';
35057                     inLiteral = true;
35058                   }
35059                 }
35060               }
35061
35062               format = format.join('');
35063             }
35064
35065             angular.forEach(formatCodeToRegex, function(data) {
35066               var index = format.indexOf(data.key);
35067
35068               if (index > -1) {
35069                 format = format.split('');
35070
35071                 regex[index] = '(' + data.regex + ')';
35072                 format[index] = '$'; // Custom symbol to define consumed part of format
35073                 for (var i = index + 1, n = index + data.key.length; i < n; i++) {
35074                   regex[i] = '';
35075                   format[i] = '$';
35076                 }
35077                 format = format.join('');
35078
35079                 map.push({
35080                   index: index,
35081                   apply: data.apply,
35082                   matcher: data.regex
35083                 });
35084               }
35085             });
35086
35087             return {
35088               regex: new RegExp('^' + regex.join('') + '$'),
35089               map: orderByFilter(map, 'index')
35090             };
35091           }
35092
35093           this.parse = function(input, format, baseDate) {
35094             if (!angular.isString(input) || !format) {
35095               return input;
35096             }
35097
35098             format = $locale.DATETIME_FORMATS[format] || format;
35099             format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
35100
35101             if ($locale.id !== localeId) {
35102               this.init();
35103             }
35104
35105             if (!this.parsers[format]) {
35106               this.parsers[format] = createParser(format);
35107             }
35108
35109             var parser = this.parsers[format],
35110                 regex = parser.regex,
35111                 map = parser.map,
35112                 results = input.match(regex),
35113                 tzOffset = false;
35114             if (results && results.length) {
35115               var fields, dt;
35116               if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
35117                 fields = {
35118                   year: baseDate.getFullYear(),
35119                   month: baseDate.getMonth(),
35120                   date: baseDate.getDate(),
35121                   hours: baseDate.getHours(),
35122                   minutes: baseDate.getMinutes(),
35123                   seconds: baseDate.getSeconds(),
35124                   milliseconds: baseDate.getMilliseconds()
35125                 };
35126               } else {
35127                 if (baseDate) {
35128                   $log.warn('dateparser:', 'baseDate is not a valid date');
35129                 }
35130                 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
35131               }
35132
35133               for (var i = 1, n = results.length; i < n; i++) {
35134                 var mapper = map[i - 1];
35135                 if (mapper.matcher === 'Z') {
35136                   tzOffset = true;
35137                 }
35138
35139                 if (mapper.apply) {
35140                   mapper.apply.call(fields, results[i]);
35141                 }
35142               }
35143
35144               var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
35145                 Date.prototype.setFullYear;
35146               var timesetter = tzOffset ? Date.prototype.setUTCHours :
35147                 Date.prototype.setHours;
35148
35149               if (isValid(fields.year, fields.month, fields.date)) {
35150                 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
35151                   dt = new Date(baseDate);
35152                   datesetter.call(dt, fields.year, fields.month, fields.date);
35153                   timesetter.call(dt, fields.hours, fields.minutes,
35154                     fields.seconds, fields.milliseconds);
35155                 } else {
35156                   dt = new Date(0);
35157                   datesetter.call(dt, fields.year, fields.month, fields.date);
35158                   timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
35159                     fields.seconds || 0, fields.milliseconds || 0);
35160                 }
35161               }
35162
35163               return dt;
35164             }
35165           };
35166
35167           // Check if date is valid for specific month (and year for February).
35168           // Month: 0 = Jan, 1 = Feb, etc
35169           function isValid(year, month, date) {
35170             if (date < 1) {
35171               return false;
35172             }
35173
35174             if (month === 1 && date > 28) {
35175               return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
35176             }
35177
35178             if (month === 3 || month === 5 || month === 8 || month === 10) {
35179               return date < 31;
35180             }
35181
35182             return true;
35183           }
35184
35185           function toInt(str) {
35186             return parseInt(str, 10);
35187           }
35188
35189           this.toTimezone = toTimezone;
35190           this.fromTimezone = fromTimezone;
35191           this.timezoneToOffset = timezoneToOffset;
35192           this.addDateMinutes = addDateMinutes;
35193           this.convertTimezoneToLocal = convertTimezoneToLocal;
35194           
35195           function toTimezone(date, timezone) {
35196             return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
35197           }
35198
35199           function fromTimezone(date, timezone) {
35200             return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
35201           }
35202
35203           //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
35204           function timezoneToOffset(timezone, fallback) {
35205             var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
35206             return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
35207           }
35208
35209           function addDateMinutes(date, minutes) {
35210             date = new Date(date.getTime());
35211             date.setMinutes(date.getMinutes() + minutes);
35212             return date;
35213           }
35214
35215           function convertTimezoneToLocal(date, timezone, reverse) {
35216             reverse = reverse ? -1 : 1;
35217             var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
35218             return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
35219           }
35220         }]);
35221
35222         // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
35223         // at most one element.
35224         angular.module('ui.bootstrap.isClass', [])
35225         .directive('uibIsClass', [
35226                  '$animate',
35227         function ($animate) {
35228           //                    11111111          22222222
35229           var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
35230           //                    11111111           22222222
35231           var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
35232
35233           var dataPerTracked = {};
35234
35235           return {
35236             restrict: 'A',
35237             compile: function (tElement, tAttrs) {
35238               var linkedScopes = [];
35239               var instances = [];
35240               var expToData = {};
35241               var lastActivated = null;
35242               var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
35243               var onExp = onExpMatches[2];
35244               var expsStr = onExpMatches[1];
35245               var exps = expsStr.split(',');
35246
35247               return linkFn;
35248
35249               function linkFn(scope, element, attrs) {
35250                 linkedScopes.push(scope);
35251                 instances.push({
35252                   scope: scope,
35253                   element: element
35254                 });
35255
35256                 exps.forEach(function (exp, k) {
35257                   addForExp(exp, scope);
35258                 });
35259
35260                 scope.$on('$destroy', removeScope);
35261               }
35262
35263               function addForExp(exp, scope) {
35264                 var matches = exp.match(IS_REGEXP);
35265                 var clazz = scope.$eval(matches[1]);
35266                 var compareWithExp = matches[2];
35267                 var data = expToData[exp];
35268                 if (!data) {
35269                   var watchFn = function (compareWithVal) {
35270                     var newActivated = null;
35271                     instances.some(function (instance) {
35272                       var thisVal = instance.scope.$eval(onExp);
35273                       if (thisVal === compareWithVal) {
35274                         newActivated = instance;
35275                         return true;
35276                       }
35277                     });
35278                     if (data.lastActivated !== newActivated) {
35279                       if (data.lastActivated) {
35280                         $animate.removeClass(data.lastActivated.element, clazz);
35281                       }
35282                       if (newActivated) {
35283                         $animate.addClass(newActivated.element, clazz);
35284                       }
35285                       data.lastActivated = newActivated;
35286                     }
35287                   };
35288                   expToData[exp] = data = {
35289                     lastActivated: null,
35290                     scope: scope,
35291                     watchFn: watchFn,
35292                     compareWithExp: compareWithExp,
35293                     watcher: scope.$watch(compareWithExp, watchFn)
35294                   };
35295                 }
35296                 data.watchFn(scope.$eval(compareWithExp));
35297               }
35298
35299               function removeScope(e) {
35300                 var removedScope = e.targetScope;
35301                 var index = linkedScopes.indexOf(removedScope);
35302                 linkedScopes.splice(index, 1);
35303                 instances.splice(index, 1);
35304                 if (linkedScopes.length) {
35305                   var newWatchScope = linkedScopes[0];
35306                   angular.forEach(expToData, function (data) {
35307                     if (data.scope === removedScope) {
35308                       data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
35309                       data.scope = newWatchScope;
35310                     }
35311                   });
35312                 }
35313                 else {
35314                   expToData = {};
35315                 }
35316               }
35317             }
35318           };
35319         }]);
35320         angular.module('ui.bootstrap.position', [])
35321
35322         /**
35323          * A set of utility methods for working with the DOM.
35324          * It is meant to be used where we need to absolute-position elements in
35325          * relation to another element (this is the case for tooltips, popovers,
35326          * typeahead suggestions etc.).
35327          */
35328           .factory('$uibPosition', ['$document', '$window', function($document, $window) {
35329             /**
35330              * Used by scrollbarWidth() function to cache scrollbar's width.
35331              * Do not access this variable directly, use scrollbarWidth() instead.
35332              */
35333             var SCROLLBAR_WIDTH;
35334             var OVERFLOW_REGEX = {
35335               normal: /(auto|scroll)/,
35336               hidden: /(auto|scroll|hidden)/
35337             };
35338             var PLACEMENT_REGEX = {
35339               auto: /\s?auto?\s?/i,
35340               primary: /^(top|bottom|left|right)$/,
35341               secondary: /^(top|bottom|left|right|center)$/,
35342               vertical: /^(top|bottom)$/
35343             };
35344
35345             return {
35346
35347               /**
35348                * Provides a raw DOM element from a jQuery/jQLite element.
35349                *
35350                * @param {element} elem - The element to convert.
35351                *
35352                * @returns {element} A HTML element.
35353                */
35354               getRawNode: function(elem) {
35355                 return elem[0] || elem;
35356               },
35357
35358               /**
35359                * Provides a parsed number for a style property.  Strips
35360                * units and casts invalid numbers to 0.
35361                *
35362                * @param {string} value - The style value to parse.
35363                *
35364                * @returns {number} A valid number.
35365                */
35366               parseStyle: function(value) {
35367                 value = parseFloat(value);
35368                 return isFinite(value) ? value : 0;
35369               },
35370
35371               /**
35372                * Provides the closest positioned ancestor.
35373                *
35374                * @param {element} element - The element to get the offest parent for.
35375                *
35376                * @returns {element} The closest positioned ancestor.
35377                */
35378               offsetParent: function(elem) {
35379                 elem = this.getRawNode(elem);
35380
35381                 var offsetParent = elem.offsetParent || $document[0].documentElement;
35382
35383                 function isStaticPositioned(el) {
35384                   return ($window.getComputedStyle(el).position || 'static') === 'static';
35385                 }
35386
35387                 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
35388                   offsetParent = offsetParent.offsetParent;
35389                 }
35390
35391                 return offsetParent || $document[0].documentElement;
35392               },
35393
35394               /**
35395                * Provides the scrollbar width, concept from TWBS measureScrollbar()
35396                * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
35397                *
35398                * @returns {number} The width of the browser scollbar.
35399                */
35400               scrollbarWidth: function() {
35401                 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
35402                   var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
35403                   $document.find('body').append(scrollElem);
35404                   SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
35405                   SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
35406                   scrollElem.remove();
35407                 }
35408
35409                 return SCROLLBAR_WIDTH;
35410               },
35411
35412               /**
35413                * Provides the closest scrollable ancestor.
35414                * A port of the jQuery UI scrollParent method:
35415                * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
35416                *
35417                * @param {element} elem - The element to find the scroll parent of.
35418                * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
35419                *   default is false.
35420                *
35421                * @returns {element} A HTML element.
35422                */
35423               scrollParent: function(elem, includeHidden) {
35424                 elem = this.getRawNode(elem);
35425
35426                 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
35427                 var documentEl = $document[0].documentElement;
35428                 var elemStyle = $window.getComputedStyle(elem);
35429                 var excludeStatic = elemStyle.position === 'absolute';
35430                 var scrollParent = elem.parentElement || documentEl;
35431
35432                 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
35433                   return documentEl;
35434                 }
35435
35436                 while (scrollParent.parentElement && scrollParent !== documentEl) {
35437                   var spStyle = $window.getComputedStyle(scrollParent);
35438                   if (excludeStatic && spStyle.position !== 'static') {
35439                     excludeStatic = false;
35440                   }
35441
35442                   if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
35443                     break;
35444                   }
35445                   scrollParent = scrollParent.parentElement;
35446                 }
35447
35448                 return scrollParent;
35449               },
35450
35451               /**
35452                * Provides read-only equivalent of jQuery's position function:
35453                * http://api.jquery.com/position/ - distance to closest positioned
35454                * ancestor.  Does not account for margins by default like jQuery position.
35455                *
35456                * @param {element} elem - The element to caclulate the position on.
35457                * @param {boolean=} [includeMargins=false] - Should margins be accounted
35458                * for, default is false.
35459                *
35460                * @returns {object} An object with the following properties:
35461                *   <ul>
35462                *     <li>**width**: the width of the element</li>
35463                *     <li>**height**: the height of the element</li>
35464                *     <li>**top**: distance to top edge of offset parent</li>
35465                *     <li>**left**: distance to left edge of offset parent</li>
35466                *   </ul>
35467                */
35468               position: function(elem, includeMagins) {
35469                 elem = this.getRawNode(elem);
35470
35471                 var elemOffset = this.offset(elem);
35472                 if (includeMagins) {
35473                   var elemStyle = $window.getComputedStyle(elem);
35474                   elemOffset.top -= this.parseStyle(elemStyle.marginTop);
35475                   elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
35476                 }
35477                 var parent = this.offsetParent(elem);
35478                 var parentOffset = {top: 0, left: 0};
35479
35480                 if (parent !== $document[0].documentElement) {
35481                   parentOffset = this.offset(parent);
35482                   parentOffset.top += parent.clientTop - parent.scrollTop;
35483                   parentOffset.left += parent.clientLeft - parent.scrollLeft;
35484                 }
35485
35486                 return {
35487                   width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
35488                   height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
35489                   top: Math.round(elemOffset.top - parentOffset.top),
35490                   left: Math.round(elemOffset.left - parentOffset.left)
35491                 };
35492               },
35493
35494               /**
35495                * Provides read-only equivalent of jQuery's offset function:
35496                * http://api.jquery.com/offset/ - distance to viewport.  Does
35497                * not account for borders, margins, or padding on the body
35498                * element.
35499                *
35500                * @param {element} elem - The element to calculate the offset on.
35501                *
35502                * @returns {object} An object with the following properties:
35503                *   <ul>
35504                *     <li>**width**: the width of the element</li>
35505                *     <li>**height**: the height of the element</li>
35506                *     <li>**top**: distance to top edge of viewport</li>
35507                *     <li>**right**: distance to bottom edge of viewport</li>
35508                *   </ul>
35509                */
35510               offset: function(elem) {
35511                 elem = this.getRawNode(elem);
35512
35513                 var elemBCR = elem.getBoundingClientRect();
35514                 return {
35515                   width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
35516                   height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
35517                   top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
35518                   left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
35519                 };
35520               },
35521
35522               /**
35523                * Provides offset distance to the closest scrollable ancestor
35524                * or viewport.  Accounts for border and scrollbar width.
35525                *
35526                * Right and bottom dimensions represent the distance to the
35527                * respective edge of the viewport element.  If the element
35528                * edge extends beyond the viewport, a negative value will be
35529                * reported.
35530                *
35531                * @param {element} elem - The element to get the viewport offset for.
35532                * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
35533                * of the first scrollable element, default is false.
35534                * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
35535                * be accounted for, default is true.
35536                *
35537                * @returns {object} An object with the following properties:
35538                *   <ul>
35539                *     <li>**top**: distance to the top content edge of viewport element</li>
35540                *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
35541                *     <li>**left**: distance to the left content edge of viewport element</li>
35542                *     <li>**right**: distance to the right content edge of viewport element</li>
35543                *   </ul>
35544                */
35545               viewportOffset: function(elem, useDocument, includePadding) {
35546                 elem = this.getRawNode(elem);
35547                 includePadding = includePadding !== false ? true : false;
35548
35549                 var elemBCR = elem.getBoundingClientRect();
35550                 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
35551
35552                 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
35553                 var offsetParentBCR = offsetParent.getBoundingClientRect();
35554
35555                 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
35556                 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
35557                 if (offsetParent === $document[0].documentElement) {
35558                   offsetBCR.top += $window.pageYOffset;
35559                   offsetBCR.left += $window.pageXOffset;
35560                 }
35561                 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
35562                 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
35563
35564                 if (includePadding) {
35565                   var offsetParentStyle = $window.getComputedStyle(offsetParent);
35566                   offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
35567                   offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
35568                   offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
35569                   offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
35570                 }
35571
35572                 return {
35573                   top: Math.round(elemBCR.top - offsetBCR.top),
35574                   bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
35575                   left: Math.round(elemBCR.left - offsetBCR.left),
35576                   right: Math.round(offsetBCR.right - elemBCR.right)
35577                 };
35578               },
35579
35580               /**
35581                * Provides an array of placement values parsed from a placement string.
35582                * Along with the 'auto' indicator, supported placement strings are:
35583                *   <ul>
35584                *     <li>top: element on top, horizontally centered on host element.</li>
35585                *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
35586                *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
35587                *     <li>bottom: element on bottom, horizontally centered on host element.</li>
35588                *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
35589                *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
35590                *     <li>left: element on left, vertically centered on host element.</li>
35591                *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
35592                *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
35593                *     <li>right: element on right, vertically centered on host element.</li>
35594                *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
35595                *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
35596                *   </ul>
35597                * A placement string with an 'auto' indicator is expected to be
35598                * space separated from the placement, i.e: 'auto bottom-left'  If
35599                * the primary and secondary placement values do not match 'top,
35600                * bottom, left, right' then 'top' will be the primary placement and
35601                * 'center' will be the secondary placement.  If 'auto' is passed, true
35602                * will be returned as the 3rd value of the array.
35603                *
35604                * @param {string} placement - The placement string to parse.
35605                *
35606                * @returns {array} An array with the following values
35607                * <ul>
35608                *   <li>**[0]**: The primary placement.</li>
35609                *   <li>**[1]**: The secondary placement.</li>
35610                *   <li>**[2]**: If auto is passed: true, else undefined.</li>
35611                * </ul>
35612                */
35613               parsePlacement: function(placement) {
35614                 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
35615                 if (autoPlace) {
35616                   placement = placement.replace(PLACEMENT_REGEX.auto, '');
35617                 }
35618
35619                 placement = placement.split('-');
35620
35621                 placement[0] = placement[0] || 'top';
35622                 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
35623                   placement[0] = 'top';
35624                 }
35625
35626                 placement[1] = placement[1] || 'center';
35627                 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
35628                   placement[1] = 'center';
35629                 }
35630
35631                 if (autoPlace) {
35632                   placement[2] = true;
35633                 } else {
35634                   placement[2] = false;
35635                 }
35636
35637                 return placement;
35638               },
35639
35640               /**
35641                * Provides coordinates for an element to be positioned relative to
35642                * another element.  Passing 'auto' as part of the placement parameter
35643                * will enable smart placement - where the element fits. i.e:
35644                * 'auto left-top' will check to see if there is enough space to the left
35645                * of the hostElem to fit the targetElem, if not place right (same for secondary
35646                * top placement).  Available space is calculated using the viewportOffset
35647                * function.
35648                *
35649                * @param {element} hostElem - The element to position against.
35650                * @param {element} targetElem - The element to position.
35651                * @param {string=} [placement=top] - The placement for the targetElem,
35652                *   default is 'top'. 'center' is assumed as secondary placement for
35653                *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
35654                *   <ul>
35655                *     <li>top</li>
35656                *     <li>top-right</li>
35657                *     <li>top-left</li>
35658                *     <li>bottom</li>
35659                *     <li>bottom-left</li>
35660                *     <li>bottom-right</li>
35661                *     <li>left</li>
35662                *     <li>left-top</li>
35663                *     <li>left-bottom</li>
35664                *     <li>right</li>
35665                *     <li>right-top</li>
35666                *     <li>right-bottom</li>
35667                *   </ul>
35668                * @param {boolean=} [appendToBody=false] - Should the top and left values returned
35669                *   be calculated from the body element, default is false.
35670                *
35671                * @returns {object} An object with the following properties:
35672                *   <ul>
35673                *     <li>**top**: Value for targetElem top.</li>
35674                *     <li>**left**: Value for targetElem left.</li>
35675                *     <li>**placement**: The resolved placement.</li>
35676                *   </ul>
35677                */
35678               positionElements: function(hostElem, targetElem, placement, appendToBody) {
35679                 hostElem = this.getRawNode(hostElem);
35680                 targetElem = this.getRawNode(targetElem);
35681
35682                 // need to read from prop to support tests.
35683                 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
35684                 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
35685
35686                 placement = this.parsePlacement(placement);
35687
35688                 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
35689                 var targetElemPos = {top: 0, left: 0, placement: ''};
35690
35691                 if (placement[2]) {
35692                   var viewportOffset = this.viewportOffset(hostElem);
35693
35694                   var targetElemStyle = $window.getComputedStyle(targetElem);
35695                   var adjustedSize = {
35696                     width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
35697                     height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
35698                   };
35699
35700                   placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
35701                                  placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
35702                                  placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
35703                                  placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
35704                                  placement[0];
35705
35706                   placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
35707                                  placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
35708                                  placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
35709                                  placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
35710                                  placement[1];
35711
35712                   if (placement[1] === 'center') {
35713                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35714                       var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
35715                       if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
35716                         placement[1] = 'left';
35717                       } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
35718                         placement[1] = 'right';
35719                       }
35720                     } else {
35721                       var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
35722                       if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
35723                         placement[1] = 'top';
35724                       } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
35725                         placement[1] = 'bottom';
35726                       }
35727                     }
35728                   }
35729                 }
35730
35731                 switch (placement[0]) {
35732                   case 'top':
35733                     targetElemPos.top = hostElemPos.top - targetHeight;
35734                     break;
35735                   case 'bottom':
35736                     targetElemPos.top = hostElemPos.top + hostElemPos.height;
35737                     break;
35738                   case 'left':
35739                     targetElemPos.left = hostElemPos.left - targetWidth;
35740                     break;
35741                   case 'right':
35742                     targetElemPos.left = hostElemPos.left + hostElemPos.width;
35743                     break;
35744                 }
35745
35746                 switch (placement[1]) {
35747                   case 'top':
35748                     targetElemPos.top = hostElemPos.top;
35749                     break;
35750                   case 'bottom':
35751                     targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
35752                     break;
35753                   case 'left':
35754                     targetElemPos.left = hostElemPos.left;
35755                     break;
35756                   case 'right':
35757                     targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
35758                     break;
35759                   case 'center':
35760                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35761                       targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
35762                     } else {
35763                       targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
35764                     }
35765                     break;
35766                 }
35767
35768                 targetElemPos.top = Math.round(targetElemPos.top);
35769                 targetElemPos.left = Math.round(targetElemPos.left);
35770                 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
35771
35772                 return targetElemPos;
35773               },
35774
35775               /**
35776               * Provides a way for positioning tooltip & dropdown
35777               * arrows when using placement options beyond the standard
35778               * left, right, top, or bottom.
35779               *
35780               * @param {element} elem - The tooltip/dropdown element.
35781               * @param {string} placement - The placement for the elem.
35782               */
35783               positionArrow: function(elem, placement) {
35784                 elem = this.getRawNode(elem);
35785
35786                 var isTooltip = true;
35787
35788                 var innerElem = elem.querySelector('.tooltip-inner');
35789                 if (!innerElem) {
35790                   isTooltip = false;
35791                   innerElem = elem.querySelector('.popover-inner');
35792                 }
35793                 if (!innerElem) {
35794                   return;
35795                 }
35796
35797                 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
35798                 if (!arrowElem) {
35799                   return;
35800                 }
35801
35802                 placement = this.parsePlacement(placement);
35803                 if (placement[1] === 'center') {
35804                   // no adjustment necessary - just reset styles
35805                   angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
35806                   return;
35807                 }
35808
35809                 var borderProp = 'border-' + placement[0] + '-width';
35810                 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
35811
35812                 var borderRadiusProp = 'border-';
35813                 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35814                   borderRadiusProp += placement[0] + '-' + placement[1];
35815                 } else {
35816                   borderRadiusProp += placement[1] + '-' + placement[0];
35817                 }
35818                 borderRadiusProp += '-radius';
35819                 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
35820
35821                 var arrowCss = {
35822                   top: 'auto',
35823                   bottom: 'auto',
35824                   left: 'auto',
35825                   right: 'auto',
35826                   margin: 0
35827                 };
35828
35829                 switch (placement[0]) {
35830                   case 'top':
35831                     arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
35832                     break;
35833                   case 'bottom':
35834                     arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
35835                     break;
35836                   case 'left':
35837                     arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
35838                     break;
35839                   case 'right':
35840                     arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
35841                     break;
35842                 }
35843
35844                 arrowCss[placement[1]] = borderRadius;
35845
35846                 angular.element(arrowElem).css(arrowCss);
35847               }
35848             };
35849           }]);
35850
35851         angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position'])
35852
35853         .value('$datepickerSuppressError', false)
35854
35855         .constant('uibDatepickerConfig', {
35856           formatDay: 'dd',
35857           formatMonth: 'MMMM',
35858           formatYear: 'yyyy',
35859           formatDayHeader: 'EEE',
35860           formatDayTitle: 'MMMM yyyy',
35861           formatMonthTitle: 'yyyy',
35862           datepickerMode: 'day',
35863           minMode: 'day',
35864           maxMode: 'year',
35865           showWeeks: true,
35866           startingDay: 0,
35867           yearRows: 4,
35868           yearColumns: 5,
35869           minDate: null,
35870           maxDate: null,
35871           shortcutPropagation: false,
35872           ngModelOptions: {}
35873         })
35874
35875         .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser',
35876           function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) {
35877           var self = this,
35878               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
35879               ngModelOptions = {};
35880
35881           // Modes chain
35882           this.modes = ['day', 'month', 'year'];
35883
35884           // Interpolated configuration attributes
35885           angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) {
35886             self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key];
35887           });
35888
35889           // Evaled configuration attributes
35890           angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) {
35891             self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key];
35892           });
35893
35894           // Watchable date attributes
35895           angular.forEach(['minDate', 'maxDate'], function(key) {
35896             if ($attrs[key]) {
35897               $scope.$parent.$watch($attrs[key], function(value) {
35898                 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null;
35899                 self.refreshView();
35900               });
35901             } else {
35902               self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null;
35903             }
35904           });
35905
35906           angular.forEach(['minMode', 'maxMode'], function(key) {
35907             if ($attrs[key]) {
35908               $scope.$parent.$watch($attrs[key], function(value) {
35909                 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key];
35910                 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) ||
35911                   key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) {
35912                   $scope.datepickerMode = self[key];
35913                 }
35914               });
35915             } else {
35916               self[key] = $scope[key] = datepickerConfig[key] || null;
35917             }
35918           });
35919
35920           $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
35921           $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
35922
35923           if (angular.isDefined($attrs.initDate)) {
35924             this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date();
35925             $scope.$parent.$watch($attrs.initDate, function(initDate) {
35926               if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
35927                 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
35928                 self.refreshView();
35929               }
35930             });
35931           } else {
35932             this.activeDate = new Date();
35933           }
35934
35935           $scope.disabled = angular.isDefined($attrs.disabled) || false;
35936           if (angular.isDefined($attrs.ngDisabled)) {
35937             $scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
35938               $scope.disabled = disabled;
35939               self.refreshView();
35940             });
35941           }
35942
35943           $scope.isActive = function(dateObject) {
35944             if (self.compare(dateObject.date, self.activeDate) === 0) {
35945               $scope.activeDateId = dateObject.uid;
35946               return true;
35947             }
35948             return false;
35949           };
35950
35951           this.init = function(ngModelCtrl_) {
35952             ngModelCtrl = ngModelCtrl_;
35953             ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
35954
35955             if (ngModelCtrl.$modelValue) {
35956               this.activeDate = ngModelCtrl.$modelValue;
35957             }
35958
35959             ngModelCtrl.$render = function() {
35960               self.render();
35961             };
35962           };
35963
35964           this.render = function() {
35965             if (ngModelCtrl.$viewValue) {
35966               var date = new Date(ngModelCtrl.$viewValue),
35967                   isValid = !isNaN(date);
35968
35969               if (isValid) {
35970                 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
35971               } else if (!$datepickerSuppressError) {
35972                 $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
35973               }
35974             }
35975             this.refreshView();
35976           };
35977
35978           this.refreshView = function() {
35979             if (this.element) {
35980               $scope.selectedDt = null;
35981               this._refreshView();
35982               if ($scope.activeDt) {
35983                 $scope.activeDateId = $scope.activeDt.uid;
35984               }
35985
35986               var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35987               date = dateParser.fromTimezone(date, ngModelOptions.timezone);
35988               ngModelCtrl.$setValidity('dateDisabled', !date ||
35989                 this.element && !this.isDisabled(date));
35990             }
35991           };
35992
35993           this.createDateObject = function(date, format) {
35994             var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35995             model = dateParser.fromTimezone(model, ngModelOptions.timezone);
35996             var dt = {
35997               date: date,
35998               label: dateFilter(date, format),
35999               selected: model && this.compare(date, model) === 0,
36000               disabled: this.isDisabled(date),
36001               current: this.compare(date, new Date()) === 0,
36002               customClass: this.customClass(date) || null
36003             };
36004
36005             if (model && this.compare(date, model) === 0) {
36006               $scope.selectedDt = dt;
36007             }
36008
36009             if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
36010               $scope.activeDt = dt;
36011             }
36012
36013             return dt;
36014           };
36015
36016           this.isDisabled = function(date) {
36017             return $scope.disabled ||
36018               this.minDate && this.compare(date, this.minDate) < 0 ||
36019               this.maxDate && this.compare(date, this.maxDate) > 0 ||
36020               $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
36021           };
36022
36023           this.customClass = function(date) {
36024             return $scope.customClass({date: date, mode: $scope.datepickerMode});
36025           };
36026
36027           // Split array into smaller arrays
36028           this.split = function(arr, size) {
36029             var arrays = [];
36030             while (arr.length > 0) {
36031               arrays.push(arr.splice(0, size));
36032             }
36033             return arrays;
36034           };
36035
36036           $scope.select = function(date) {
36037             if ($scope.datepickerMode === self.minMode) {
36038               var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
36039               dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
36040               dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
36041               ngModelCtrl.$setViewValue(dt);
36042               ngModelCtrl.$render();
36043             } else {
36044               self.activeDate = date;
36045               $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
36046             }
36047           };
36048
36049           $scope.move = function(direction) {
36050             var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
36051                 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
36052             self.activeDate.setFullYear(year, month, 1);
36053             self.refreshView();
36054           };
36055
36056           $scope.toggleMode = function(direction) {
36057             direction = direction || 1;
36058
36059             if ($scope.datepickerMode === self.maxMode && direction === 1 ||
36060               $scope.datepickerMode === self.minMode && direction === -1) {
36061               return;
36062             }
36063
36064             $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
36065           };
36066
36067           // Key event mapper
36068           $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
36069
36070           var focusElement = function() {
36071             self.element[0].focus();
36072           };
36073
36074           // Listen for focus requests from popup directive
36075           $scope.$on('uib:datepicker.focus', focusElement);
36076
36077           $scope.keydown = function(evt) {
36078             var key = $scope.keys[evt.which];
36079
36080             if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
36081               return;
36082             }
36083
36084             evt.preventDefault();
36085             if (!self.shortcutPropagation) {
36086               evt.stopPropagation();
36087             }
36088
36089             if (key === 'enter' || key === 'space') {
36090               if (self.isDisabled(self.activeDate)) {
36091                 return; // do nothing
36092               }
36093               $scope.select(self.activeDate);
36094             } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
36095               $scope.toggleMode(key === 'up' ? 1 : -1);
36096             } else {
36097               self.handleKeyDown(key, evt);
36098               self.refreshView();
36099             }
36100           };
36101         }])
36102
36103         .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36104           var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
36105
36106           this.step = { months: 1 };
36107           this.element = $element;
36108           function getDaysInMonth(year, month) {
36109             return month === 1 && year % 4 === 0 &&
36110               (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
36111           }
36112
36113           this.init = function(ctrl) {
36114             angular.extend(ctrl, this);
36115             scope.showWeeks = ctrl.showWeeks;
36116             ctrl.refreshView();
36117           };
36118
36119           this.getDates = function(startDate, n) {
36120             var dates = new Array(n), current = new Date(startDate), i = 0, date;
36121             while (i < n) {
36122               date = new Date(current);
36123               dates[i++] = date;
36124               current.setDate(current.getDate() + 1);
36125             }
36126             return dates;
36127           };
36128
36129           this._refreshView = function() {
36130             var year = this.activeDate.getFullYear(),
36131               month = this.activeDate.getMonth(),
36132               firstDayOfMonth = new Date(this.activeDate);
36133
36134             firstDayOfMonth.setFullYear(year, month, 1);
36135
36136             var difference = this.startingDay - firstDayOfMonth.getDay(),
36137               numDisplayedFromPreviousMonth = difference > 0 ?
36138                 7 - difference : - difference,
36139               firstDate = new Date(firstDayOfMonth);
36140
36141             if (numDisplayedFromPreviousMonth > 0) {
36142               firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
36143             }
36144
36145             // 42 is the number of days on a six-week calendar
36146             var days = this.getDates(firstDate, 42);
36147             for (var i = 0; i < 42; i ++) {
36148               days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
36149                 secondary: days[i].getMonth() !== month,
36150                 uid: scope.uniqueId + '-' + i
36151               });
36152             }
36153
36154             scope.labels = new Array(7);
36155             for (var j = 0; j < 7; j++) {
36156               scope.labels[j] = {
36157                 abbr: dateFilter(days[j].date, this.formatDayHeader),
36158                 full: dateFilter(days[j].date, 'EEEE')
36159               };
36160             }
36161
36162             scope.title = dateFilter(this.activeDate, this.formatDayTitle);
36163             scope.rows = this.split(days, 7);
36164
36165             if (scope.showWeeks) {
36166               scope.weekNumbers = [];
36167               var thursdayIndex = (4 + 7 - this.startingDay) % 7,
36168                   numWeeks = scope.rows.length;
36169               for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
36170                 scope.weekNumbers.push(
36171                   getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
36172               }
36173             }
36174           };
36175
36176           this.compare = function(date1, date2) {
36177             var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
36178             var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36179             _date1.setFullYear(date1.getFullYear());
36180             _date2.setFullYear(date2.getFullYear());
36181             return _date1 - _date2;
36182           };
36183
36184           function getISO8601WeekNumber(date) {
36185             var checkDate = new Date(date);
36186             checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
36187             var time = checkDate.getTime();
36188             checkDate.setMonth(0); // Compare with Jan 1
36189             checkDate.setDate(1);
36190             return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
36191           }
36192
36193           this.handleKeyDown = function(key, evt) {
36194             var date = this.activeDate.getDate();
36195
36196             if (key === 'left') {
36197               date = date - 1;
36198             } else if (key === 'up') {
36199               date = date - 7;
36200             } else if (key === 'right') {
36201               date = date + 1;
36202             } else if (key === 'down') {
36203               date = date + 7;
36204             } else if (key === 'pageup' || key === 'pagedown') {
36205               var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
36206               this.activeDate.setMonth(month, 1);
36207               date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
36208             } else if (key === 'home') {
36209               date = 1;
36210             } else if (key === 'end') {
36211               date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
36212             }
36213             this.activeDate.setDate(date);
36214           };
36215         }])
36216
36217         .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36218           this.step = { years: 1 };
36219           this.element = $element;
36220
36221           this.init = function(ctrl) {
36222             angular.extend(ctrl, this);
36223             ctrl.refreshView();
36224           };
36225
36226           this._refreshView = function() {
36227             var months = new Array(12),
36228                 year = this.activeDate.getFullYear(),
36229                 date;
36230
36231             for (var i = 0; i < 12; i++) {
36232               date = new Date(this.activeDate);
36233               date.setFullYear(year, i, 1);
36234               months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
36235                 uid: scope.uniqueId + '-' + i
36236               });
36237             }
36238
36239             scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
36240             scope.rows = this.split(months, 3);
36241           };
36242
36243           this.compare = function(date1, date2) {
36244             var _date1 = new Date(date1.getFullYear(), date1.getMonth());
36245             var _date2 = new Date(date2.getFullYear(), date2.getMonth());
36246             _date1.setFullYear(date1.getFullYear());
36247             _date2.setFullYear(date2.getFullYear());
36248             return _date1 - _date2;
36249           };
36250
36251           this.handleKeyDown = function(key, evt) {
36252             var date = this.activeDate.getMonth();
36253
36254             if (key === 'left') {
36255               date = date - 1;
36256             } else if (key === 'up') {
36257               date = date - 3;
36258             } else if (key === 'right') {
36259               date = date + 1;
36260             } else if (key === 'down') {
36261               date = date + 3;
36262             } else if (key === 'pageup' || key === 'pagedown') {
36263               var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
36264               this.activeDate.setFullYear(year);
36265             } else if (key === 'home') {
36266               date = 0;
36267             } else if (key === 'end') {
36268               date = 11;
36269             }
36270             this.activeDate.setMonth(date);
36271           };
36272         }])
36273
36274         .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36275           var columns, range;
36276           this.element = $element;
36277
36278           function getStartingYear(year) {
36279             return parseInt((year - 1) / range, 10) * range + 1;
36280           }
36281
36282           this.yearpickerInit = function() {
36283             columns = this.yearColumns;
36284             range = this.yearRows * columns;
36285             this.step = { years: range };
36286           };
36287
36288           this._refreshView = function() {
36289             var years = new Array(range), date;
36290
36291             for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
36292               date = new Date(this.activeDate);
36293               date.setFullYear(start + i, 0, 1);
36294               years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
36295                 uid: scope.uniqueId + '-' + i
36296               });
36297             }
36298
36299             scope.title = [years[0].label, years[range - 1].label].join(' - ');
36300             scope.rows = this.split(years, columns);
36301             scope.columns = columns;
36302           };
36303
36304           this.compare = function(date1, date2) {
36305             return date1.getFullYear() - date2.getFullYear();
36306           };
36307
36308           this.handleKeyDown = function(key, evt) {
36309             var date = this.activeDate.getFullYear();
36310
36311             if (key === 'left') {
36312               date = date - 1;
36313             } else if (key === 'up') {
36314               date = date - columns;
36315             } else if (key === 'right') {
36316               date = date + 1;
36317             } else if (key === 'down') {
36318               date = date + columns;
36319             } else if (key === 'pageup' || key === 'pagedown') {
36320               date += (key === 'pageup' ? - 1 : 1) * range;
36321             } else if (key === 'home') {
36322               date = getStartingYear(this.activeDate.getFullYear());
36323             } else if (key === 'end') {
36324               date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
36325             }
36326             this.activeDate.setFullYear(date);
36327           };
36328         }])
36329
36330         .directive('uibDatepicker', function() {
36331           return {
36332             replace: true,
36333             templateUrl: function(element, attrs) {
36334               return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
36335             },
36336             scope: {
36337               datepickerMode: '=?',
36338               dateDisabled: '&',
36339               customClass: '&',
36340               shortcutPropagation: '&?'
36341             },
36342             require: ['uibDatepicker', '^ngModel'],
36343             controller: 'UibDatepickerController',
36344             controllerAs: 'datepicker',
36345             link: function(scope, element, attrs, ctrls) {
36346               var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
36347
36348               datepickerCtrl.init(ngModelCtrl);
36349             }
36350           };
36351         })
36352
36353         .directive('uibDaypicker', function() {
36354           return {
36355             replace: true,
36356             templateUrl: function(element, attrs) {
36357               return attrs.templateUrl || 'uib/template/datepicker/day.html';
36358             },
36359             require: ['^uibDatepicker', 'uibDaypicker'],
36360             controller: 'UibDaypickerController',
36361             link: function(scope, element, attrs, ctrls) {
36362               var datepickerCtrl = ctrls[0],
36363                 daypickerCtrl = ctrls[1];
36364
36365               daypickerCtrl.init(datepickerCtrl);
36366             }
36367           };
36368         })
36369
36370         .directive('uibMonthpicker', function() {
36371           return {
36372             replace: true,
36373             templateUrl: function(element, attrs) {
36374               return attrs.templateUrl || 'uib/template/datepicker/month.html';
36375             },
36376             require: ['^uibDatepicker', 'uibMonthpicker'],
36377             controller: 'UibMonthpickerController',
36378             link: function(scope, element, attrs, ctrls) {
36379               var datepickerCtrl = ctrls[0],
36380                 monthpickerCtrl = ctrls[1];
36381
36382               monthpickerCtrl.init(datepickerCtrl);
36383             }
36384           };
36385         })
36386
36387         .directive('uibYearpicker', function() {
36388           return {
36389             replace: true,
36390             templateUrl: function(element, attrs) {
36391               return attrs.templateUrl || 'uib/template/datepicker/year.html';
36392             },
36393             require: ['^uibDatepicker', 'uibYearpicker'],
36394             controller: 'UibYearpickerController',
36395             link: function(scope, element, attrs, ctrls) {
36396               var ctrl = ctrls[0];
36397               angular.extend(ctrl, ctrls[1]);
36398               ctrl.yearpickerInit();
36399
36400               ctrl.refreshView();
36401             }
36402           };
36403         })
36404
36405         .constant('uibDatepickerPopupConfig', {
36406           datepickerPopup: 'yyyy-MM-dd',
36407           datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html',
36408           datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
36409           html5Types: {
36410             date: 'yyyy-MM-dd',
36411             'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
36412             'month': 'yyyy-MM'
36413           },
36414           currentText: 'Today',
36415           clearText: 'Clear',
36416           closeText: 'Done',
36417           closeOnDateSelection: true,
36418           appendToBody: false,
36419           showButtonBar: true,
36420           onOpenFocus: true,
36421           altInputFormats: []
36422         })
36423
36424         .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig',
36425         function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) {
36426           var self = this;
36427           var cache = {},
36428             isHtml5DateInput = false;
36429           var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
36430             datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
36431             ngModel, ngModelOptions, $popup, altInputFormats;
36432
36433           scope.watchData = {};
36434
36435           this.init = function(_ngModel_) {
36436             ngModel = _ngModel_;
36437             ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions;
36438             closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
36439             appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
36440             onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
36441             datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
36442             datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
36443             altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
36444
36445             scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
36446
36447             if (datepickerPopupConfig.html5Types[attrs.type]) {
36448               dateFormat = datepickerPopupConfig.html5Types[attrs.type];
36449               isHtml5DateInput = true;
36450             } else {
36451               dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
36452               attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
36453                   var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
36454                   // Invalidate the $modelValue to ensure that formatters re-run
36455                   // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
36456                   if (newDateFormat !== dateFormat) {
36457                     dateFormat = newDateFormat;
36458                     ngModel.$modelValue = null;
36459
36460                     if (!dateFormat) {
36461                       throw new Error('uibDatepickerPopup must have a date format specified.');
36462                     }
36463                   }
36464               });
36465             }
36466
36467             if (!dateFormat) {
36468               throw new Error('uibDatepickerPopup must have a date format specified.');
36469             }
36470
36471             if (isHtml5DateInput && attrs.uibDatepickerPopup) {
36472               throw new Error('HTML5 date input types do not support custom formats.');
36473             }
36474
36475             // popup element used to display calendar
36476             popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
36477             scope.ngModelOptions = angular.copy(ngModelOptions);
36478             scope.ngModelOptions.timezone = null;
36479             popupEl.attr({
36480               'ng-model': 'date',
36481               'ng-model-options': 'ngModelOptions',
36482               'ng-change': 'dateSelection(date)',
36483               'template-url': datepickerPopupTemplateUrl
36484             });
36485
36486             // datepicker element
36487             datepickerEl = angular.element(popupEl.children()[0]);
36488             datepickerEl.attr('template-url', datepickerTemplateUrl);
36489
36490             if (isHtml5DateInput) {
36491               if (attrs.type === 'month') {
36492                 datepickerEl.attr('datepicker-mode', '"month"');
36493                 datepickerEl.attr('min-mode', 'month');
36494               }
36495             }
36496
36497             if (attrs.datepickerOptions) {
36498               var options = scope.$parent.$eval(attrs.datepickerOptions);
36499               if (options && options.initDate) {
36500                 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone);
36501                 datepickerEl.attr('init-date', 'initDate');
36502                 delete options.initDate;
36503               }
36504               angular.forEach(options, function(value, option) {
36505                 datepickerEl.attr(cameltoDash(option), value);
36506               });
36507             }
36508
36509             angular.forEach(['minMode', 'maxMode'], function(key) {
36510               if (attrs[key]) {
36511                 scope.$parent.$watch(function() { return attrs[key]; }, function(value) {
36512                   scope.watchData[key] = value;
36513                 });
36514                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36515               }
36516             });
36517
36518             angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) {
36519               if (attrs[key]) {
36520                 var getAttribute = $parse(attrs[key]);
36521                 var propConfig = {
36522                   get: function() {
36523                     return getAttribute(scope.$parent);
36524                   }
36525                 };
36526
36527                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36528
36529                 // Propagate changes from datepicker to outside
36530                 if (key === 'datepickerMode') {
36531                   var setAttribute = getAttribute.assign;
36532                   propConfig.set = function(v) {
36533                     setAttribute(scope.$parent, v);
36534                   };
36535                 }
36536
36537                 Object.defineProperty(scope.watchData, key, propConfig);
36538               }
36539             });
36540
36541             angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) {
36542               if (attrs[key]) {
36543                 var getAttribute = $parse(attrs[key]);
36544
36545                 scope.$parent.$watch(getAttribute, function(value) {
36546                   if (key === 'minDate' || key === 'maxDate') {
36547                     cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium'));
36548                   }
36549
36550                   scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
36551                 });
36552
36553                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36554               }
36555             });
36556
36557             if (attrs.dateDisabled) {
36558               datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
36559             }
36560
36561             angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) {
36562               if (angular.isDefined(attrs[key])) {
36563                 datepickerEl.attr(cameltoDash(key), attrs[key]);
36564               }
36565             });
36566
36567             if (attrs.customClass) {
36568               datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
36569             }
36570
36571             if (!isHtml5DateInput) {
36572               // Internal API to maintain the correct ng-invalid-[key] class
36573               ngModel.$$parserName = 'date';
36574               ngModel.$validators.date = validator;
36575               ngModel.$parsers.unshift(parseDate);
36576               ngModel.$formatters.push(function(value) {
36577                 if (ngModel.$isEmpty(value)) {
36578                   scope.date = value;
36579                   return value;
36580                 }
36581                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36582                 return dateFilter(scope.date, dateFormat);
36583               });
36584             } else {
36585               ngModel.$formatters.push(function(value) {
36586                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36587                 return value;
36588               });
36589             }
36590
36591             // Detect changes in the view from the text box
36592             ngModel.$viewChangeListeners.push(function() {
36593               scope.date = parseDateString(ngModel.$viewValue);
36594             });
36595
36596             element.bind('keydown', inputKeydownBind);
36597
36598             $popup = $compile(popupEl)(scope);
36599             // Prevent jQuery cache memory leak (template is now redundant after linking)
36600             popupEl.remove();
36601
36602             if (appendToBody) {
36603               $document.find('body').append($popup);
36604             } else {
36605               element.after($popup);
36606             }
36607
36608             scope.$on('$destroy', function() {
36609               if (scope.isOpen === true) {
36610                 if (!$rootScope.$$phase) {
36611                   scope.$apply(function() {
36612                     scope.isOpen = false;
36613                   });
36614                 }
36615               }
36616
36617               $popup.remove();
36618               element.unbind('keydown', inputKeydownBind);
36619               $document.unbind('click', documentClickBind);
36620             });
36621           };
36622
36623           scope.getText = function(key) {
36624             return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
36625           };
36626
36627           scope.isDisabled = function(date) {
36628             if (date === 'today') {
36629               date = new Date();
36630             }
36631
36632             return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 ||
36633               scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0;
36634           };
36635
36636           scope.compare = function(date1, date2) {
36637             return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36638           };
36639
36640           // Inner change
36641           scope.dateSelection = function(dt) {
36642             if (angular.isDefined(dt)) {
36643               scope.date = dt;
36644             }
36645             var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
36646             element.val(date);
36647             ngModel.$setViewValue(date);
36648
36649             if (closeOnDateSelection) {
36650               scope.isOpen = false;
36651               element[0].focus();
36652             }
36653           };
36654
36655           scope.keydown = function(evt) {
36656             if (evt.which === 27) {
36657               evt.stopPropagation();
36658               scope.isOpen = false;
36659               element[0].focus();
36660             }
36661           };
36662
36663           scope.select = function(date) {
36664             if (date === 'today') {
36665               var today = new Date();
36666               if (angular.isDate(scope.date)) {
36667                 date = new Date(scope.date);
36668                 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
36669               } else {
36670                 date = new Date(today.setHours(0, 0, 0, 0));
36671               }
36672             }
36673             scope.dateSelection(date);
36674           };
36675
36676           scope.close = function() {
36677             scope.isOpen = false;
36678             element[0].focus();
36679           };
36680
36681           scope.disabled = angular.isDefined(attrs.disabled) || false;
36682           if (attrs.ngDisabled) {
36683             scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) {
36684               scope.disabled = disabled;
36685             });
36686           }
36687
36688           scope.$watch('isOpen', function(value) {
36689             if (value) {
36690               if (!scope.disabled) {
36691                 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
36692                 scope.position.top = scope.position.top + element.prop('offsetHeight');
36693
36694                 $timeout(function() {
36695                   if (onOpenFocus) {
36696                     scope.$broadcast('uib:datepicker.focus');
36697                   }
36698                   $document.bind('click', documentClickBind);
36699                 }, 0, false);
36700               } else {
36701                 scope.isOpen = false;
36702               }
36703             } else {
36704               $document.unbind('click', documentClickBind);
36705             }
36706           });
36707
36708           function cameltoDash(string) {
36709             return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
36710           }
36711
36712           function parseDateString(viewValue) {
36713             var date = dateParser.parse(viewValue, dateFormat, scope.date);
36714             if (isNaN(date)) {
36715               for (var i = 0; i < altInputFormats.length; i++) {
36716                 date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
36717                 if (!isNaN(date)) {
36718                   return date;
36719                 }
36720               }
36721             }
36722             return date;
36723           }
36724
36725           function parseDate(viewValue) {
36726             if (angular.isNumber(viewValue)) {
36727               // presumably timestamp to date object
36728               viewValue = new Date(viewValue);
36729             }
36730
36731             if (!viewValue) {
36732               return null;
36733             }
36734
36735             if (angular.isDate(viewValue) && !isNaN(viewValue)) {
36736               return viewValue;
36737             }
36738
36739             if (angular.isString(viewValue)) {
36740               var date = parseDateString(viewValue);
36741               if (!isNaN(date)) {
36742                 return dateParser.toTimezone(date, ngModelOptions.timezone);
36743               }
36744             }
36745
36746             return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
36747           }
36748
36749           function validator(modelValue, viewValue) {
36750             var value = modelValue || viewValue;
36751
36752             if (!attrs.ngRequired && !value) {
36753               return true;
36754             }
36755
36756             if (angular.isNumber(value)) {
36757               value = new Date(value);
36758             }
36759
36760             if (!value) {
36761               return true;
36762             }
36763
36764             if (angular.isDate(value) && !isNaN(value)) {
36765               return true;
36766             }
36767
36768             if (angular.isString(value)) {
36769               return !isNaN(parseDateString(viewValue));
36770             }
36771
36772             return false;
36773           }
36774
36775           function documentClickBind(event) {
36776             if (!scope.isOpen && scope.disabled) {
36777               return;
36778             }
36779
36780             var popup = $popup[0];
36781             var dpContainsTarget = element[0].contains(event.target);
36782             // The popup node may not be an element node
36783             // In some browsers (IE) only element nodes have the 'contains' function
36784             var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
36785             if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
36786               scope.$apply(function() {
36787                 scope.isOpen = false;
36788               });
36789             }
36790           }
36791
36792           function inputKeydownBind(evt) {
36793             if (evt.which === 27 && scope.isOpen) {
36794               evt.preventDefault();
36795               evt.stopPropagation();
36796               scope.$apply(function() {
36797                 scope.isOpen = false;
36798               });
36799               element[0].focus();
36800             } else if (evt.which === 40 && !scope.isOpen) {
36801               evt.preventDefault();
36802               evt.stopPropagation();
36803               scope.$apply(function() {
36804                 scope.isOpen = true;
36805               });
36806             }
36807           }
36808         }])
36809
36810         .directive('uibDatepickerPopup', function() {
36811           return {
36812             require: ['ngModel', 'uibDatepickerPopup'],
36813             controller: 'UibDatepickerPopupController',
36814             scope: {
36815               isOpen: '=?',
36816               currentText: '@',
36817               clearText: '@',
36818               closeText: '@',
36819               dateDisabled: '&',
36820               customClass: '&'
36821             },
36822             link: function(scope, element, attrs, ctrls) {
36823               var ngModel = ctrls[0],
36824                 ctrl = ctrls[1];
36825
36826               ctrl.init(ngModel);
36827             }
36828           };
36829         })
36830
36831         .directive('uibDatepickerPopupWrap', function() {
36832           return {
36833             replace: true,
36834             transclude: true,
36835             templateUrl: function(element, attrs) {
36836               return attrs.templateUrl || 'uib/template/datepicker/popup.html';
36837             }
36838           };
36839         });
36840
36841         angular.module('ui.bootstrap.debounce', [])
36842         /**
36843          * A helper, internal service that debounces a function
36844          */
36845           .factory('$$debounce', ['$timeout', function($timeout) {
36846             return function(callback, debounceTime) {
36847               var timeoutPromise;
36848
36849               return function() {
36850                 var self = this;
36851                 var args = Array.prototype.slice.call(arguments);
36852                 if (timeoutPromise) {
36853                   $timeout.cancel(timeoutPromise);
36854                 }
36855
36856                 timeoutPromise = $timeout(function() {
36857                   callback.apply(self, args);
36858                 }, debounceTime);
36859               };
36860             };
36861           }]);
36862
36863         angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
36864
36865         .constant('uibDropdownConfig', {
36866           appendToOpenClass: 'uib-dropdown-open',
36867           openClass: 'open'
36868         })
36869
36870         .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
36871           var openScope = null;
36872
36873           this.open = function(dropdownScope) {
36874             if (!openScope) {
36875               $document.on('click', closeDropdown);
36876               $document.on('keydown', keybindFilter);
36877             }
36878
36879             if (openScope && openScope !== dropdownScope) {
36880               openScope.isOpen = false;
36881             }
36882
36883             openScope = dropdownScope;
36884           };
36885
36886           this.close = function(dropdownScope) {
36887             if (openScope === dropdownScope) {
36888               openScope = null;
36889               $document.off('click', closeDropdown);
36890               $document.off('keydown', keybindFilter);
36891             }
36892           };
36893
36894           var closeDropdown = function(evt) {
36895             // This method may still be called during the same mouse event that
36896             // unbound this event handler. So check openScope before proceeding.
36897             if (!openScope) { return; }
36898
36899             if (evt && openScope.getAutoClose() === 'disabled') { return; }
36900
36901             if (evt && evt.which === 3) { return; }
36902
36903             var toggleElement = openScope.getToggleElement();
36904             if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
36905               return;
36906             }
36907
36908             var dropdownElement = openScope.getDropdownElement();
36909             if (evt && openScope.getAutoClose() === 'outsideClick' &&
36910               dropdownElement && dropdownElement[0].contains(evt.target)) {
36911               return;
36912             }
36913
36914             openScope.isOpen = false;
36915
36916             if (!$rootScope.$$phase) {
36917               openScope.$apply();
36918             }
36919           };
36920
36921           var keybindFilter = function(evt) {
36922             if (evt.which === 27) {
36923               openScope.focusToggleElement();
36924               closeDropdown();
36925             } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
36926               evt.preventDefault();
36927               evt.stopPropagation();
36928               openScope.focusDropdownEntry(evt.which);
36929             }
36930           };
36931         }])
36932
36933         .controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
36934           var self = this,
36935             scope = $scope.$new(), // create a child scope so we are not polluting original one
36936             templateScope,
36937             appendToOpenClass = dropdownConfig.appendToOpenClass,
36938             openClass = dropdownConfig.openClass,
36939             getIsOpen,
36940             setIsOpen = angular.noop,
36941             toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
36942             appendToBody = false,
36943             appendTo = null,
36944             keynavEnabled = false,
36945             selectedOption = null,
36946             body = $document.find('body');
36947
36948           $element.addClass('dropdown');
36949
36950           this.init = function() {
36951             if ($attrs.isOpen) {
36952               getIsOpen = $parse($attrs.isOpen);
36953               setIsOpen = getIsOpen.assign;
36954
36955               $scope.$watch(getIsOpen, function(value) {
36956                 scope.isOpen = !!value;
36957               });
36958             }
36959
36960             if (angular.isDefined($attrs.dropdownAppendTo)) {
36961               var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
36962               if (appendToEl) {
36963                 appendTo = angular.element(appendToEl);
36964               }
36965             }
36966
36967             appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
36968             keynavEnabled = angular.isDefined($attrs.keyboardNav);
36969
36970             if (appendToBody && !appendTo) {
36971               appendTo = body;
36972             }
36973
36974             if (appendTo && self.dropdownMenu) {
36975               appendTo.append(self.dropdownMenu);
36976               $element.on('$destroy', function handleDestroyEvent() {
36977                 self.dropdownMenu.remove();
36978               });
36979             }
36980           };
36981
36982           this.toggle = function(open) {
36983             return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
36984           };
36985
36986           // Allow other directives to watch status
36987           this.isOpen = function() {
36988             return scope.isOpen;
36989           };
36990
36991           scope.getToggleElement = function() {
36992             return self.toggleElement;
36993           };
36994
36995           scope.getAutoClose = function() {
36996             return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
36997           };
36998
36999           scope.getElement = function() {
37000             return $element;
37001           };
37002
37003           scope.isKeynavEnabled = function() {
37004             return keynavEnabled;
37005           };
37006
37007           scope.focusDropdownEntry = function(keyCode) {
37008             var elems = self.dropdownMenu ? //If append to body is used.
37009               angular.element(self.dropdownMenu).find('a') :
37010               $element.find('ul').eq(0).find('a');
37011
37012             switch (keyCode) {
37013               case 40: {
37014                 if (!angular.isNumber(self.selectedOption)) {
37015                   self.selectedOption = 0;
37016                 } else {
37017                   self.selectedOption = self.selectedOption === elems.length - 1 ?
37018                     self.selectedOption :
37019                     self.selectedOption + 1;
37020                 }
37021                 break;
37022               }
37023               case 38: {
37024                 if (!angular.isNumber(self.selectedOption)) {
37025                   self.selectedOption = elems.length - 1;
37026                 } else {
37027                   self.selectedOption = self.selectedOption === 0 ?
37028                     0 : self.selectedOption - 1;
37029                 }
37030                 break;
37031               }
37032             }
37033             elems[self.selectedOption].focus();
37034           };
37035
37036           scope.getDropdownElement = function() {
37037             return self.dropdownMenu;
37038           };
37039
37040           scope.focusToggleElement = function() {
37041             if (self.toggleElement) {
37042               self.toggleElement[0].focus();
37043             }
37044           };
37045
37046           scope.$watch('isOpen', function(isOpen, wasOpen) {
37047             if (appendTo && self.dropdownMenu) {
37048               var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
37049                 css,
37050                 rightalign;
37051
37052               css = {
37053                 top: pos.top + 'px',
37054                 display: isOpen ? 'block' : 'none'
37055               };
37056
37057               rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
37058               if (!rightalign) {
37059                 css.left = pos.left + 'px';
37060                 css.right = 'auto';
37061               } else {
37062                 css.left = 'auto';
37063                 css.right = window.innerWidth -
37064                   (pos.left + $element.prop('offsetWidth')) + 'px';
37065               }
37066
37067               // Need to adjust our positioning to be relative to the appendTo container
37068               // if it's not the body element
37069               if (!appendToBody) {
37070                 var appendOffset = $position.offset(appendTo);
37071
37072                 css.top = pos.top - appendOffset.top + 'px';
37073
37074                 if (!rightalign) {
37075                   css.left = pos.left - appendOffset.left + 'px';
37076                 } else {
37077                   css.right = window.innerWidth -
37078                     (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
37079                 }
37080               }
37081
37082               self.dropdownMenu.css(css);
37083             }
37084
37085             var openContainer = appendTo ? appendTo : $element;
37086
37087             $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
37088               if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
37089                 toggleInvoker($scope, { open: !!isOpen });
37090               }
37091             });
37092
37093             if (isOpen) {
37094               if (self.dropdownMenuTemplateUrl) {
37095                 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
37096                   templateScope = scope.$new();
37097                   $compile(tplContent.trim())(templateScope, function(dropdownElement) {
37098                     var newEl = dropdownElement;
37099                     self.dropdownMenu.replaceWith(newEl);
37100                     self.dropdownMenu = newEl;
37101                   });
37102                 });
37103               }
37104
37105               scope.focusToggleElement();
37106               uibDropdownService.open(scope);
37107             } else {
37108               if (self.dropdownMenuTemplateUrl) {
37109                 if (templateScope) {
37110                   templateScope.$destroy();
37111                 }
37112                 var newEl = angular.element('<ul class="dropdown-menu"></ul>');
37113                 self.dropdownMenu.replaceWith(newEl);
37114                 self.dropdownMenu = newEl;
37115               }
37116
37117               uibDropdownService.close(scope);
37118               self.selectedOption = null;
37119             }
37120
37121             if (angular.isFunction(setIsOpen)) {
37122               setIsOpen($scope, isOpen);
37123             }
37124           });
37125
37126           $scope.$on('$locationChangeSuccess', function() {
37127             if (scope.getAutoClose() !== 'disabled') {
37128               scope.isOpen = false;
37129             }
37130           });
37131         }])
37132
37133         .directive('uibDropdown', function() {
37134           return {
37135             controller: 'UibDropdownController',
37136             link: function(scope, element, attrs, dropdownCtrl) {
37137               dropdownCtrl.init();
37138             }
37139           };
37140         })
37141
37142         .directive('uibDropdownMenu', function() {
37143           return {
37144             restrict: 'A',
37145             require: '?^uibDropdown',
37146             link: function(scope, element, attrs, dropdownCtrl) {
37147               if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
37148                 return;
37149               }
37150
37151               element.addClass('dropdown-menu');
37152
37153               var tplUrl = attrs.templateUrl;
37154               if (tplUrl) {
37155                 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
37156               }
37157
37158               if (!dropdownCtrl.dropdownMenu) {
37159                 dropdownCtrl.dropdownMenu = element;
37160               }
37161             }
37162           };
37163         })
37164
37165         .directive('uibDropdownToggle', function() {
37166           return {
37167             require: '?^uibDropdown',
37168             link: function(scope, element, attrs, dropdownCtrl) {
37169               if (!dropdownCtrl) {
37170                 return;
37171               }
37172
37173               element.addClass('dropdown-toggle');
37174
37175               dropdownCtrl.toggleElement = element;
37176
37177               var toggleDropdown = function(event) {
37178                 event.preventDefault();
37179
37180                 if (!element.hasClass('disabled') && !attrs.disabled) {
37181                   scope.$apply(function() {
37182                     dropdownCtrl.toggle();
37183                   });
37184                 }
37185               };
37186
37187               element.bind('click', toggleDropdown);
37188
37189               // WAI-ARIA
37190               element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
37191               scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
37192                 element.attr('aria-expanded', !!isOpen);
37193               });
37194
37195               scope.$on('$destroy', function() {
37196                 element.unbind('click', toggleDropdown);
37197               });
37198             }
37199           };
37200         });
37201
37202         angular.module('ui.bootstrap.stackedMap', [])
37203         /**
37204          * A helper, internal data structure that acts as a map but also allows getting / removing
37205          * elements in the LIFO order
37206          */
37207           .factory('$$stackedMap', function() {
37208             return {
37209               createNew: function() {
37210                 var stack = [];
37211
37212                 return {
37213                   add: function(key, value) {
37214                     stack.push({
37215                       key: key,
37216                       value: value
37217                     });
37218                   },
37219                   get: function(key) {
37220                     for (var i = 0; i < stack.length; i++) {
37221                       if (key === stack[i].key) {
37222                         return stack[i];
37223                       }
37224                     }
37225                   },
37226                   keys: function() {
37227                     var keys = [];
37228                     for (var i = 0; i < stack.length; i++) {
37229                       keys.push(stack[i].key);
37230                     }
37231                     return keys;
37232                   },
37233                   top: function() {
37234                     return stack[stack.length - 1];
37235                   },
37236                   remove: function(key) {
37237                     var idx = -1;
37238                     for (var i = 0; i < stack.length; i++) {
37239                       if (key === stack[i].key) {
37240                         idx = i;
37241                         break;
37242                       }
37243                     }
37244                     return stack.splice(idx, 1)[0];
37245                   },
37246                   removeTop: function() {
37247                     return stack.splice(stack.length - 1, 1)[0];
37248                   },
37249                   length: function() {
37250                     return stack.length;
37251                   }
37252                 };
37253               }
37254             };
37255           });
37256         angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
37257         /**
37258          * A helper, internal data structure that stores all references attached to key
37259          */
37260           .factory('$$multiMap', function() {
37261             return {
37262               createNew: function() {
37263                 var map = {};
37264
37265                 return {
37266                   entries: function() {
37267                     return Object.keys(map).map(function(key) {
37268                       return {
37269                         key: key,
37270                         value: map[key]
37271                       };
37272                     });
37273                   },
37274                   get: function(key) {
37275                     return map[key];
37276                   },
37277                   hasKey: function(key) {
37278                     return !!map[key];
37279                   },
37280                   keys: function() {
37281                     return Object.keys(map);
37282                   },
37283                   put: function(key, value) {
37284                     if (!map[key]) {
37285                       map[key] = [];
37286                     }
37287
37288                     map[key].push(value);
37289                   },
37290                   remove: function(key, value) {
37291                     var values = map[key];
37292
37293                     if (!values) {
37294                       return;
37295                     }
37296
37297                     var idx = values.indexOf(value);
37298
37299                     if (idx !== -1) {
37300                       values.splice(idx, 1);
37301                     }
37302
37303                     if (!values.length) {
37304                       delete map[key];
37305                     }
37306                   }
37307                 };
37308               }
37309             };
37310           })
37311
37312         /**
37313          * Pluggable resolve mechanism for the modal resolve resolution
37314          * Supports UI Router's $resolve service
37315          */
37316           .provider('$uibResolve', function() {
37317             var resolve = this;
37318             this.resolver = null;
37319
37320             this.setResolver = function(resolver) {
37321               this.resolver = resolver;
37322             };
37323
37324             this.$get = ['$injector', '$q', function($injector, $q) {
37325               var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
37326               return {
37327                 resolve: function(invocables, locals, parent, self) {
37328                   if (resolver) {
37329                     return resolver.resolve(invocables, locals, parent, self);
37330                   }
37331
37332                   var promises = [];
37333
37334                   angular.forEach(invocables, function(value) {
37335                     if (angular.isFunction(value) || angular.isArray(value)) {
37336                       promises.push($q.resolve($injector.invoke(value)));
37337                     } else if (angular.isString(value)) {
37338                       promises.push($q.resolve($injector.get(value)));
37339                     } else {
37340                       promises.push($q.resolve(value));
37341                     }
37342                   });
37343
37344                   return $q.all(promises).then(function(resolves) {
37345                     var resolveObj = {};
37346                     var resolveIter = 0;
37347                     angular.forEach(invocables, function(value, key) {
37348                       resolveObj[key] = resolves[resolveIter++];
37349                     });
37350
37351                     return resolveObj;
37352                   });
37353                 }
37354               };
37355             }];
37356           })
37357
37358         /**
37359          * A helper directive for the $modal service. It creates a backdrop element.
37360          */
37361           .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack',
37362           function($animateCss, $injector, $modalStack) {
37363             return {
37364               replace: true,
37365               templateUrl: 'uib/template/modal/backdrop.html',
37366               compile: function(tElement, tAttrs) {
37367                 tElement.addClass(tAttrs.backdropClass);
37368                 return linkFn;
37369               }
37370             };
37371
37372             function linkFn(scope, element, attrs) {
37373               if (attrs.modalInClass) {
37374                 $animateCss(element, {
37375                   addClass: attrs.modalInClass
37376                 }).start();
37377
37378                 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37379                   var done = setIsAsync();
37380                   if (scope.modalOptions.animation) {
37381                     $animateCss(element, {
37382                       removeClass: attrs.modalInClass
37383                     }).start().then(done);
37384                   } else {
37385                     done();
37386                   }
37387                 });
37388               }
37389             }
37390           }])
37391
37392           .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
37393           function($modalStack, $q, $animate, $animateCss, $document) {
37394             return {
37395               scope: {
37396                 index: '@'
37397               },
37398               replace: true,
37399               transclude: true,
37400               templateUrl: function(tElement, tAttrs) {
37401                 return tAttrs.templateUrl || 'uib/template/modal/window.html';
37402               },
37403               link: function(scope, element, attrs) {
37404                 element.addClass(attrs.windowClass || '');
37405                 element.addClass(attrs.windowTopClass || '');
37406                 scope.size = attrs.size;
37407
37408                 scope.close = function(evt) {
37409                   var modal = $modalStack.getTop();
37410                   if (modal && modal.value.backdrop &&
37411                     modal.value.backdrop !== 'static' &&
37412                     evt.target === evt.currentTarget) {
37413                     evt.preventDefault();
37414                     evt.stopPropagation();
37415                     $modalStack.dismiss(modal.key, 'backdrop click');
37416                   }
37417                 };
37418
37419                 // moved from template to fix issue #2280
37420                 element.on('click', scope.close);
37421
37422                 // This property is only added to the scope for the purpose of detecting when this directive is rendered.
37423                 // We can detect that by using this property in the template associated with this directive and then use
37424                 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
37425                 scope.$isRendered = true;
37426
37427                 // Deferred object that will be resolved when this modal is render.
37428                 var modalRenderDeferObj = $q.defer();
37429                 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
37430                 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
37431                 attrs.$observe('modalRender', function(value) {
37432                   if (value === 'true') {
37433                     modalRenderDeferObj.resolve();
37434                   }
37435                 });
37436
37437                 modalRenderDeferObj.promise.then(function() {
37438                   var animationPromise = null;
37439
37440                   if (attrs.modalInClass) {
37441                     animationPromise = $animateCss(element, {
37442                       addClass: attrs.modalInClass
37443                     }).start();
37444
37445                     scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37446                       var done = setIsAsync();
37447                       if ($animateCss) {
37448                         $animateCss(element, {
37449                           removeClass: attrs.modalInClass
37450                         }).start().then(done);
37451                       } else {
37452                         $animate.removeClass(element, attrs.modalInClass).then(done);
37453                       }
37454                     });
37455                   }
37456
37457
37458                   $q.when(animationPromise).then(function() {
37459                     /**
37460                      * If something within the freshly-opened modal already has focus (perhaps via a
37461                      * directive that causes focus). then no need to try and focus anything.
37462                      */
37463                     if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
37464                       var inputWithAutofocus = element[0].querySelector('[autofocus]');
37465                       /**
37466                        * Auto-focusing of a freshly-opened modal element causes any child elements
37467                        * with the autofocus attribute to lose focus. This is an issue on touch
37468                        * based devices which will show and then hide the onscreen keyboard.
37469                        * Attempts to refocus the autofocus element via JavaScript will not reopen
37470                        * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
37471                        * the modal element if the modal does not contain an autofocus element.
37472                        */
37473                       if (inputWithAutofocus) {
37474                         inputWithAutofocus.focus();
37475                       } else {
37476                         element[0].focus();
37477                       }
37478                     }
37479                   });
37480
37481                   // Notify {@link $modalStack} that modal is rendered.
37482                   var modal = $modalStack.getTop();
37483                   if (modal) {
37484                     $modalStack.modalRendered(modal.key);
37485                   }
37486                 });
37487               }
37488             };
37489           }])
37490
37491           .directive('uibModalAnimationClass', function() {
37492             return {
37493               compile: function(tElement, tAttrs) {
37494                 if (tAttrs.modalAnimation) {
37495                   tElement.addClass(tAttrs.uibModalAnimationClass);
37496                 }
37497               }
37498             };
37499           })
37500
37501           .directive('uibModalTransclude', function() {
37502             return {
37503               link: function(scope, element, attrs, controller, transclude) {
37504                 transclude(scope.$parent, function(clone) {
37505                   element.empty();
37506                   element.append(clone);
37507                 });
37508               }
37509             };
37510           })
37511
37512           .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
37513             '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap',
37514             function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) {
37515               var OPENED_MODAL_CLASS = 'modal-open';
37516
37517               var backdropDomEl, backdropScope;
37518               var openedWindows = $$stackedMap.createNew();
37519               var openedClasses = $$multiMap.createNew();
37520               var $modalStack = {
37521                 NOW_CLOSING_EVENT: 'modal.stack.now-closing'
37522               };
37523
37524               //Modal focus behavior
37525               var focusableElementList;
37526               var focusIndex = 0;
37527               var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
37528                 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
37529                 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
37530
37531               function backdropIndex() {
37532                 var topBackdropIndex = -1;
37533                 var opened = openedWindows.keys();
37534                 for (var i = 0; i < opened.length; i++) {
37535                   if (openedWindows.get(opened[i]).value.backdrop) {
37536                     topBackdropIndex = i;
37537                   }
37538                 }
37539                 return topBackdropIndex;
37540               }
37541
37542               $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
37543                 if (backdropScope) {
37544                   backdropScope.index = newBackdropIndex;
37545                 }
37546               });
37547
37548               function removeModalWindow(modalInstance, elementToReceiveFocus) {
37549                 var modalWindow = openedWindows.get(modalInstance).value;
37550                 var appendToElement = modalWindow.appendTo;
37551
37552                 //clean up the stack
37553                 openedWindows.remove(modalInstance);
37554
37555                 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
37556                   var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
37557                   openedClasses.remove(modalBodyClass, modalInstance);
37558                   appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
37559                   toggleTopWindowClass(true);
37560                 });
37561                 checkRemoveBackdrop();
37562
37563                 //move focus to specified element if available, or else to body
37564                 if (elementToReceiveFocus && elementToReceiveFocus.focus) {
37565                   elementToReceiveFocus.focus();
37566                 } else if (appendToElement.focus) {
37567                   appendToElement.focus();
37568                 }
37569               }
37570
37571               // Add or remove "windowTopClass" from the top window in the stack
37572               function toggleTopWindowClass(toggleSwitch) {
37573                 var modalWindow;
37574
37575                 if (openedWindows.length() > 0) {
37576                   modalWindow = openedWindows.top().value;
37577                   modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
37578                 }
37579               }
37580
37581               function checkRemoveBackdrop() {
37582                 //remove backdrop if no longer needed
37583                 if (backdropDomEl && backdropIndex() === -1) {
37584                   var backdropScopeRef = backdropScope;
37585                   removeAfterAnimate(backdropDomEl, backdropScope, function() {
37586                     backdropScopeRef = null;
37587                   });
37588                   backdropDomEl = undefined;
37589                   backdropScope = undefined;
37590                 }
37591               }
37592
37593               function removeAfterAnimate(domEl, scope, done, closedDeferred) {
37594                 var asyncDeferred;
37595                 var asyncPromise = null;
37596                 var setIsAsync = function() {
37597                   if (!asyncDeferred) {
37598                     asyncDeferred = $q.defer();
37599                     asyncPromise = asyncDeferred.promise;
37600                   }
37601
37602                   return function asyncDone() {
37603                     asyncDeferred.resolve();
37604                   };
37605                 };
37606                 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
37607
37608                 // Note that it's intentional that asyncPromise might be null.
37609                 // That's when setIsAsync has not been called during the
37610                 // NOW_CLOSING_EVENT broadcast.
37611                 return $q.when(asyncPromise).then(afterAnimating);
37612
37613                 function afterAnimating() {
37614                   if (afterAnimating.done) {
37615                     return;
37616                   }
37617                   afterAnimating.done = true;
37618
37619                   $animateCss(domEl, {
37620                     event: 'leave'
37621                   }).start().then(function() {
37622                     domEl.remove();
37623                     if (closedDeferred) {
37624                       closedDeferred.resolve();
37625                     }
37626                   });
37627
37628                   scope.$destroy();
37629                   if (done) {
37630                     done();
37631                   }
37632                 }
37633               }
37634
37635               $document.on('keydown', keydownListener);
37636
37637               $rootScope.$on('$destroy', function() {
37638                 $document.off('keydown', keydownListener);
37639               });
37640
37641               function keydownListener(evt) {
37642                 if (evt.isDefaultPrevented()) {
37643                   return evt;
37644                 }
37645
37646                 var modal = openedWindows.top();
37647                 if (modal) {
37648                   switch (evt.which) {
37649                     case 27: {
37650                       if (modal.value.keyboard) {
37651                         evt.preventDefault();
37652                         $rootScope.$apply(function() {
37653                           $modalStack.dismiss(modal.key, 'escape key press');
37654                         });
37655                       }
37656                       break;
37657                     }
37658                     case 9: {
37659                       $modalStack.loadFocusElementList(modal);
37660                       var focusChanged = false;
37661                       if (evt.shiftKey) {
37662                         if ($modalStack.isFocusInFirstItem(evt)) {
37663                           focusChanged = $modalStack.focusLastFocusableElement();
37664                         }
37665                       } else {
37666                         if ($modalStack.isFocusInLastItem(evt)) {
37667                           focusChanged = $modalStack.focusFirstFocusableElement();
37668                         }
37669                       }
37670
37671                       if (focusChanged) {
37672                         evt.preventDefault();
37673                         evt.stopPropagation();
37674                       }
37675                       break;
37676                     }
37677                   }
37678                 }
37679               }
37680
37681               $modalStack.open = function(modalInstance, modal) {
37682                 var modalOpener = $document[0].activeElement,
37683                   modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
37684
37685                 toggleTopWindowClass(false);
37686
37687                 openedWindows.add(modalInstance, {
37688                   deferred: modal.deferred,
37689                   renderDeferred: modal.renderDeferred,
37690                   closedDeferred: modal.closedDeferred,
37691                   modalScope: modal.scope,
37692                   backdrop: modal.backdrop,
37693                   keyboard: modal.keyboard,
37694                   openedClass: modal.openedClass,
37695                   windowTopClass: modal.windowTopClass,
37696                   animation: modal.animation,
37697                   appendTo: modal.appendTo
37698                 });
37699
37700                 openedClasses.put(modalBodyClass, modalInstance);
37701
37702                 var appendToElement = modal.appendTo,
37703                     currBackdropIndex = backdropIndex();
37704
37705                 if (!appendToElement.length) {
37706                   throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
37707                 }
37708
37709                 if (currBackdropIndex >= 0 && !backdropDomEl) {
37710                   backdropScope = $rootScope.$new(true);
37711                   backdropScope.modalOptions = modal;
37712                   backdropScope.index = currBackdropIndex;
37713                   backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
37714                   backdropDomEl.attr('backdrop-class', modal.backdropClass);
37715                   if (modal.animation) {
37716                     backdropDomEl.attr('modal-animation', 'true');
37717                   }
37718                   $compile(backdropDomEl)(backdropScope);
37719                   $animate.enter(backdropDomEl, appendToElement);
37720                 }
37721
37722                 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
37723                 angularDomEl.attr({
37724                   'template-url': modal.windowTemplateUrl,
37725                   'window-class': modal.windowClass,
37726                   'window-top-class': modal.windowTopClass,
37727                   'size': modal.size,
37728                   'index': openedWindows.length() - 1,
37729                   'animate': 'animate'
37730                 }).html(modal.content);
37731                 if (modal.animation) {
37732                   angularDomEl.attr('modal-animation', 'true');
37733                 }
37734
37735                 $animate.enter(angularDomEl, appendToElement)
37736                   .then(function() {
37737                     $compile(angularDomEl)(modal.scope);
37738                     $animate.addClass(appendToElement, modalBodyClass);
37739                   });
37740
37741                 openedWindows.top().value.modalDomEl = angularDomEl;
37742                 openedWindows.top().value.modalOpener = modalOpener;
37743
37744                 $modalStack.clearFocusListCache();
37745               };
37746
37747               function broadcastClosing(modalWindow, resultOrReason, closing) {
37748                 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
37749               }
37750
37751               $modalStack.close = function(modalInstance, result) {
37752                 var modalWindow = openedWindows.get(modalInstance);
37753                 if (modalWindow && broadcastClosing(modalWindow, result, true)) {
37754                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37755                   modalWindow.value.deferred.resolve(result);
37756                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37757                   return true;
37758                 }
37759                 return !modalWindow;
37760               };
37761
37762               $modalStack.dismiss = function(modalInstance, reason) {
37763                 var modalWindow = openedWindows.get(modalInstance);
37764                 if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
37765                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37766                   modalWindow.value.deferred.reject(reason);
37767                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37768                   return true;
37769                 }
37770                 return !modalWindow;
37771               };
37772
37773               $modalStack.dismissAll = function(reason) {
37774                 var topModal = this.getTop();
37775                 while (topModal && this.dismiss(topModal.key, reason)) {
37776                   topModal = this.getTop();
37777                 }
37778               };
37779
37780               $modalStack.getTop = function() {
37781                 return openedWindows.top();
37782               };
37783
37784               $modalStack.modalRendered = function(modalInstance) {
37785                 var modalWindow = openedWindows.get(modalInstance);
37786                 if (modalWindow) {
37787                   modalWindow.value.renderDeferred.resolve();
37788                 }
37789               };
37790
37791               $modalStack.focusFirstFocusableElement = function() {
37792                 if (focusableElementList.length > 0) {
37793                   focusableElementList[0].focus();
37794                   return true;
37795                 }
37796                 return false;
37797               };
37798               $modalStack.focusLastFocusableElement = function() {
37799                 if (focusableElementList.length > 0) {
37800                   focusableElementList[focusableElementList.length - 1].focus();
37801                   return true;
37802                 }
37803                 return false;
37804               };
37805
37806               $modalStack.isFocusInFirstItem = function(evt) {
37807                 if (focusableElementList.length > 0) {
37808                   return (evt.target || evt.srcElement) === focusableElementList[0];
37809                 }
37810                 return false;
37811               };
37812
37813               $modalStack.isFocusInLastItem = function(evt) {
37814                 if (focusableElementList.length > 0) {
37815                   return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
37816                 }
37817                 return false;
37818               };
37819
37820               $modalStack.clearFocusListCache = function() {
37821                 focusableElementList = [];
37822                 focusIndex = 0;
37823               };
37824
37825               $modalStack.loadFocusElementList = function(modalWindow) {
37826                 if (focusableElementList === undefined || !focusableElementList.length) {
37827                   if (modalWindow) {
37828                     var modalDomE1 = modalWindow.value.modalDomEl;
37829                     if (modalDomE1 && modalDomE1.length) {
37830                       focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
37831                     }
37832                   }
37833                 }
37834               };
37835
37836               return $modalStack;
37837             }])
37838
37839           .provider('$uibModal', function() {
37840             var $modalProvider = {
37841               options: {
37842                 animation: true,
37843                 backdrop: true, //can also be false or 'static'
37844                 keyboard: true
37845               },
37846               $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
37847                 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
37848                   var $modal = {};
37849
37850                   function getTemplatePromise(options) {
37851                     return options.template ? $q.when(options.template) :
37852                       $templateRequest(angular.isFunction(options.templateUrl) ?
37853                         options.templateUrl() : options.templateUrl);
37854                   }
37855
37856                   var promiseChain = null;
37857                   $modal.getPromiseChain = function() {
37858                     return promiseChain;
37859                   };
37860
37861                   $modal.open = function(modalOptions) {
37862                     var modalResultDeferred = $q.defer();
37863                     var modalOpenedDeferred = $q.defer();
37864                     var modalClosedDeferred = $q.defer();
37865                     var modalRenderDeferred = $q.defer();
37866
37867                     //prepare an instance of a modal to be injected into controllers and returned to a caller
37868                     var modalInstance = {
37869                       result: modalResultDeferred.promise,
37870                       opened: modalOpenedDeferred.promise,
37871                       closed: modalClosedDeferred.promise,
37872                       rendered: modalRenderDeferred.promise,
37873                       close: function (result) {
37874                         return $modalStack.close(modalInstance, result);
37875                       },
37876                       dismiss: function (reason) {
37877                         return $modalStack.dismiss(modalInstance, reason);
37878                       }
37879                     };
37880
37881                     //merge and clean up options
37882                     modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
37883                     modalOptions.resolve = modalOptions.resolve || {};
37884                     modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
37885
37886                     //verify options
37887                     if (!modalOptions.template && !modalOptions.templateUrl) {
37888                       throw new Error('One of template or templateUrl options is required.');
37889                     }
37890
37891                     var templateAndResolvePromise =
37892                       $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
37893
37894                     function resolveWithTemplate() {
37895                       return templateAndResolvePromise;
37896                     }
37897
37898                     // Wait for the resolution of the existing promise chain.
37899                     // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
37900                     // Then add to $modalStack and resolve opened.
37901                     // Finally clean up the chain variable if no subsequent modal has overwritten it.
37902                     var samePromise;
37903                     samePromise = promiseChain = $q.all([promiseChain])
37904                       .then(resolveWithTemplate, resolveWithTemplate)
37905                       .then(function resolveSuccess(tplAndVars) {
37906                         var providedScope = modalOptions.scope || $rootScope;
37907
37908                         var modalScope = providedScope.$new();
37909                         modalScope.$close = modalInstance.close;
37910                         modalScope.$dismiss = modalInstance.dismiss;
37911
37912                         modalScope.$on('$destroy', function() {
37913                           if (!modalScope.$$uibDestructionScheduled) {
37914                             modalScope.$dismiss('$uibUnscheduledDestruction');
37915                           }
37916                         });
37917
37918                         var ctrlInstance, ctrlLocals = {};
37919
37920                         //controllers
37921                         if (modalOptions.controller) {
37922                           ctrlLocals.$scope = modalScope;
37923                           ctrlLocals.$uibModalInstance = modalInstance;
37924                           angular.forEach(tplAndVars[1], function(value, key) {
37925                             ctrlLocals[key] = value;
37926                           });
37927
37928                           ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
37929                           if (modalOptions.controllerAs) {
37930                             if (modalOptions.bindToController) {
37931                               ctrlInstance.$close = modalScope.$close;
37932                               ctrlInstance.$dismiss = modalScope.$dismiss;
37933                               angular.extend(ctrlInstance, providedScope);
37934                             }
37935
37936                             modalScope[modalOptions.controllerAs] = ctrlInstance;
37937                           }
37938                         }
37939
37940                         $modalStack.open(modalInstance, {
37941                           scope: modalScope,
37942                           deferred: modalResultDeferred,
37943                           renderDeferred: modalRenderDeferred,
37944                           closedDeferred: modalClosedDeferred,
37945                           content: tplAndVars[0],
37946                           animation: modalOptions.animation,
37947                           backdrop: modalOptions.backdrop,
37948                           keyboard: modalOptions.keyboard,
37949                           backdropClass: modalOptions.backdropClass,
37950                           windowTopClass: modalOptions.windowTopClass,
37951                           windowClass: modalOptions.windowClass,
37952                           windowTemplateUrl: modalOptions.windowTemplateUrl,
37953                           size: modalOptions.size,
37954                           openedClass: modalOptions.openedClass,
37955                           appendTo: modalOptions.appendTo
37956                         });
37957                         modalOpenedDeferred.resolve(true);
37958
37959                     }, function resolveError(reason) {
37960                       modalOpenedDeferred.reject(reason);
37961                       modalResultDeferred.reject(reason);
37962                     })['finally'](function() {
37963                       if (promiseChain === samePromise) {
37964                         promiseChain = null;
37965                       }
37966                     });
37967
37968                     return modalInstance;
37969                   };
37970
37971                   return $modal;
37972                 }
37973               ]
37974             };
37975
37976             return $modalProvider;
37977           });
37978
37979         angular.module('ui.bootstrap.paging', [])
37980         /**
37981          * Helper internal service for generating common controller code between the
37982          * pager and pagination components
37983          */
37984         .factory('uibPaging', ['$parse', function($parse) {
37985           return {
37986             create: function(ctrl, $scope, $attrs) {
37987               ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
37988               ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
37989
37990               ctrl.init = function(ngModelCtrl, config) {
37991                 ctrl.ngModelCtrl = ngModelCtrl;
37992                 ctrl.config = config;
37993
37994                 ngModelCtrl.$render = function() {
37995                   ctrl.render();
37996                 };
37997
37998                 if ($attrs.itemsPerPage) {
37999                   $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
38000                     ctrl.itemsPerPage = parseInt(value, 10);
38001                     $scope.totalPages = ctrl.calculateTotalPages();
38002                     ctrl.updatePage();
38003                   });
38004                 } else {
38005                   ctrl.itemsPerPage = config.itemsPerPage;
38006                 }
38007
38008                 $scope.$watch('totalItems', function(newTotal, oldTotal) {
38009                   if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
38010                     $scope.totalPages = ctrl.calculateTotalPages();
38011                     ctrl.updatePage();
38012                   }
38013                 });
38014               };
38015
38016               ctrl.calculateTotalPages = function() {
38017                 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
38018                 return Math.max(totalPages || 0, 1);
38019               };
38020
38021               ctrl.render = function() {
38022                 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
38023               };
38024
38025               $scope.selectPage = function(page, evt) {
38026                 if (evt) {
38027                   evt.preventDefault();
38028                 }
38029
38030                 var clickAllowed = !$scope.ngDisabled || !evt;
38031                 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
38032                   if (evt && evt.target) {
38033                     evt.target.blur();
38034                   }
38035                   ctrl.ngModelCtrl.$setViewValue(page);
38036                   ctrl.ngModelCtrl.$render();
38037                 }
38038               };
38039
38040               $scope.getText = function(key) {
38041                 return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
38042               };
38043
38044               $scope.noPrevious = function() {
38045                 return $scope.page === 1;
38046               };
38047
38048               $scope.noNext = function() {
38049                 return $scope.page === $scope.totalPages;
38050               };
38051
38052               ctrl.updatePage = function() {
38053                 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
38054
38055                 if ($scope.page > $scope.totalPages) {
38056                   $scope.selectPage($scope.totalPages);
38057                 } else {
38058                   ctrl.ngModelCtrl.$render();
38059                 }
38060               };
38061             }
38062           };
38063         }]);
38064
38065         angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
38066
38067         .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
38068           $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
38069
38070           uibPaging.create(this, $scope, $attrs);
38071         }])
38072
38073         .constant('uibPagerConfig', {
38074           itemsPerPage: 10,
38075           previousText: '« Previous',
38076           nextText: 'Next »',
38077           align: true
38078         })
38079
38080         .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
38081           return {
38082             scope: {
38083               totalItems: '=',
38084               previousText: '@',
38085               nextText: '@',
38086               ngDisabled: '='
38087             },
38088             require: ['uibPager', '?ngModel'],
38089             controller: 'UibPagerController',
38090             controllerAs: 'pager',
38091             templateUrl: function(element, attrs) {
38092               return attrs.templateUrl || 'uib/template/pager/pager.html';
38093             },
38094             replace: true,
38095             link: function(scope, element, attrs, ctrls) {
38096               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38097
38098               if (!ngModelCtrl) {
38099                 return; // do nothing if no ng-model
38100               }
38101
38102               paginationCtrl.init(ngModelCtrl, uibPagerConfig);
38103             }
38104           };
38105         }]);
38106
38107         angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
38108         .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
38109           var ctrl = this;
38110           // Setup configuration parameters
38111           var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
38112             rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
38113             forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
38114             boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
38115           $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
38116           $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
38117
38118           uibPaging.create(this, $scope, $attrs);
38119
38120           if ($attrs.maxSize) {
38121             $scope.$parent.$watch($parse($attrs.maxSize), function(value) {
38122               maxSize = parseInt(value, 10);
38123               ctrl.render();
38124             });
38125           }
38126
38127           // Create page object used in template
38128           function makePage(number, text, isActive) {
38129             return {
38130               number: number,
38131               text: text,
38132               active: isActive
38133             };
38134           }
38135
38136           function getPages(currentPage, totalPages) {
38137             var pages = [];
38138
38139             // Default page limits
38140             var startPage = 1, endPage = totalPages;
38141             var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
38142
38143             // recompute if maxSize
38144             if (isMaxSized) {
38145               if (rotate) {
38146                 // Current page is displayed in the middle of the visible ones
38147                 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
38148                 endPage = startPage + maxSize - 1;
38149
38150                 // Adjust if limit is exceeded
38151                 if (endPage > totalPages) {
38152                   endPage = totalPages;
38153                   startPage = endPage - maxSize + 1;
38154                 }
38155               } else {
38156                 // Visible pages are paginated with maxSize
38157                 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
38158
38159                 // Adjust last page if limit is exceeded
38160                 endPage = Math.min(startPage + maxSize - 1, totalPages);
38161               }
38162             }
38163
38164             // Add page number links
38165             for (var number = startPage; number <= endPage; number++) {
38166               var page = makePage(number, number, number === currentPage);
38167               pages.push(page);
38168             }
38169
38170             // Add links to move between page sets
38171             if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
38172               if (startPage > 1) {
38173                 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
38174                 var previousPageSet = makePage(startPage - 1, '...', false);
38175                 pages.unshift(previousPageSet);
38176               }
38177                 if (boundaryLinkNumbers) {
38178                   if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
38179                     var secondPageLink = makePage(2, '2', false);
38180                     pages.unshift(secondPageLink);
38181                   }
38182                   //add the first page
38183                   var firstPageLink = makePage(1, '1', false);
38184                   pages.unshift(firstPageLink);
38185                 }
38186               }
38187
38188               if (endPage < totalPages) {
38189                 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
38190                 var nextPageSet = makePage(endPage + 1, '...', false);
38191                 pages.push(nextPageSet);
38192               }
38193                 if (boundaryLinkNumbers) {
38194                   if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
38195                     var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
38196                     pages.push(secondToLastPageLink);
38197                   }
38198                   //add the last page
38199                   var lastPageLink = makePage(totalPages, totalPages, false);
38200                   pages.push(lastPageLink);
38201                 }
38202               }
38203             }
38204             return pages;
38205           }
38206
38207           var originalRender = this.render;
38208           this.render = function() {
38209             originalRender();
38210             if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
38211               $scope.pages = getPages($scope.page, $scope.totalPages);
38212             }
38213           };
38214         }])
38215
38216         .constant('uibPaginationConfig', {
38217           itemsPerPage: 10,
38218           boundaryLinks: false,
38219           boundaryLinkNumbers: false,
38220           directionLinks: true,
38221           firstText: 'First',
38222           previousText: 'Previous',
38223           nextText: 'Next',
38224           lastText: 'Last',
38225           rotate: true,
38226           forceEllipses: false
38227         })
38228
38229         .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
38230           return {
38231             scope: {
38232               totalItems: '=',
38233               firstText: '@',
38234               previousText: '@',
38235               nextText: '@',
38236               lastText: '@',
38237               ngDisabled:'='
38238             },
38239             require: ['uibPagination', '?ngModel'],
38240             controller: 'UibPaginationController',
38241             controllerAs: 'pagination',
38242             templateUrl: function(element, attrs) {
38243               return attrs.templateUrl || 'uib/template/pagination/pagination.html';
38244             },
38245             replace: true,
38246             link: function(scope, element, attrs, ctrls) {
38247               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38248
38249               if (!ngModelCtrl) {
38250                  return; // do nothing if no ng-model
38251               }
38252
38253               paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
38254             }
38255           };
38256         }]);
38257
38258         /**
38259          * The following features are still outstanding: animation as a
38260          * function, placement as a function, inside, support for more triggers than
38261          * just mouse enter/leave, html tooltips, and selector delegation.
38262          */
38263         angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
38264
38265         /**
38266          * The $tooltip service creates tooltip- and popover-like directives as well as
38267          * houses global options for them.
38268          */
38269         .provider('$uibTooltip', function() {
38270           // The default options tooltip and popover.
38271           var defaultOptions = {
38272             placement: 'top',
38273             placementClassPrefix: '',
38274             animation: true,
38275             popupDelay: 0,
38276             popupCloseDelay: 0,
38277             useContentExp: false
38278           };
38279
38280           // Default hide triggers for each show trigger
38281           var triggerMap = {
38282             'mouseenter': 'mouseleave',
38283             'click': 'click',
38284             'outsideClick': 'outsideClick',
38285             'focus': 'blur',
38286             'none': ''
38287           };
38288
38289           // The options specified to the provider globally.
38290           var globalOptions = {};
38291
38292           /**
38293            * `options({})` allows global configuration of all tooltips in the
38294            * application.
38295            *
38296            *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
38297            *     // place tooltips left instead of top by default
38298            *     $tooltipProvider.options( { placement: 'left' } );
38299            *   });
38300            */
38301                 this.options = function(value) {
38302                         angular.extend(globalOptions, value);
38303                 };
38304
38305           /**
38306            * This allows you to extend the set of trigger mappings available. E.g.:
38307            *
38308            *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
38309            */
38310           this.setTriggers = function setTriggers(triggers) {
38311             angular.extend(triggerMap, triggers);
38312           };
38313
38314           /**
38315            * This is a helper function for translating camel-case to snake_case.
38316            */
38317           function snake_case(name) {
38318             var regexp = /[A-Z]/g;
38319             var separator = '-';
38320             return name.replace(regexp, function(letter, pos) {
38321               return (pos ? separator : '') + letter.toLowerCase();
38322             });
38323           }
38324
38325           /**
38326            * Returns the actual instance of the $tooltip service.
38327            * TODO support multiple triggers
38328            */
38329           this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
38330             var openedTooltips = $$stackedMap.createNew();
38331             $document.on('keypress', keypressListener);
38332
38333             $rootScope.$on('$destroy', function() {
38334               $document.off('keypress', keypressListener);
38335             });
38336
38337             function keypressListener(e) {
38338               if (e.which === 27) {
38339                 var last = openedTooltips.top();
38340                 if (last) {
38341                   last.value.close();
38342                   openedTooltips.removeTop();
38343                   last = null;
38344                 }
38345               }
38346             }
38347
38348             return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
38349               options = angular.extend({}, defaultOptions, globalOptions, options);
38350
38351               /**
38352                * Returns an object of show and hide triggers.
38353                *
38354                * If a trigger is supplied,
38355                * it is used to show the tooltip; otherwise, it will use the `trigger`
38356                * option passed to the `$tooltipProvider.options` method; else it will
38357                * default to the trigger supplied to this directive factory.
38358                *
38359                * The hide trigger is based on the show trigger. If the `trigger` option
38360                * was passed to the `$tooltipProvider.options` method, it will use the
38361                * mapped trigger from `triggerMap` or the passed trigger if the map is
38362                * undefined; otherwise, it uses the `triggerMap` value of the show
38363                * trigger; else it will just use the show trigger.
38364                */
38365               function getTriggers(trigger) {
38366                 var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
38367                 var hide = show.map(function(trigger) {
38368                   return triggerMap[trigger] || trigger;
38369                 });
38370                 return {
38371                   show: show,
38372                   hide: hide
38373                 };
38374               }
38375
38376               var directiveName = snake_case(ttType);
38377
38378               var startSym = $interpolate.startSymbol();
38379               var endSym = $interpolate.endSymbol();
38380               var template =
38381                 '<div '+ directiveName + '-popup '+
38382                   'title="' + startSym + 'title' + endSym + '" '+
38383                   (options.useContentExp ?
38384                     'content-exp="contentExp()" ' :
38385                     'content="' + startSym + 'content' + endSym + '" ') +
38386                   'placement="' + startSym + 'placement' + endSym + '" '+
38387                   'popup-class="' + startSym + 'popupClass' + endSym + '" '+
38388                   'animation="animation" ' +
38389                   'is-open="isOpen"' +
38390                   'origin-scope="origScope" ' +
38391                   'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
38392                   '>' +
38393                 '</div>';
38394
38395               return {
38396                 compile: function(tElem, tAttrs) {
38397                   var tooltipLinker = $compile(template);
38398
38399                   return function link(scope, element, attrs, tooltipCtrl) {
38400                     var tooltip;
38401                     var tooltipLinkedScope;
38402                     var transitionTimeout;
38403                     var showTimeout;
38404                     var hideTimeout;
38405                     var positionTimeout;
38406                     var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
38407                     var triggers = getTriggers(undefined);
38408                     var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
38409                     var ttScope = scope.$new(true);
38410                     var repositionScheduled = false;
38411                     var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
38412                     var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
38413                     var observers = [];
38414
38415                     var positionTooltip = function() {
38416                       // check if tooltip exists and is not empty
38417                       if (!tooltip || !tooltip.html()) { return; }
38418
38419                       if (!positionTimeout) {
38420                         positionTimeout = $timeout(function() {
38421                           // Reset the positioning.
38422                           tooltip.css({ top: 0, left: 0 });
38423
38424                           // Now set the calculated positioning.
38425                           var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
38426                           tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' });
38427
38428                           // If the placement class is prefixed, still need
38429                           // to remove the TWBS standard class.
38430                           if (options.placementClassPrefix) {
38431                             tooltip.removeClass('top bottom left right');
38432                           }
38433
38434                           tooltip.removeClass(
38435                             options.placementClassPrefix + 'top ' +
38436                             options.placementClassPrefix + 'top-left ' +
38437                             options.placementClassPrefix + 'top-right ' +
38438                             options.placementClassPrefix + 'bottom ' +
38439                             options.placementClassPrefix + 'bottom-left ' +
38440                             options.placementClassPrefix + 'bottom-right ' +
38441                             options.placementClassPrefix + 'left ' +
38442                             options.placementClassPrefix + 'left-top ' +
38443                             options.placementClassPrefix + 'left-bottom ' +
38444                             options.placementClassPrefix + 'right ' +
38445                             options.placementClassPrefix + 'right-top ' +
38446                             options.placementClassPrefix + 'right-bottom');
38447
38448                           var placement = ttPosition.placement.split('-');
38449                           tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement);
38450                           $position.positionArrow(tooltip, ttPosition.placement);
38451
38452                           positionTimeout = null;
38453                         }, 0, false);
38454                       }
38455                     };
38456
38457                     // Set up the correct scope to allow transclusion later
38458                     ttScope.origScope = scope;
38459
38460                     // By default, the tooltip is not open.
38461                     // TODO add ability to start tooltip opened
38462                     ttScope.isOpen = false;
38463                     openedTooltips.add(ttScope, {
38464                       close: hide
38465                     });
38466
38467                     function toggleTooltipBind() {
38468                       if (!ttScope.isOpen) {
38469                         showTooltipBind();
38470                       } else {
38471                         hideTooltipBind();
38472                       }
38473                     }
38474
38475                     // Show the tooltip with delay if specified, otherwise show it immediately
38476                     function showTooltipBind() {
38477                       if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
38478                         return;
38479                       }
38480
38481                       cancelHide();
38482                       prepareTooltip();
38483
38484                       if (ttScope.popupDelay) {
38485                         // Do nothing if the tooltip was already scheduled to pop-up.
38486                         // This happens if show is triggered multiple times before any hide is triggered.
38487                         if (!showTimeout) {
38488                           showTimeout = $timeout(show, ttScope.popupDelay, false);
38489                         }
38490                       } else {
38491                         show();
38492                       }
38493                     }
38494
38495                     function hideTooltipBind() {
38496                       cancelShow();
38497
38498                       if (ttScope.popupCloseDelay) {
38499                         if (!hideTimeout) {
38500                           hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
38501                         }
38502                       } else {
38503                         hide();
38504                       }
38505                     }
38506
38507                     // Show the tooltip popup element.
38508                     function show() {
38509                       cancelShow();
38510                       cancelHide();
38511
38512                       // Don't show empty tooltips.
38513                       if (!ttScope.content) {
38514                         return angular.noop;
38515                       }
38516
38517                       createTooltip();
38518
38519                       // And show the tooltip.
38520                       ttScope.$evalAsync(function() {
38521                         ttScope.isOpen = true;
38522                         assignIsOpen(true);
38523                         positionTooltip();
38524                       });
38525                     }
38526
38527                     function cancelShow() {
38528                       if (showTimeout) {
38529                         $timeout.cancel(showTimeout);
38530                         showTimeout = null;
38531                       }
38532
38533                       if (positionTimeout) {
38534                         $timeout.cancel(positionTimeout);
38535                         positionTimeout = null;
38536                       }
38537                     }
38538
38539                     // Hide the tooltip popup element.
38540                     function hide() {
38541                       if (!ttScope) {
38542                         return;
38543                       }
38544
38545                       // First things first: we don't show it anymore.
38546                       ttScope.$evalAsync(function() {
38547                         ttScope.isOpen = false;
38548                         assignIsOpen(false);
38549                         // And now we remove it from the DOM. However, if we have animation, we
38550                         // need to wait for it to expire beforehand.
38551                         // FIXME: this is a placeholder for a port of the transitions library.
38552                         // The fade transition in TWBS is 150ms.
38553                         if (ttScope.animation) {
38554                           if (!transitionTimeout) {
38555                             transitionTimeout = $timeout(removeTooltip, 150, false);
38556                           }
38557                         } else {
38558                           removeTooltip();
38559                         }
38560                       });
38561                     }
38562
38563                     function cancelHide() {
38564                       if (hideTimeout) {
38565                         $timeout.cancel(hideTimeout);
38566                         hideTimeout = null;
38567                       }
38568                       if (transitionTimeout) {
38569                         $timeout.cancel(transitionTimeout);
38570                         transitionTimeout = null;
38571                       }
38572                     }
38573
38574                     function createTooltip() {
38575                       // There can only be one tooltip element per directive shown at once.
38576                       if (tooltip) {
38577                         return;
38578                       }
38579
38580                       tooltipLinkedScope = ttScope.$new();
38581                       tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
38582                         if (appendToBody) {
38583                           $document.find('body').append(tooltip);
38584                         } else {
38585                           element.after(tooltip);
38586                         }
38587                       });
38588
38589                       prepObservers();
38590                     }
38591
38592                     function removeTooltip() {
38593                       cancelShow();
38594                       cancelHide();
38595                       unregisterObservers();
38596
38597                       if (tooltip) {
38598                         tooltip.remove();
38599                         tooltip = null;
38600                       }
38601                       if (tooltipLinkedScope) {
38602                         tooltipLinkedScope.$destroy();
38603                         tooltipLinkedScope = null;
38604                       }
38605                     }
38606
38607                     /**
38608                      * Set the initial scope values. Once
38609                      * the tooltip is created, the observers
38610                      * will be added to keep things in sync.
38611                      */
38612                     function prepareTooltip() {
38613                       ttScope.title = attrs[prefix + 'Title'];
38614                       if (contentParse) {
38615                         ttScope.content = contentParse(scope);
38616                       } else {
38617                         ttScope.content = attrs[ttType];
38618                       }
38619
38620                       ttScope.popupClass = attrs[prefix + 'Class'];
38621                       ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
38622
38623                       var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
38624                       var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
38625                       ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
38626                       ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
38627                     }
38628
38629                     function assignIsOpen(isOpen) {
38630                       if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
38631                         isOpenParse.assign(scope, isOpen);
38632                       }
38633                     }
38634
38635                     ttScope.contentExp = function() {
38636                       return ttScope.content;
38637                     };
38638
38639                     /**
38640                      * Observe the relevant attributes.
38641                      */
38642                     attrs.$observe('disabled', function(val) {
38643                       if (val) {
38644                         cancelShow();
38645                       }
38646
38647                       if (val && ttScope.isOpen) {
38648                         hide();
38649                       }
38650                     });
38651
38652                     if (isOpenParse) {
38653                       scope.$watch(isOpenParse, function(val) {
38654                         if (ttScope && !val === ttScope.isOpen) {
38655                           toggleTooltipBind();
38656                         }
38657                       });
38658                     }
38659
38660                     function prepObservers() {
38661                       observers.length = 0;
38662
38663                       if (contentParse) {
38664                         observers.push(
38665                           scope.$watch(contentParse, function(val) {
38666                             ttScope.content = val;
38667                             if (!val && ttScope.isOpen) {
38668                               hide();
38669                             }
38670                           })
38671                         );
38672
38673                         observers.push(
38674                           tooltipLinkedScope.$watch(function() {
38675                             if (!repositionScheduled) {
38676                               repositionScheduled = true;
38677                               tooltipLinkedScope.$$postDigest(function() {
38678                                 repositionScheduled = false;
38679                                 if (ttScope && ttScope.isOpen) {
38680                                   positionTooltip();
38681                                 }
38682                               });
38683                             }
38684                           })
38685                         );
38686                       } else {
38687                         observers.push(
38688                           attrs.$observe(ttType, function(val) {
38689                             ttScope.content = val;
38690                             if (!val && ttScope.isOpen) {
38691                               hide();
38692                             } else {
38693                               positionTooltip();
38694                             }
38695                           })
38696                         );
38697                       }
38698
38699                       observers.push(
38700                         attrs.$observe(prefix + 'Title', function(val) {
38701                           ttScope.title = val;
38702                           if (ttScope.isOpen) {
38703                             positionTooltip();
38704                           }
38705                         })
38706                       );
38707
38708                       observers.push(
38709                         attrs.$observe(prefix + 'Placement', function(val) {
38710                           ttScope.placement = val ? val : options.placement;
38711                           if (ttScope.isOpen) {
38712                             positionTooltip();
38713                           }
38714                         })
38715                       );
38716                     }
38717
38718                     function unregisterObservers() {
38719                       if (observers.length) {
38720                         angular.forEach(observers, function(observer) {
38721                           observer();
38722                         });
38723                         observers.length = 0;
38724                       }
38725                     }
38726
38727                     // hide tooltips/popovers for outsideClick trigger
38728                     function bodyHideTooltipBind(e) {
38729                       if (!ttScope || !ttScope.isOpen || !tooltip) {
38730                         return;
38731                       }
38732                       // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
38733                       if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
38734                         hideTooltipBind();
38735                       }
38736                     }
38737
38738                     var unregisterTriggers = function() {
38739                       triggers.show.forEach(function(trigger) {
38740                         if (trigger === 'outsideClick') {
38741                           element.off('click', toggleTooltipBind);
38742                         } else {
38743                           element.off(trigger, showTooltipBind);
38744                           element.off(trigger, toggleTooltipBind);
38745                         }
38746                       });
38747                       triggers.hide.forEach(function(trigger) {
38748                         if (trigger === 'outsideClick') {
38749                           $document.off('click', bodyHideTooltipBind);
38750                         } else {
38751                           element.off(trigger, hideTooltipBind);
38752                         }
38753                       });
38754                     };
38755
38756                     function prepTriggers() {
38757                       var val = attrs[prefix + 'Trigger'];
38758                       unregisterTriggers();
38759
38760                       triggers = getTriggers(val);
38761
38762                       if (triggers.show !== 'none') {
38763                         triggers.show.forEach(function(trigger, idx) {
38764                           if (trigger === 'outsideClick') {
38765                             element.on('click', toggleTooltipBind);
38766                             $document.on('click', bodyHideTooltipBind);
38767                           } else if (trigger === triggers.hide[idx]) {
38768                             element.on(trigger, toggleTooltipBind);
38769                           } else if (trigger) {
38770                             element.on(trigger, showTooltipBind);
38771                             element.on(triggers.hide[idx], hideTooltipBind);
38772                           }
38773
38774                           element.on('keypress', function(e) {
38775                             if (e.which === 27) {
38776                               hideTooltipBind();
38777                             }
38778                           });
38779                         });
38780                       }
38781                     }
38782
38783                     prepTriggers();
38784
38785                     var animation = scope.$eval(attrs[prefix + 'Animation']);
38786                     ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
38787
38788                     var appendToBodyVal;
38789                     var appendKey = prefix + 'AppendToBody';
38790                     if (appendKey in attrs && attrs[appendKey] === undefined) {
38791                       appendToBodyVal = true;
38792                     } else {
38793                       appendToBodyVal = scope.$eval(attrs[appendKey]);
38794                     }
38795
38796                     appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
38797
38798                     // if a tooltip is attached to <body> we need to remove it on
38799                     // location change as its parent scope will probably not be destroyed
38800                     // by the change.
38801                     if (appendToBody) {
38802                       scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
38803                         if (ttScope.isOpen) {
38804                           hide();
38805                         }
38806                       });
38807                     }
38808
38809                     // Make sure tooltip is destroyed and removed.
38810                     scope.$on('$destroy', function onDestroyTooltip() {
38811                       unregisterTriggers();
38812                       removeTooltip();
38813                       openedTooltips.remove(ttScope);
38814                       ttScope = null;
38815                     });
38816                   };
38817                 }
38818               };
38819             };
38820           }];
38821         })
38822
38823         // This is mostly ngInclude code but with a custom scope
38824         .directive('uibTooltipTemplateTransclude', [
38825                  '$animate', '$sce', '$compile', '$templateRequest',
38826         function ($animate, $sce, $compile, $templateRequest) {
38827           return {
38828             link: function(scope, elem, attrs) {
38829               var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
38830
38831               var changeCounter = 0,
38832                 currentScope,
38833                 previousElement,
38834                 currentElement;
38835
38836               var cleanupLastIncludeContent = function() {
38837                 if (previousElement) {
38838                   previousElement.remove();
38839                   previousElement = null;
38840                 }
38841
38842                 if (currentScope) {
38843                   currentScope.$destroy();
38844                   currentScope = null;
38845                 }
38846
38847                 if (currentElement) {
38848                   $animate.leave(currentElement).then(function() {
38849                     previousElement = null;
38850                   });
38851                   previousElement = currentElement;
38852                   currentElement = null;
38853                 }
38854               };
38855
38856               scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
38857                 var thisChangeId = ++changeCounter;
38858
38859                 if (src) {
38860                   //set the 2nd param to true to ignore the template request error so that the inner
38861                   //contents and scope can be cleaned up.
38862                   $templateRequest(src, true).then(function(response) {
38863                     if (thisChangeId !== changeCounter) { return; }
38864                     var newScope = origScope.$new();
38865                     var template = response;
38866
38867                     var clone = $compile(template)(newScope, function(clone) {
38868                       cleanupLastIncludeContent();
38869                       $animate.enter(clone, elem);
38870                     });
38871
38872                     currentScope = newScope;
38873                     currentElement = clone;
38874
38875                     currentScope.$emit('$includeContentLoaded', src);
38876                   }, function() {
38877                     if (thisChangeId === changeCounter) {
38878                       cleanupLastIncludeContent();
38879                       scope.$emit('$includeContentError', src);
38880                     }
38881                   });
38882                   scope.$emit('$includeContentRequested', src);
38883                 } else {
38884                   cleanupLastIncludeContent();
38885                 }
38886               });
38887
38888               scope.$on('$destroy', cleanupLastIncludeContent);
38889             }
38890           };
38891         }])
38892
38893         /**
38894          * Note that it's intentional that these classes are *not* applied through $animate.
38895          * They must not be animated as they're expected to be present on the tooltip on
38896          * initialization.
38897          */
38898         .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
38899           return {
38900             restrict: 'A',
38901             link: function(scope, element, attrs) {
38902               // need to set the primary position so the
38903               // arrow has space during position measure.
38904               // tooltip.positionTooltip()
38905               if (scope.placement) {
38906                 // // There are no top-left etc... classes
38907                 // // in TWBS, so we need the primary position.
38908                 var position = $uibPosition.parsePlacement(scope.placement);
38909                 element.addClass(position[0]);
38910               } else {
38911                 element.addClass('top');
38912               }
38913
38914               if (scope.popupClass) {
38915                 element.addClass(scope.popupClass);
38916               }
38917
38918               if (scope.animation()) {
38919                 element.addClass(attrs.tooltipAnimationClass);
38920               }
38921             }
38922           };
38923         }])
38924
38925         .directive('uibTooltipPopup', function() {
38926           return {
38927             replace: true,
38928             scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38929             templateUrl: 'uib/template/tooltip/tooltip-popup.html'
38930           };
38931         })
38932
38933         .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
38934           return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
38935         }])
38936
38937         .directive('uibTooltipTemplatePopup', function() {
38938           return {
38939             replace: true,
38940             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38941               originScope: '&' },
38942             templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
38943           };
38944         })
38945
38946         .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
38947           return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
38948             useContentExp: true
38949           });
38950         }])
38951
38952         .directive('uibTooltipHtmlPopup', function() {
38953           return {
38954             replace: true,
38955             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38956             templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
38957           };
38958         })
38959
38960         .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
38961           return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
38962             useContentExp: true
38963           });
38964         }]);
38965
38966         /**
38967          * The following features are still outstanding: popup delay, animation as a
38968          * function, placement as a function, inside, support for more triggers than
38969          * just mouse enter/leave, and selector delegatation.
38970          */
38971         angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
38972
38973         .directive('uibPopoverTemplatePopup', function() {
38974           return {
38975             replace: true,
38976             scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38977               originScope: '&' },
38978             templateUrl: 'uib/template/popover/popover-template.html'
38979           };
38980         })
38981
38982         .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
38983           return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
38984             useContentExp: true
38985           });
38986         }])
38987
38988         .directive('uibPopoverHtmlPopup', function() {
38989           return {
38990             replace: true,
38991             scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38992             templateUrl: 'uib/template/popover/popover-html.html'
38993           };
38994         })
38995
38996         .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
38997           return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
38998             useContentExp: true
38999           });
39000         }])
39001
39002         .directive('uibPopoverPopup', function() {
39003           return {
39004             replace: true,
39005             scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
39006             templateUrl: 'uib/template/popover/popover.html'
39007           };
39008         })
39009
39010         .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
39011           return $uibTooltip('uibPopover', 'popover', 'click');
39012         }]);
39013
39014         angular.module('ui.bootstrap.progressbar', [])
39015
39016         .constant('uibProgressConfig', {
39017           animate: true,
39018           max: 100
39019         })
39020
39021         .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
39022           var self = this,
39023               animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
39024
39025           this.bars = [];
39026           $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
39027
39028           this.addBar = function(bar, element, attrs) {
39029             if (!animate) {
39030               element.css({'transition': 'none'});
39031             }
39032
39033             this.bars.push(bar);
39034
39035             bar.max = $scope.max;
39036             bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
39037
39038             bar.$watch('value', function(value) {
39039               bar.recalculatePercentage();
39040             });
39041
39042             bar.recalculatePercentage = function() {
39043               var totalPercentage = self.bars.reduce(function(total, bar) {
39044                 bar.percent = +(100 * bar.value / bar.max).toFixed(2);
39045                 return total + bar.percent;
39046               }, 0);
39047
39048               if (totalPercentage > 100) {
39049                 bar.percent -= totalPercentage - 100;
39050               }
39051             };
39052
39053             bar.$on('$destroy', function() {
39054               element = null;
39055               self.removeBar(bar);
39056             });
39057           };
39058
39059           this.removeBar = function(bar) {
39060             this.bars.splice(this.bars.indexOf(bar), 1);
39061             this.bars.forEach(function (bar) {
39062               bar.recalculatePercentage();
39063             });
39064           };
39065
39066           $scope.$watch('max', function(max) {
39067             self.bars.forEach(function(bar) {
39068               bar.max = $scope.max;
39069               bar.recalculatePercentage();
39070             });
39071           });
39072         }])
39073
39074         .directive('uibProgress', function() {
39075           return {
39076             replace: true,
39077             transclude: true,
39078             controller: 'UibProgressController',
39079             require: 'uibProgress',
39080             scope: {
39081               max: '=?'
39082             },
39083             templateUrl: 'uib/template/progressbar/progress.html'
39084           };
39085         })
39086
39087         .directive('uibBar', function() {
39088           return {
39089             replace: true,
39090             transclude: true,
39091             require: '^uibProgress',
39092             scope: {
39093               value: '=',
39094               type: '@'
39095             },
39096             templateUrl: 'uib/template/progressbar/bar.html',
39097             link: function(scope, element, attrs, progressCtrl) {
39098               progressCtrl.addBar(scope, element, attrs);
39099             }
39100           };
39101         })
39102
39103         .directive('uibProgressbar', function() {
39104           return {
39105             replace: true,
39106             transclude: true,
39107             controller: 'UibProgressController',
39108             scope: {
39109               value: '=',
39110               max: '=?',
39111               type: '@'
39112             },
39113             templateUrl: 'uib/template/progressbar/progressbar.html',
39114             link: function(scope, element, attrs, progressCtrl) {
39115               progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
39116             }
39117           };
39118         });
39119
39120         angular.module('ui.bootstrap.rating', [])
39121
39122         .constant('uibRatingConfig', {
39123           max: 5,
39124           stateOn: null,
39125           stateOff: null,
39126           titles : ['one', 'two', 'three', 'four', 'five']
39127         })
39128
39129         .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
39130           var ngModelCtrl = { $setViewValue: angular.noop };
39131
39132           this.init = function(ngModelCtrl_) {
39133             ngModelCtrl = ngModelCtrl_;
39134             ngModelCtrl.$render = this.render;
39135
39136             ngModelCtrl.$formatters.push(function(value) {
39137               if (angular.isNumber(value) && value << 0 !== value) {
39138                 value = Math.round(value);
39139               }
39140
39141               return value;
39142             });
39143
39144             this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
39145             this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
39146             var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
39147             this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
39148               tmpTitles : ratingConfig.titles;
39149
39150             var ratingStates = angular.isDefined($attrs.ratingStates) ?
39151               $scope.$parent.$eval($attrs.ratingStates) :
39152               new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
39153             $scope.range = this.buildTemplateObjects(ratingStates);
39154           };
39155
39156           this.buildTemplateObjects = function(states) {
39157             for (var i = 0, n = states.length; i < n; i++) {
39158               states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
39159             }
39160             return states;
39161           };
39162
39163           this.getTitle = function(index) {
39164             if (index >= this.titles.length) {
39165               return index + 1;
39166             }
39167
39168             return this.titles[index];
39169           };
39170
39171           $scope.rate = function(value) {
39172             if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
39173               ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
39174               ngModelCtrl.$render();
39175             }
39176           };
39177
39178           $scope.enter = function(value) {
39179             if (!$scope.readonly) {
39180               $scope.value = value;
39181             }
39182             $scope.onHover({value: value});
39183           };
39184
39185           $scope.reset = function() {
39186             $scope.value = ngModelCtrl.$viewValue;
39187             $scope.onLeave();
39188           };
39189
39190           $scope.onKeydown = function(evt) {
39191             if (/(37|38|39|40)/.test(evt.which)) {
39192               evt.preventDefault();
39193               evt.stopPropagation();
39194               $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
39195             }
39196           };
39197
39198           this.render = function() {
39199             $scope.value = ngModelCtrl.$viewValue;
39200           };
39201         }])
39202
39203         .directive('uibRating', function() {
39204           return {
39205             require: ['uibRating', 'ngModel'],
39206             scope: {
39207               readonly: '=?',
39208               onHover: '&',
39209               onLeave: '&'
39210             },
39211             controller: 'UibRatingController',
39212             templateUrl: 'uib/template/rating/rating.html',
39213             replace: true,
39214             link: function(scope, element, attrs, ctrls) {
39215               var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39216               ratingCtrl.init(ngModelCtrl);
39217             }
39218           };
39219         });
39220
39221         angular.module('ui.bootstrap.tabs', [])
39222
39223         .controller('UibTabsetController', ['$scope', function ($scope) {
39224           var ctrl = this,
39225               tabs = ctrl.tabs = $scope.tabs = [];
39226
39227           ctrl.select = function(selectedTab) {
39228             angular.forEach(tabs, function(tab) {
39229               if (tab.active && tab !== selectedTab) {
39230                 tab.active = false;
39231                 tab.onDeselect();
39232                 selectedTab.selectCalled = false;
39233               }
39234             });
39235             selectedTab.active = true;
39236             // only call select if it has not already been called
39237             if (!selectedTab.selectCalled) {
39238               selectedTab.onSelect();
39239               selectedTab.selectCalled = true;
39240             }
39241           };
39242
39243           ctrl.addTab = function addTab(tab) {
39244             tabs.push(tab);
39245             // we can't run the select function on the first tab
39246             // since that would select it twice
39247             if (tabs.length === 1 && tab.active !== false) {
39248               tab.active = true;
39249             } else if (tab.active) {
39250               ctrl.select(tab);
39251             } else {
39252               tab.active = false;
39253             }
39254           };
39255
39256           ctrl.removeTab = function removeTab(tab) {
39257             var index = tabs.indexOf(tab);
39258             //Select a new tab if the tab to be removed is selected and not destroyed
39259             if (tab.active && tabs.length > 1 && !destroyed) {
39260               //If this is the last tab, select the previous tab. else, the next tab.
39261               var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
39262               ctrl.select(tabs[newActiveIndex]);
39263             }
39264             tabs.splice(index, 1);
39265           };
39266
39267           var destroyed;
39268           $scope.$on('$destroy', function() {
39269             destroyed = true;
39270           });
39271         }])
39272
39273         .directive('uibTabset', function() {
39274           return {
39275             transclude: true,
39276             replace: true,
39277             scope: {
39278               type: '@'
39279             },
39280             controller: 'UibTabsetController',
39281             templateUrl: 'uib/template/tabs/tabset.html',
39282             link: function(scope, element, attrs) {
39283               scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
39284               scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
39285             }
39286           };
39287         })
39288
39289         .directive('uibTab', ['$parse', function($parse) {
39290           return {
39291             require: '^uibTabset',
39292             replace: true,
39293             templateUrl: 'uib/template/tabs/tab.html',
39294             transclude: true,
39295             scope: {
39296               active: '=?',
39297               heading: '@',
39298               onSelect: '&select', //This callback is called in contentHeadingTransclude
39299                                   //once it inserts the tab's content into the dom
39300               onDeselect: '&deselect'
39301             },
39302             controller: function() {
39303               //Empty controller so other directives can require being 'under' a tab
39304             },
39305             controllerAs: 'tab',
39306             link: function(scope, elm, attrs, tabsetCtrl, transclude) {
39307               scope.$watch('active', function(active) {
39308                 if (active) {
39309                   tabsetCtrl.select(scope);
39310                 }
39311               });
39312
39313               scope.disabled = false;
39314               if (attrs.disable) {
39315                 scope.$parent.$watch($parse(attrs.disable), function(value) {
39316                   scope.disabled = !! value;
39317                 });
39318               }
39319
39320               scope.select = function() {
39321                 if (!scope.disabled) {
39322                   scope.active = true;
39323                 }
39324               };
39325
39326               tabsetCtrl.addTab(scope);
39327               scope.$on('$destroy', function() {
39328                 tabsetCtrl.removeTab(scope);
39329               });
39330
39331               //We need to transclude later, once the content container is ready.
39332               //when this link happens, we're inside a tab heading.
39333               scope.$transcludeFn = transclude;
39334             }
39335           };
39336         }])
39337
39338         .directive('uibTabHeadingTransclude', function() {
39339           return {
39340             restrict: 'A',
39341             require: '^uibTab',
39342             link: function(scope, elm) {
39343               scope.$watch('headingElement', function updateHeadingElement(heading) {
39344                 if (heading) {
39345                   elm.html('');
39346                   elm.append(heading);
39347                 }
39348               });
39349             }
39350           };
39351         })
39352
39353         .directive('uibTabContentTransclude', function() {
39354           return {
39355             restrict: 'A',
39356             require: '^uibTabset',
39357             link: function(scope, elm, attrs) {
39358               var tab = scope.$eval(attrs.uibTabContentTransclude);
39359
39360               //Now our tab is ready to be transcluded: both the tab heading area
39361               //and the tab content area are loaded.  Transclude 'em both.
39362               tab.$transcludeFn(tab.$parent, function(contents) {
39363                 angular.forEach(contents, function(node) {
39364                   if (isTabHeading(node)) {
39365                     //Let tabHeadingTransclude know.
39366                     tab.headingElement = node;
39367                   } else {
39368                     elm.append(node);
39369                   }
39370                 });
39371               });
39372             }
39373           };
39374
39375           function isTabHeading(node) {
39376             return node.tagName && (
39377               node.hasAttribute('uib-tab-heading') ||
39378               node.hasAttribute('data-uib-tab-heading') ||
39379               node.hasAttribute('x-uib-tab-heading') ||
39380               node.tagName.toLowerCase() === 'uib-tab-heading' ||
39381               node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
39382               node.tagName.toLowerCase() === 'x-uib-tab-heading'
39383             );
39384           }
39385         });
39386
39387         angular.module('ui.bootstrap.timepicker', [])
39388
39389         .constant('uibTimepickerConfig', {
39390           hourStep: 1,
39391           minuteStep: 1,
39392           secondStep: 1,
39393           showMeridian: true,
39394           showSeconds: false,
39395           meridians: null,
39396           readonlyInput: false,
39397           mousewheel: true,
39398           arrowkeys: true,
39399           showSpinners: true
39400         })
39401
39402         .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
39403           var selected = new Date(),
39404               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
39405               meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
39406
39407           $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
39408           $element.removeAttr('tabindex');
39409
39410           this.init = function(ngModelCtrl_, inputs) {
39411             ngModelCtrl = ngModelCtrl_;
39412             ngModelCtrl.$render = this.render;
39413
39414             ngModelCtrl.$formatters.unshift(function(modelValue) {
39415               return modelValue ? new Date(modelValue) : null;
39416             });
39417
39418             var hoursInputEl = inputs.eq(0),
39419                 minutesInputEl = inputs.eq(1),
39420                 secondsInputEl = inputs.eq(2);
39421
39422             var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
39423
39424             if (mousewheel) {
39425               this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39426             }
39427
39428             var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
39429             if (arrowkeys) {
39430               this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39431             }
39432
39433             $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
39434             this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39435           };
39436
39437           var hourStep = timepickerConfig.hourStep;
39438           if ($attrs.hourStep) {
39439             $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
39440               hourStep = parseInt(value, 10);
39441             });
39442           }
39443
39444           var minuteStep = timepickerConfig.minuteStep;
39445           if ($attrs.minuteStep) {
39446             $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
39447               minuteStep = parseInt(value, 10);
39448             });
39449           }
39450
39451           var min;
39452           $scope.$parent.$watch($parse($attrs.min), function(value) {
39453             var dt = new Date(value);
39454             min = isNaN(dt) ? undefined : dt;
39455           });
39456
39457           var max;
39458           $scope.$parent.$watch($parse($attrs.max), function(value) {
39459             var dt = new Date(value);
39460             max = isNaN(dt) ? undefined : dt;
39461           });
39462
39463           var disabled = false;
39464           if ($attrs.ngDisabled) {
39465             $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
39466               disabled = value;
39467             });
39468           }
39469
39470           $scope.noIncrementHours = function() {
39471             var incrementedSelected = addMinutes(selected, hourStep * 60);
39472             return disabled || incrementedSelected > max ||
39473               incrementedSelected < selected && incrementedSelected < min;
39474           };
39475
39476           $scope.noDecrementHours = function() {
39477             var decrementedSelected = addMinutes(selected, -hourStep * 60);
39478             return disabled || decrementedSelected < min ||
39479               decrementedSelected > selected && decrementedSelected > max;
39480           };
39481
39482           $scope.noIncrementMinutes = function() {
39483             var incrementedSelected = addMinutes(selected, minuteStep);
39484             return disabled || incrementedSelected > max ||
39485               incrementedSelected < selected && incrementedSelected < min;
39486           };
39487
39488           $scope.noDecrementMinutes = function() {
39489             var decrementedSelected = addMinutes(selected, -minuteStep);
39490             return disabled || decrementedSelected < min ||
39491               decrementedSelected > selected && decrementedSelected > max;
39492           };
39493
39494           $scope.noIncrementSeconds = function() {
39495             var incrementedSelected = addSeconds(selected, secondStep);
39496             return disabled || incrementedSelected > max ||
39497               incrementedSelected < selected && incrementedSelected < min;
39498           };
39499
39500           $scope.noDecrementSeconds = function() {
39501             var decrementedSelected = addSeconds(selected, -secondStep);
39502             return disabled || decrementedSelected < min ||
39503               decrementedSelected > selected && decrementedSelected > max;
39504           };
39505
39506           $scope.noToggleMeridian = function() {
39507             if (selected.getHours() < 12) {
39508               return disabled || addMinutes(selected, 12 * 60) > max;
39509             }
39510
39511             return disabled || addMinutes(selected, -12 * 60) < min;
39512           };
39513
39514           var secondStep = timepickerConfig.secondStep;
39515           if ($attrs.secondStep) {
39516             $scope.$parent.$watch($parse($attrs.secondStep), function(value) {
39517               secondStep = parseInt(value, 10);
39518             });
39519           }
39520
39521           $scope.showSeconds = timepickerConfig.showSeconds;
39522           if ($attrs.showSeconds) {
39523             $scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
39524               $scope.showSeconds = !!value;
39525             });
39526           }
39527
39528           // 12H / 24H mode
39529           $scope.showMeridian = timepickerConfig.showMeridian;
39530           if ($attrs.showMeridian) {
39531             $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
39532               $scope.showMeridian = !!value;
39533
39534               if (ngModelCtrl.$error.time) {
39535                 // Evaluate from template
39536                 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
39537                 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39538                   selected.setHours(hours);
39539                   refresh();
39540                 }
39541               } else {
39542                 updateTemplate();
39543               }
39544             });
39545           }
39546
39547           // Get $scope.hours in 24H mode if valid
39548           function getHoursFromTemplate() {
39549             var hours = parseInt($scope.hours, 10);
39550             var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
39551               hours >= 0 && hours < 24;
39552             if (!valid) {
39553               return undefined;
39554             }
39555
39556             if ($scope.showMeridian) {
39557               if (hours === 12) {
39558                 hours = 0;
39559               }
39560               if ($scope.meridian === meridians[1]) {
39561                 hours = hours + 12;
39562               }
39563             }
39564             return hours;
39565           }
39566
39567           function getMinutesFromTemplate() {
39568             var minutes = parseInt($scope.minutes, 10);
39569             return minutes >= 0 && minutes < 60 ? minutes : undefined;
39570           }
39571
39572           function getSecondsFromTemplate() {
39573             var seconds = parseInt($scope.seconds, 10);
39574             return seconds >= 0 && seconds < 60 ? seconds : undefined;
39575           }
39576
39577           function pad(value) {
39578             if (value === null) {
39579               return '';
39580             }
39581
39582             return angular.isDefined(value) && value.toString().length < 2 ?
39583               '0' + value : value.toString();
39584           }
39585
39586           // Respond on mousewheel spin
39587           this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39588             var isScrollingUp = function(e) {
39589               if (e.originalEvent) {
39590                 e = e.originalEvent;
39591               }
39592               //pick correct delta variable depending on event
39593               var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
39594               return e.detail || delta > 0;
39595             };
39596
39597             hoursInputEl.bind('mousewheel wheel', function(e) {
39598               if (!disabled) {
39599                 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
39600               }
39601               e.preventDefault();
39602             });
39603
39604             minutesInputEl.bind('mousewheel wheel', function(e) {
39605               if (!disabled) {
39606                 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
39607               }
39608               e.preventDefault();
39609             });
39610
39611              secondsInputEl.bind('mousewheel wheel', function(e) {
39612               if (!disabled) {
39613                 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
39614               }
39615               e.preventDefault();
39616             });
39617           };
39618
39619           // Respond on up/down arrowkeys
39620           this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39621             hoursInputEl.bind('keydown', function(e) {
39622               if (!disabled) {
39623                 if (e.which === 38) { // up
39624                   e.preventDefault();
39625                   $scope.incrementHours();
39626                   $scope.$apply();
39627                 } else if (e.which === 40) { // down
39628                   e.preventDefault();
39629                   $scope.decrementHours();
39630                   $scope.$apply();
39631                 }
39632               }
39633             });
39634
39635             minutesInputEl.bind('keydown', function(e) {
39636               if (!disabled) {
39637                 if (e.which === 38) { // up
39638                   e.preventDefault();
39639                   $scope.incrementMinutes();
39640                   $scope.$apply();
39641                 } else if (e.which === 40) { // down
39642                   e.preventDefault();
39643                   $scope.decrementMinutes();
39644                   $scope.$apply();
39645                 }
39646               }
39647             });
39648
39649             secondsInputEl.bind('keydown', function(e) {
39650               if (!disabled) {
39651                 if (e.which === 38) { // up
39652                   e.preventDefault();
39653                   $scope.incrementSeconds();
39654                   $scope.$apply();
39655                 } else if (e.which === 40) { // down
39656                   e.preventDefault();
39657                   $scope.decrementSeconds();
39658                   $scope.$apply();
39659                 }
39660               }
39661             });
39662           };
39663
39664           this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39665             if ($scope.readonlyInput) {
39666               $scope.updateHours = angular.noop;
39667               $scope.updateMinutes = angular.noop;
39668               $scope.updateSeconds = angular.noop;
39669               return;
39670             }
39671
39672             var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
39673               ngModelCtrl.$setViewValue(null);
39674               ngModelCtrl.$setValidity('time', false);
39675               if (angular.isDefined(invalidHours)) {
39676                 $scope.invalidHours = invalidHours;
39677               }
39678
39679               if (angular.isDefined(invalidMinutes)) {
39680                 $scope.invalidMinutes = invalidMinutes;
39681               }
39682
39683               if (angular.isDefined(invalidSeconds)) {
39684                 $scope.invalidSeconds = invalidSeconds;
39685               }
39686             };
39687
39688             $scope.updateHours = function() {
39689               var hours = getHoursFromTemplate(),
39690                 minutes = getMinutesFromTemplate();
39691
39692               ngModelCtrl.$setDirty();
39693
39694               if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39695                 selected.setHours(hours);
39696                 selected.setMinutes(minutes);
39697                 if (selected < min || selected > max) {
39698                   invalidate(true);
39699                 } else {
39700                   refresh('h');
39701                 }
39702               } else {
39703                 invalidate(true);
39704               }
39705             };
39706
39707             hoursInputEl.bind('blur', function(e) {
39708               ngModelCtrl.$setTouched();
39709               if ($scope.hours === null || $scope.hours === '') {
39710                 invalidate(true);
39711               } else if (!$scope.invalidHours && $scope.hours < 10) {
39712                 $scope.$apply(function() {
39713                   $scope.hours = pad($scope.hours);
39714                 });
39715               }
39716             });
39717
39718             $scope.updateMinutes = function() {
39719               var minutes = getMinutesFromTemplate(),
39720                 hours = getHoursFromTemplate();
39721
39722               ngModelCtrl.$setDirty();
39723
39724               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39725                 selected.setHours(hours);
39726                 selected.setMinutes(minutes);
39727                 if (selected < min || selected > max) {
39728                   invalidate(undefined, true);
39729                 } else {
39730                   refresh('m');
39731                 }
39732               } else {
39733                 invalidate(undefined, true);
39734               }
39735             };
39736
39737             minutesInputEl.bind('blur', function(e) {
39738               ngModelCtrl.$setTouched();
39739               if ($scope.minutes === null) {
39740                 invalidate(undefined, true);
39741               } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
39742                 $scope.$apply(function() {
39743                   $scope.minutes = pad($scope.minutes);
39744                 });
39745               }
39746             });
39747
39748             $scope.updateSeconds = function() {
39749               var seconds = getSecondsFromTemplate();
39750
39751               ngModelCtrl.$setDirty();
39752
39753               if (angular.isDefined(seconds)) {
39754                 selected.setSeconds(seconds);
39755                 refresh('s');
39756               } else {
39757                 invalidate(undefined, undefined, true);
39758               }
39759             };
39760
39761             secondsInputEl.bind('blur', function(e) {
39762               if (!$scope.invalidSeconds && $scope.seconds < 10) {
39763                 $scope.$apply( function() {
39764                   $scope.seconds = pad($scope.seconds);
39765                 });
39766               }
39767             });
39768
39769           };
39770
39771           this.render = function() {
39772             var date = ngModelCtrl.$viewValue;
39773
39774             if (isNaN(date)) {
39775               ngModelCtrl.$setValidity('time', false);
39776               $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
39777             } else {
39778               if (date) {
39779                 selected = date;
39780               }
39781
39782               if (selected < min || selected > max) {
39783                 ngModelCtrl.$setValidity('time', false);
39784                 $scope.invalidHours = true;
39785                 $scope.invalidMinutes = true;
39786               } else {
39787                 makeValid();
39788               }
39789               updateTemplate();
39790             }
39791           };
39792
39793           // Call internally when we know that model is valid.
39794           function refresh(keyboardChange) {
39795             makeValid();
39796             ngModelCtrl.$setViewValue(new Date(selected));
39797             updateTemplate(keyboardChange);
39798           }
39799
39800           function makeValid() {
39801             ngModelCtrl.$setValidity('time', true);
39802             $scope.invalidHours = false;
39803             $scope.invalidMinutes = false;
39804             $scope.invalidSeconds = false;
39805           }
39806
39807           function updateTemplate(keyboardChange) {
39808             if (!ngModelCtrl.$modelValue) {
39809               $scope.hours = null;
39810               $scope.minutes = null;
39811               $scope.seconds = null;
39812               $scope.meridian = meridians[0];
39813             } else {
39814               var hours = selected.getHours(),
39815                 minutes = selected.getMinutes(),
39816                 seconds = selected.getSeconds();
39817
39818               if ($scope.showMeridian) {
39819                 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
39820               }
39821
39822               $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
39823               if (keyboardChange !== 'm') {
39824                 $scope.minutes = pad(minutes);
39825               }
39826               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39827
39828               if (keyboardChange !== 's') {
39829                 $scope.seconds = pad(seconds);
39830               }
39831               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39832             }
39833           }
39834
39835           function addSecondsToSelected(seconds) {
39836             selected = addSeconds(selected, seconds);
39837             refresh();
39838           }
39839
39840           function addMinutes(selected, minutes) {
39841             return addSeconds(selected, minutes*60);
39842           }
39843
39844           function addSeconds(date, seconds) {
39845             var dt = new Date(date.getTime() + seconds * 1000);
39846             var newDate = new Date(date);
39847             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
39848             return newDate;
39849           }
39850
39851           $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
39852             $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
39853
39854           $scope.incrementHours = function() {
39855             if (!$scope.noIncrementHours()) {
39856               addSecondsToSelected(hourStep * 60 * 60);
39857             }
39858           };
39859
39860           $scope.decrementHours = function() {
39861             if (!$scope.noDecrementHours()) {
39862               addSecondsToSelected(-hourStep * 60 * 60);
39863             }
39864           };
39865
39866           $scope.incrementMinutes = function() {
39867             if (!$scope.noIncrementMinutes()) {
39868               addSecondsToSelected(minuteStep * 60);
39869             }
39870           };
39871
39872           $scope.decrementMinutes = function() {
39873             if (!$scope.noDecrementMinutes()) {
39874               addSecondsToSelected(-minuteStep * 60);
39875             }
39876           };
39877
39878           $scope.incrementSeconds = function() {
39879             if (!$scope.noIncrementSeconds()) {
39880               addSecondsToSelected(secondStep);
39881             }
39882           };
39883
39884           $scope.decrementSeconds = function() {
39885             if (!$scope.noDecrementSeconds()) {
39886               addSecondsToSelected(-secondStep);
39887             }
39888           };
39889
39890           $scope.toggleMeridian = function() {
39891             var minutes = getMinutesFromTemplate(),
39892                 hours = getHoursFromTemplate();
39893
39894             if (!$scope.noToggleMeridian()) {
39895               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39896                 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
39897               } else {
39898                 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
39899               }
39900             }
39901           };
39902
39903           $scope.blur = function() {
39904             ngModelCtrl.$setTouched();
39905           };
39906         }])
39907
39908         .directive('uibTimepicker', function() {
39909           return {
39910             require: ['uibTimepicker', '?^ngModel'],
39911             controller: 'UibTimepickerController',
39912             controllerAs: 'timepicker',
39913             replace: true,
39914             scope: {},
39915             templateUrl: function(element, attrs) {
39916               return attrs.templateUrl || 'uib/template/timepicker/timepicker.html';
39917             },
39918             link: function(scope, element, attrs, ctrls) {
39919               var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39920
39921               if (ngModelCtrl) {
39922                 timepickerCtrl.init(ngModelCtrl, element.find('input'));
39923               }
39924             }
39925           };
39926         });
39927
39928         angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
39929
39930         /**
39931          * A helper service that can parse typeahead's syntax (string provided by users)
39932          * Extracted to a separate service for ease of unit testing
39933          */
39934           .factory('uibTypeaheadParser', ['$parse', function($parse) {
39935             //                      00000111000000000000022200000000000000003333333333333330000000000044000
39936             var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
39937             return {
39938               parse: function(input) {
39939                 var match = input.match(TYPEAHEAD_REGEXP);
39940                 if (!match) {
39941                   throw new Error(
39942                     'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
39943                       ' but got "' + input + '".');
39944                 }
39945
39946                 return {
39947                   itemName: match[3],
39948                   source: $parse(match[4]),
39949                   viewMapper: $parse(match[2] || match[1]),
39950                   modelMapper: $parse(match[1])
39951                 };
39952               }
39953             };
39954           }])
39955
39956           .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
39957             function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
39958             var HOT_KEYS = [9, 13, 27, 38, 40];
39959             var eventDebounceTime = 200;
39960             var modelCtrl, ngModelOptions;
39961             //SUPPORTED ATTRIBUTES (OPTIONS)
39962
39963             //minimal no of characters that needs to be entered before typeahead kicks-in
39964             var minLength = originalScope.$eval(attrs.typeaheadMinLength);
39965             if (!minLength && minLength !== 0) {
39966               minLength = 1;
39967             }
39968
39969             //minimal wait time after last character typed before typeahead kicks-in
39970             var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
39971
39972             //should it restrict model values to the ones selected from the popup only?
39973             var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
39974             originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
39975               isEditable = newVal !== false;
39976             });
39977
39978             //binding to a variable that indicates if matches are being retrieved asynchronously
39979             var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
39980
39981             //a callback executed when a match is selected
39982             var onSelectCallback = $parse(attrs.typeaheadOnSelect);
39983
39984             //should it select highlighted popup value when losing focus?
39985             var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
39986
39987             //binding to a variable that indicates if there were no results after the query is completed
39988             var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
39989
39990             var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
39991
39992             var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
39993
39994             var appendTo = attrs.typeaheadAppendTo ?
39995               originalScope.$eval(attrs.typeaheadAppendTo) : null;
39996
39997             var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
39998
39999             //If input matches an item of the list exactly, select it automatically
40000             var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
40001
40002             //binding to a variable that indicates if dropdown is open
40003             var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
40004
40005             var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
40006
40007             //INTERNAL VARIABLES
40008
40009             //model setter executed upon match selection
40010             var parsedModel = $parse(attrs.ngModel);
40011             var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
40012             var $setModelValue = function(scope, newValue) {
40013               if (angular.isFunction(parsedModel(originalScope)) &&
40014                 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
40015                 return invokeModelSetter(scope, {$$$p: newValue});
40016               }
40017
40018               return parsedModel.assign(scope, newValue);
40019             };
40020
40021             //expressions used by typeahead
40022             var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
40023
40024             var hasFocus;
40025
40026             //Used to avoid bug in iOS webview where iOS keyboard does not fire
40027             //mousedown & mouseup events
40028             //Issue #3699
40029             var selected;
40030
40031             //create a child scope for the typeahead directive so we are not polluting original scope
40032             //with typeahead-specific data (matches, query etc.)
40033             var scope = originalScope.$new();
40034             var offDestroy = originalScope.$on('$destroy', function() {
40035               scope.$destroy();
40036             });
40037             scope.$on('$destroy', offDestroy);
40038
40039             // WAI-ARIA
40040             var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
40041             element.attr({
40042               'aria-autocomplete': 'list',
40043               'aria-expanded': false,
40044               'aria-owns': popupId
40045             });
40046
40047             var inputsContainer, hintInputElem;
40048             //add read-only input to show hint
40049             if (showHint) {
40050               inputsContainer = angular.element('<div></div>');
40051               inputsContainer.css('position', 'relative');
40052               element.after(inputsContainer);
40053               hintInputElem = element.clone();
40054               hintInputElem.attr('placeholder', '');
40055               hintInputElem.val('');
40056               hintInputElem.css({
40057                 'position': 'absolute',
40058                 'top': '0px',
40059                 'left': '0px',
40060                 'border-color': 'transparent',
40061                 'box-shadow': 'none',
40062                 'opacity': 1,
40063                 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
40064                 'color': '#999'
40065               });
40066               element.css({
40067                 'position': 'relative',
40068                 'vertical-align': 'top',
40069                 'background-color': 'transparent'
40070               });
40071               inputsContainer.append(hintInputElem);
40072               hintInputElem.after(element);
40073             }
40074
40075             //pop-up element used to display matches
40076             var popUpEl = angular.element('<div uib-typeahead-popup></div>');
40077             popUpEl.attr({
40078               id: popupId,
40079               matches: 'matches',
40080               active: 'activeIdx',
40081               select: 'select(activeIdx, evt)',
40082               'move-in-progress': 'moveInProgress',
40083               query: 'query',
40084               position: 'position',
40085               'assign-is-open': 'assignIsOpen(isOpen)',
40086               debounce: 'debounceUpdate'
40087             });
40088             //custom item template
40089             if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
40090               popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
40091             }
40092
40093             if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
40094               popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
40095             }
40096
40097             var resetHint = function() {
40098               if (showHint) {
40099                 hintInputElem.val('');
40100               }
40101             };
40102
40103             var resetMatches = function() {
40104               scope.matches = [];
40105               scope.activeIdx = -1;
40106               element.attr('aria-expanded', false);
40107               resetHint();
40108             };
40109
40110             var getMatchId = function(index) {
40111               return popupId + '-option-' + index;
40112             };
40113
40114             // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
40115             // This attribute is added or removed automatically when the `activeIdx` changes.
40116             scope.$watch('activeIdx', function(index) {
40117               if (index < 0) {
40118                 element.removeAttr('aria-activedescendant');
40119               } else {
40120                 element.attr('aria-activedescendant', getMatchId(index));
40121               }
40122             });
40123
40124             var inputIsExactMatch = function(inputValue, index) {
40125               if (scope.matches.length > index && inputValue) {
40126                 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
40127               }
40128
40129               return false;
40130             };
40131
40132             var getMatchesAsync = function(inputValue, evt) {
40133               var locals = {$viewValue: inputValue};
40134               isLoadingSetter(originalScope, true);
40135               isNoResultsSetter(originalScope, false);
40136               $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
40137                 //it might happen that several async queries were in progress if a user were typing fast
40138                 //but we are interested only in responses that correspond to the current view value
40139                 var onCurrentRequest = inputValue === modelCtrl.$viewValue;
40140                 if (onCurrentRequest && hasFocus) {
40141                   if (matches && matches.length > 0) {
40142                     scope.activeIdx = focusFirst ? 0 : -1;
40143                     isNoResultsSetter(originalScope, false);
40144                     scope.matches.length = 0;
40145
40146                     //transform labels
40147                     for (var i = 0; i < matches.length; i++) {
40148                       locals[parserResult.itemName] = matches[i];
40149                       scope.matches.push({
40150                         id: getMatchId(i),
40151                         label: parserResult.viewMapper(scope, locals),
40152                         model: matches[i]
40153                       });
40154                     }
40155
40156                     scope.query = inputValue;
40157                     //position pop-up with matches - we need to re-calculate its position each time we are opening a window
40158                     //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
40159                     //due to other elements being rendered
40160                     recalculatePosition();
40161
40162                     element.attr('aria-expanded', true);
40163
40164                     //Select the single remaining option if user input matches
40165                     if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
40166                       if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40167                         $$debounce(function() {
40168                           scope.select(0, evt);
40169                         }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40170                       } else {
40171                         scope.select(0, evt);
40172                       }
40173                     }
40174
40175                     if (showHint) {
40176                       var firstLabel = scope.matches[0].label;
40177                       if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
40178                         hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
40179                       }
40180                       else {
40181                         hintInputElem.val('');
40182                       }
40183                     }
40184                   } else {
40185                     resetMatches();
40186                     isNoResultsSetter(originalScope, true);
40187                   }
40188                 }
40189                 if (onCurrentRequest) {
40190                   isLoadingSetter(originalScope, false);
40191                 }
40192               }, function() {
40193                 resetMatches();
40194                 isLoadingSetter(originalScope, false);
40195                 isNoResultsSetter(originalScope, true);
40196               });
40197             };
40198
40199             // bind events only if appendToBody params exist - performance feature
40200             if (appendToBody) {
40201               angular.element($window).on('resize', fireRecalculating);
40202               $document.find('body').on('scroll', fireRecalculating);
40203             }
40204
40205             // Declare the debounced function outside recalculating for
40206             // proper debouncing
40207             var debouncedRecalculate = $$debounce(function() {
40208               // if popup is visible
40209               if (scope.matches.length) {
40210                 recalculatePosition();
40211               }
40212
40213               scope.moveInProgress = false;
40214             }, eventDebounceTime);
40215
40216             // Default progress type
40217             scope.moveInProgress = false;
40218
40219             function fireRecalculating() {
40220               if (!scope.moveInProgress) {
40221                 scope.moveInProgress = true;
40222                 scope.$digest();
40223               }
40224
40225               debouncedRecalculate();
40226             }
40227
40228             // recalculate actual position and set new values to scope
40229             // after digest loop is popup in right position
40230             function recalculatePosition() {
40231               scope.position = appendToBody ? $position.offset(element) : $position.position(element);
40232               scope.position.top += element.prop('offsetHeight');
40233             }
40234
40235             //we need to propagate user's query so we can higlight matches
40236             scope.query = undefined;
40237
40238             //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
40239             var timeoutPromise;
40240
40241             var scheduleSearchWithTimeout = function(inputValue) {
40242               timeoutPromise = $timeout(function() {
40243                 getMatchesAsync(inputValue);
40244               }, waitTime);
40245             };
40246
40247             var cancelPreviousTimeout = function() {
40248               if (timeoutPromise) {
40249                 $timeout.cancel(timeoutPromise);
40250               }
40251             };
40252
40253             resetMatches();
40254
40255             scope.assignIsOpen = function (isOpen) {
40256               isOpenSetter(originalScope, isOpen);
40257             };
40258
40259             scope.select = function(activeIdx, evt) {
40260               //called from within the $digest() cycle
40261               var locals = {};
40262               var model, item;
40263
40264               selected = true;
40265               locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
40266               model = parserResult.modelMapper(originalScope, locals);
40267               $setModelValue(originalScope, model);
40268               modelCtrl.$setValidity('editable', true);
40269               modelCtrl.$setValidity('parse', true);
40270
40271               onSelectCallback(originalScope, {
40272                 $item: item,
40273                 $model: model,
40274                 $label: parserResult.viewMapper(originalScope, locals),
40275                 $event: evt
40276               });
40277
40278               resetMatches();
40279
40280               //return focus to the input element if a match was selected via a mouse click event
40281               // use timeout to avoid $rootScope:inprog error
40282               if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
40283                 $timeout(function() { element[0].focus(); }, 0, false);
40284               }
40285             };
40286
40287             //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
40288             element.on('keydown', function(evt) {
40289               //typeahead is open and an "interesting" key was pressed
40290               if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
40291                 return;
40292               }
40293
40294               // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
40295               if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
40296                 resetMatches();
40297                 scope.$digest();
40298                 return;
40299               }
40300
40301               evt.preventDefault();
40302
40303               switch (evt.which) {
40304                 case 9:
40305                 case 13:
40306                   scope.$apply(function () {
40307                     if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40308                       $$debounce(function() {
40309                         scope.select(scope.activeIdx, evt);
40310                       }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40311                     } else {
40312                       scope.select(scope.activeIdx, evt);
40313                     }
40314                   });
40315                   break;
40316                 case 27:
40317                   evt.stopPropagation();
40318
40319                   resetMatches();
40320                   scope.$digest();
40321                   break;
40322                 case 38:
40323                   scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
40324                   scope.$digest();
40325                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40326                   break;
40327                 case 40:
40328                   scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
40329                   scope.$digest();
40330                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40331                   break;
40332               }
40333             });
40334
40335             element.bind('focus', function (evt) {
40336               hasFocus = true;
40337               if (minLength === 0 && !modelCtrl.$viewValue) {
40338                 $timeout(function() {
40339                   getMatchesAsync(modelCtrl.$viewValue, evt);
40340                 }, 0);
40341               }
40342             });
40343
40344             element.bind('blur', function(evt) {
40345               if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
40346                 selected = true;
40347                 scope.$apply(function() {
40348                   if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
40349                     $$debounce(function() {
40350                       scope.select(scope.activeIdx, evt);
40351                     }, scope.debounceUpdate.blur);
40352                   } else {
40353                     scope.select(scope.activeIdx, evt);
40354                   }
40355                 });
40356               }
40357               if (!isEditable && modelCtrl.$error.editable) {
40358                 modelCtrl.$viewValue = '';
40359                 element.val('');
40360               }
40361               hasFocus = false;
40362               selected = false;
40363             });
40364
40365             // Keep reference to click handler to unbind it.
40366             var dismissClickHandler = function(evt) {
40367               // Issue #3973
40368               // Firefox treats right click as a click on document
40369               if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
40370                 resetMatches();
40371                 if (!$rootScope.$$phase) {
40372                   scope.$digest();
40373                 }
40374               }
40375             };
40376
40377             $document.on('click', dismissClickHandler);
40378
40379             originalScope.$on('$destroy', function() {
40380               $document.off('click', dismissClickHandler);
40381               if (appendToBody || appendTo) {
40382                 $popup.remove();
40383               }
40384
40385               if (appendToBody) {
40386                 angular.element($window).off('resize', fireRecalculating);
40387                 $document.find('body').off('scroll', fireRecalculating);
40388               }
40389               // Prevent jQuery cache memory leak
40390               popUpEl.remove();
40391
40392               if (showHint) {
40393                   inputsContainer.remove();
40394               }
40395             });
40396
40397             var $popup = $compile(popUpEl)(scope);
40398
40399             if (appendToBody) {
40400               $document.find('body').append($popup);
40401             } else if (appendTo) {
40402               angular.element(appendTo).eq(0).append($popup);
40403             } else {
40404               element.after($popup);
40405             }
40406
40407             this.init = function(_modelCtrl, _ngModelOptions) {
40408               modelCtrl = _modelCtrl;
40409               ngModelOptions = _ngModelOptions;
40410
40411               scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
40412
40413               //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
40414               //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
40415               modelCtrl.$parsers.unshift(function(inputValue) {
40416                 hasFocus = true;
40417
40418                 if (minLength === 0 || inputValue && inputValue.length >= minLength) {
40419                   if (waitTime > 0) {
40420                     cancelPreviousTimeout();
40421                     scheduleSearchWithTimeout(inputValue);
40422                   } else {
40423                     getMatchesAsync(inputValue);
40424                   }
40425                 } else {
40426                   isLoadingSetter(originalScope, false);
40427                   cancelPreviousTimeout();
40428                   resetMatches();
40429                 }
40430
40431                 if (isEditable) {
40432                   return inputValue;
40433                 }
40434
40435                 if (!inputValue) {
40436                   // Reset in case user had typed something previously.
40437                   modelCtrl.$setValidity('editable', true);
40438                   return null;
40439                 }
40440
40441                 modelCtrl.$setValidity('editable', false);
40442                 return undefined;
40443               });
40444
40445               modelCtrl.$formatters.push(function(modelValue) {
40446                 var candidateViewValue, emptyViewValue;
40447                 var locals = {};
40448
40449                 // The validity may be set to false via $parsers (see above) if
40450                 // the model is restricted to selected values. If the model
40451                 // is set manually it is considered to be valid.
40452                 if (!isEditable) {
40453                   modelCtrl.$setValidity('editable', true);
40454                 }
40455
40456                 if (inputFormatter) {
40457                   locals.$model = modelValue;
40458                   return inputFormatter(originalScope, locals);
40459                 }
40460
40461                 //it might happen that we don't have enough info to properly render input value
40462                 //we need to check for this situation and simply return model value if we can't apply custom formatting
40463                 locals[parserResult.itemName] = modelValue;
40464                 candidateViewValue = parserResult.viewMapper(originalScope, locals);
40465                 locals[parserResult.itemName] = undefined;
40466                 emptyViewValue = parserResult.viewMapper(originalScope, locals);
40467
40468                 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
40469               });
40470             };
40471           }])
40472
40473           .directive('uibTypeahead', function() {
40474             return {
40475               controller: 'UibTypeaheadController',
40476               require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
40477               link: function(originalScope, element, attrs, ctrls) {
40478                 ctrls[2].init(ctrls[0], ctrls[1]);
40479               }
40480             };
40481           })
40482
40483           .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
40484             return {
40485               scope: {
40486                 matches: '=',
40487                 query: '=',
40488                 active: '=',
40489                 position: '&',
40490                 moveInProgress: '=',
40491                 select: '&',
40492                 assignIsOpen: '&',
40493                 debounce: '&'
40494               },
40495               replace: true,
40496               templateUrl: function(element, attrs) {
40497                 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
40498               },
40499               link: function(scope, element, attrs) {
40500                 scope.templateUrl = attrs.templateUrl;
40501
40502                 scope.isOpen = function() {
40503                   var isDropdownOpen = scope.matches.length > 0;
40504                   scope.assignIsOpen({ isOpen: isDropdownOpen });
40505                   return isDropdownOpen;
40506                 };
40507
40508                 scope.isActive = function(matchIdx) {
40509                   return scope.active === matchIdx;
40510                 };
40511
40512                 scope.selectActive = function(matchIdx) {
40513                   scope.active = matchIdx;
40514                 };
40515
40516                 scope.selectMatch = function(activeIdx, evt) {
40517                   var debounce = scope.debounce();
40518                   if (angular.isNumber(debounce) || angular.isObject(debounce)) {
40519                     $$debounce(function() {
40520                       scope.select({activeIdx: activeIdx, evt: evt});
40521                     }, angular.isNumber(debounce) ? debounce : debounce['default']);
40522                   } else {
40523                     scope.select({activeIdx: activeIdx, evt: evt});
40524                   }
40525                 };
40526               }
40527             };
40528           }])
40529
40530           .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
40531             return {
40532               scope: {
40533                 index: '=',
40534                 match: '=',
40535                 query: '='
40536               },
40537               link: function(scope, element, attrs) {
40538                 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
40539                 $templateRequest(tplUrl).then(function(tplContent) {
40540                   var tplEl = angular.element(tplContent.trim());
40541                   element.replaceWith(tplEl);
40542                   $compile(tplEl)(scope);
40543                 });
40544               }
40545             };
40546           }])
40547
40548           .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
40549             var isSanitizePresent;
40550             isSanitizePresent = $injector.has('$sanitize');
40551
40552             function escapeRegexp(queryToEscape) {
40553               // Regex: capture the whole query string and replace it with the string that will be used to match
40554               // the results, for example if the capture is "a" the result will be \a
40555               return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
40556             }
40557
40558             function containsHtml(matchItem) {
40559               return /<.*>/g.test(matchItem);
40560             }
40561
40562             return function(matchItem, query) {
40563               if (!isSanitizePresent && containsHtml(matchItem)) {
40564                 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
40565               }
40566               matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
40567               if (!isSanitizePresent) {
40568                 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
40569               }
40570               return matchItem;
40571             };
40572           }]);
40573
40574         angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
40575           $templateCache.put("uib/template/accordion/accordion-group.html",
40576             "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
40577             "  <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
40578             "    <h4 class=\"panel-title\">\n" +
40579             "      <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" +
40580             "    </h4>\n" +
40581             "  </div>\n" +
40582             "  <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
40583             "     <div class=\"panel-body\" ng-transclude></div>\n" +
40584             "  </div>\n" +
40585             "</div>\n" +
40586             "");
40587         }]);
40588
40589         angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
40590           $templateCache.put("uib/template/accordion/accordion.html",
40591             "<div class=\"panel-group\" ng-transclude></div>");
40592         }]);
40593
40594         angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
40595           $templateCache.put("uib/template/alert/alert.html",
40596             "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
40597             "    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
40598             "        <span aria-hidden=\"true\">&times;</span>\n" +
40599             "        <span class=\"sr-only\">Close</span>\n" +
40600             "    </button>\n" +
40601             "    <div ng-transclude></div>\n" +
40602             "</div>\n" +
40603             "");
40604         }]);
40605
40606         angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
40607           $templateCache.put("uib/template/carousel/carousel.html",
40608             "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
40609             "  <div class=\"carousel-inner\" ng-transclude></div>\n" +
40610             "  <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" +
40611             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
40612             "    <span class=\"sr-only\">previous</span>\n" +
40613             "  </a>\n" +
40614             "  <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" +
40615             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
40616             "    <span class=\"sr-only\">next</span>\n" +
40617             "  </a>\n" +
40618             "  <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
40619             "    <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
40620             "      <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
40621             "    </li>\n" +
40622             "  </ol>\n" +
40623             "</div>");
40624         }]);
40625
40626         angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
40627           $templateCache.put("uib/template/carousel/slide.html",
40628             "<div ng-class=\"{\n" +
40629             "    'active': active\n" +
40630             "  }\" class=\"item text-center\" ng-transclude></div>\n" +
40631             "");
40632         }]);
40633
40634         angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
40635           $templateCache.put("uib/template/datepicker/datepicker.html",
40636             "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
40637             "  <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
40638             "  <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
40639             "  <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
40640             "</div>");
40641         }]);
40642
40643         angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
40644           $templateCache.put("uib/template/datepicker/day.html",
40645             "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40646             "  <thead>\n" +
40647             "    <tr>\n" +
40648             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40649             "      <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40650             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40651             "    </tr>\n" +
40652             "    <tr>\n" +
40653             "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
40654             "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
40655             "    </tr>\n" +
40656             "  </thead>\n" +
40657             "  <tbody>\n" +
40658             "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
40659             "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
40660             "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
40661             "        id=\"{{::dt.uid}}\"\n" +
40662             "        ng-class=\"::dt.customClass\">\n" +
40663             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" +
40664             "          uib-is-class=\"\n" +
40665             "            'btn-info' for selectedDt,\n" +
40666             "            'active' for activeDt\n" +
40667             "            on dt\"\n" +
40668             "          ng-click=\"select(dt.date)\"\n" +
40669             "          ng-disabled=\"::dt.disabled\"\n" +
40670             "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40671             "      </td>\n" +
40672             "    </tr>\n" +
40673             "  </tbody>\n" +
40674             "</table>\n" +
40675             "");
40676         }]);
40677
40678         angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
40679           $templateCache.put("uib/template/datepicker/month.html",
40680             "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40681             "  <thead>\n" +
40682             "    <tr>\n" +
40683             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40684             "      <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40685             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40686             "    </tr>\n" +
40687             "  </thead>\n" +
40688             "  <tbody>\n" +
40689             "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
40690             "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
40691             "        id=\"{{::dt.uid}}\"\n" +
40692             "        ng-class=\"::dt.customClass\">\n" +
40693             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40694             "          uib-is-class=\"\n" +
40695             "            'btn-info' for selectedDt,\n" +
40696             "            'active' for activeDt\n" +
40697             "            on dt\"\n" +
40698             "          ng-click=\"select(dt.date)\"\n" +
40699             "          ng-disabled=\"::dt.disabled\"\n" +
40700             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40701             "      </td>\n" +
40702             "    </tr>\n" +
40703             "  </tbody>\n" +
40704             "</table>\n" +
40705             "");
40706         }]);
40707
40708         angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
40709           $templateCache.put("uib/template/datepicker/popup.html",
40710             "<ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
40711             "   <li ng-transclude></li>\n" +
40712             "   <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" +
40713             "           <span class=\"btn-group pull-left\">\n" +
40714             "                   <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
40715             "                   <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
40716             "           </span>\n" +
40717             "           <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
40718             "   </li>\n" +
40719             "</ul>\n" +
40720             "");
40721         }]);
40722
40723         angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
40724           $templateCache.put("uib/template/datepicker/year.html",
40725             "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40726             "  <thead>\n" +
40727             "    <tr>\n" +
40728             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40729             "      <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40730             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40731             "    </tr>\n" +
40732             "  </thead>\n" +
40733             "  <tbody>\n" +
40734             "    <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
40735             "      <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
40736             "        id=\"{{::dt.uid}}\"\n" +
40737             "        ng-class=\"::dt.customClass\">\n" +
40738             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40739             "          uib-is-class=\"\n" +
40740             "            'btn-info' for selectedDt,\n" +
40741             "            'active' for activeDt\n" +
40742             "            on dt\"\n" +
40743             "          ng-click=\"select(dt.date)\"\n" +
40744             "          ng-disabled=\"::dt.disabled\"\n" +
40745             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40746             "      </td>\n" +
40747             "    </tr>\n" +
40748             "  </tbody>\n" +
40749             "</table>\n" +
40750             "");
40751         }]);
40752
40753         angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
40754           $templateCache.put("uib/template/modal/backdrop.html",
40755             "<div class=\"modal-backdrop\"\n" +
40756             "     uib-modal-animation-class=\"fade\"\n" +
40757             "     modal-in-class=\"in\"\n" +
40758             "     ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
40759             "></div>\n" +
40760             "");
40761         }]);
40762
40763         angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
40764           $templateCache.put("uib/template/modal/window.html",
40765             "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
40766             "    uib-modal-animation-class=\"fade\"\n" +
40767             "    modal-in-class=\"in\"\n" +
40768             "    ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
40769             "    <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
40770             "</div>\n" +
40771             "");
40772         }]);
40773
40774         angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
40775           $templateCache.put("uib/template/pager/pager.html",
40776             "<ul class=\"pager\">\n" +
40777             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40778             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40779             "</ul>\n" +
40780             "");
40781         }]);
40782
40783         angular.module("uib/template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
40784           $templateCache.put("uib/template/pagination/pager.html",
40785             "<ul class=\"pager\">\n" +
40786             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40787             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40788             "</ul>\n" +
40789             "");
40790         }]);
40791
40792         angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
40793           $templateCache.put("uib/template/pagination/pagination.html",
40794             "<ul class=\"pagination\">\n" +
40795             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
40796             "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40797             "  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
40798             "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40799             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
40800             "</ul>\n" +
40801             "");
40802         }]);
40803
40804         angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
40805           $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
40806             "<div class=\"tooltip\"\n" +
40807             "  tooltip-animation-class=\"fade\"\n" +
40808             "  uib-tooltip-classes\n" +
40809             "  ng-class=\"{ in: isOpen() }\">\n" +
40810             "  <div class=\"tooltip-arrow\"></div>\n" +
40811             "  <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
40812             "</div>\n" +
40813             "");
40814         }]);
40815
40816         angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
40817           $templateCache.put("uib/template/tooltip/tooltip-popup.html",
40818             "<div class=\"tooltip\"\n" +
40819             "  tooltip-animation-class=\"fade\"\n" +
40820             "  uib-tooltip-classes\n" +
40821             "  ng-class=\"{ in: isOpen() }\">\n" +
40822             "  <div class=\"tooltip-arrow\"></div>\n" +
40823             "  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
40824             "</div>\n" +
40825             "");
40826         }]);
40827
40828         angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
40829           $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
40830             "<div class=\"tooltip\"\n" +
40831             "  tooltip-animation-class=\"fade\"\n" +
40832             "  uib-tooltip-classes\n" +
40833             "  ng-class=\"{ in: isOpen() }\">\n" +
40834             "  <div class=\"tooltip-arrow\"></div>\n" +
40835             "  <div class=\"tooltip-inner\"\n" +
40836             "    uib-tooltip-template-transclude=\"contentExp()\"\n" +
40837             "    tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40838             "</div>\n" +
40839             "");
40840         }]);
40841
40842         angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
40843           $templateCache.put("uib/template/popover/popover-html.html",
40844             "<div class=\"popover\"\n" +
40845             "  tooltip-animation-class=\"fade\"\n" +
40846             "  uib-tooltip-classes\n" +
40847             "  ng-class=\"{ in: isOpen() }\">\n" +
40848             "  <div class=\"arrow\"></div>\n" +
40849             "\n" +
40850             "  <div class=\"popover-inner\">\n" +
40851             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40852             "      <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
40853             "  </div>\n" +
40854             "</div>\n" +
40855             "");
40856         }]);
40857
40858         angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
40859           $templateCache.put("uib/template/popover/popover-template.html",
40860             "<div class=\"popover\"\n" +
40861             "  tooltip-animation-class=\"fade\"\n" +
40862             "  uib-tooltip-classes\n" +
40863             "  ng-class=\"{ in: isOpen() }\">\n" +
40864             "  <div class=\"arrow\"></div>\n" +
40865             "\n" +
40866             "  <div class=\"popover-inner\">\n" +
40867             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40868             "      <div class=\"popover-content\"\n" +
40869             "        uib-tooltip-template-transclude=\"contentExp()\"\n" +
40870             "        tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40871             "  </div>\n" +
40872             "</div>\n" +
40873             "");
40874         }]);
40875
40876         angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
40877           $templateCache.put("uib/template/popover/popover.html",
40878             "<div class=\"popover\"\n" +
40879             "  tooltip-animation-class=\"fade\"\n" +
40880             "  uib-tooltip-classes\n" +
40881             "  ng-class=\"{ in: isOpen() }\">\n" +
40882             "  <div class=\"arrow\"></div>\n" +
40883             "\n" +
40884             "  <div class=\"popover-inner\">\n" +
40885             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40886             "      <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
40887             "  </div>\n" +
40888             "</div>\n" +
40889             "");
40890         }]);
40891
40892         angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
40893           $templateCache.put("uib/template/progressbar/bar.html",
40894             "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
40895             "");
40896         }]);
40897
40898         angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
40899           $templateCache.put("uib/template/progressbar/progress.html",
40900             "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
40901         }]);
40902
40903         angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
40904           $templateCache.put("uib/template/progressbar/progressbar.html",
40905             "<div class=\"progress\">\n" +
40906             "  <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
40907             "</div>\n" +
40908             "");
40909         }]);
40910
40911         angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
40912           $templateCache.put("uib/template/rating/rating.html",
40913             "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
40914             "    <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
40915             "    <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\" aria-valuetext=\"{{r.title}}\"></i>\n" +
40916             "</span>\n" +
40917             "");
40918         }]);
40919
40920         angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
40921           $templateCache.put("uib/template/tabs/tab.html",
40922             "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" +
40923             "  <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" +
40924             "</li>\n" +
40925             "");
40926         }]);
40927
40928         angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
40929           $templateCache.put("uib/template/tabs/tabset.html",
40930             "<div>\n" +
40931             "  <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
40932             "  <div class=\"tab-content\">\n" +
40933             "    <div class=\"tab-pane\" \n" +
40934             "         ng-repeat=\"tab in tabs\" \n" +
40935             "         ng-class=\"{active: tab.active}\"\n" +
40936             "         uib-tab-content-transclude=\"tab\">\n" +
40937             "    </div>\n" +
40938             "  </div>\n" +
40939             "</div>\n" +
40940             "");
40941         }]);
40942
40943         angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
40944           $templateCache.put("uib/template/timepicker/timepicker.html",
40945             "<table class=\"uib-timepicker\">\n" +
40946             "  <tbody>\n" +
40947             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40948             "      <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40949             "      <td>&nbsp;</td>\n" +
40950             "      <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40951             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40952             "      <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40953             "      <td ng-show=\"showMeridian\"></td>\n" +
40954             "    </tr>\n" +
40955             "    <tr>\n" +
40956             "      <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
40957             "        <input style=\"width:50px;\" type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40958             "      </td>\n" +
40959             "      <td class=\"uib-separator\">:</td>\n" +
40960             "      <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
40961             "        <input style=\"width:50px;\" type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40962             "      </td>\n" +
40963             "      <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
40964             "      <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
40965             "        <input style=\"width:50px;\" type=\"text\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40966             "      </td>\n" +
40967             "      <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
40968             "    </tr>\n" +
40969             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40970             "      <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40971             "      <td>&nbsp;</td>\n" +
40972             "      <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40973             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40974             "      <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40975             "      <td ng-show=\"showMeridian\"></td>\n" +
40976             "    </tr>\n" +
40977             "  </tbody>\n" +
40978             "</table>\n" +
40979             "");
40980         }]);
40981
40982         angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
40983           $templateCache.put("uib/template/typeahead/typeahead-match.html",
40984             "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" +
40985             "");
40986         }]);
40987
40988         angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
40989           $templateCache.put("uib/template/typeahead/typeahead-popup.html",
40990             "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
40991             "    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
40992             "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
40993             "    </li>\n" +
40994             "</ul>\n" +
40995             "");
40996         }]);
40997         angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); })
40998
40999 /***/ },
41000 /* 8 */
41001 /***/ function(module, exports) {
41002
41003         var app;
41004         (function (app) {
41005             var declares;
41006             (function (declares) {
41007                 var CommandInfo = (function () {
41008                     function CommandInfo(name) {
41009                         this.name = name;
41010                     }
41011                     return CommandInfo;
41012                 })();
41013                 declares.CommandInfo = CommandInfo;
41014             })(declares = app.declares || (app.declares = {}));
41015         })(app || (app = {}));
41016         var app;
41017         (function (app) {
41018             var services;
41019             (function (services) {
41020                 var APIEndPoint = (function () {
41021                     function APIEndPoint($resource, $http) {
41022                         this.$resource = $resource;
41023                         this.$http = $http;
41024                     }
41025                     APIEndPoint.prototype.resource = function (endPoint, data) {
41026                         var customAction = {
41027                             method: 'GET',
41028                             isArray: false
41029                         };
41030                         var execute = {
41031                             method: 'POST',
41032                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
41033                         };
41034                         return this.$resource(endPoint, {}, { execute: execute });
41035                     };
41036                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41037                         var endPoint = '/api/v1/optionControlFile/' + command;
41038                         return this.resource(endPoint, {}).get();
41039                     };
41040                     APIEndPoint.prototype.getFiles = function (fileId) {
41041                         var endPoint = '/api/v1/workspace';
41042                         if (fileId) {
41043                             endPoint += '/' + fileId;
41044                         }
41045                         return this.resource(endPoint, {}).get();
41046                     };
41047                     APIEndPoint.prototype.getDirectories = function () {
41048                         var endPoint = '/api/v1/all/workspace/directory';
41049                         return this.resource(endPoint, {}).get();
41050                     };
41051                     APIEndPoint.prototype.getTags = function () {
41052                         var endPoint = '/api/v1/tagList';
41053                         return this.resource(endPoint, {}).get();
41054                     };
41055                     APIEndPoint.prototype.getCommands = function () {
41056                         var endPoint = '/api/v1/commandList';
41057                         return this.resource(endPoint, {}).get();
41058                     };
41059                     APIEndPoint.prototype.execute = function (data) {
41060                         var endPoint = '/api/v1/execution';
41061                         var fd = new FormData();
41062                         fd.append('data', data);
41063                         return this.$http.post(endPoint, fd, {
41064                             headers: { 'Content-Type': undefined },
41065                             transformRequest: angular.identity
41066                         });
41067                     };
41068                     return APIEndPoint;
41069                 })();
41070                 services.APIEndPoint = APIEndPoint;
41071             })(services = app.services || (app.services = {}));
41072         })(app || (app = {}));
41073         var app;
41074         (function (app) {
41075             var services;
41076             (function (services) {
41077                 var MyModal = (function () {
41078                     function MyModal($uibModal) {
41079                         this.$uibModal = $uibModal;
41080                         this.modalOption = {
41081                             backdrop: true,
41082                             controller: null,
41083                             templateUrl: null,
41084                             size: null
41085                         };
41086                     }
41087                     MyModal.prototype.open = function (modalName) {
41088                         if (modalName === 'SelectCommand') {
41089                             this.modalOption.templateUrl = 'templates/select-command.html';
41090                             this.modalOption.size = 'lg';
41091                         }
41092                         return this.$uibModal.open(this.modalOption);
41093                     };
41094                     MyModal.prototype.selectCommand = function () {
41095                         this.modalOption.templateUrl = 'templates/select-command.html';
41096                         this.modalOption.controller = 'selectCommandController';
41097                         this.modalOption.controllerAs = 'c';
41098                         this.modalOption.size = 'lg';
41099                         return this.$uibModal.open(this.modalOption);
41100                     };
41101                     MyModal.prototype.preview = function () {
41102                         this.modalOption.templateUrl = 'templates/preview.html';
41103                         this.modalOption.controller = 'previewController';
41104                         this.modalOption.controllerAs = 'c';
41105                         this.modalOption.size = 'lg';
41106                         return this.$uibModal.open(this.modalOption);
41107                     };
41108                     MyModal.$inject = ['$uibModal'];
41109                     return MyModal;
41110                 })();
41111                 services.MyModal = MyModal;
41112             })(services = app.services || (app.services = {}));
41113         })(app || (app = {}));
41114         var app;
41115         (function (app) {
41116             var directives;
41117             (function (directives) {
41118                 var Command = (function () {
41119                     function Command() {
41120                         this.restrict = 'E';
41121                         this.replace = true;
41122                         this.scope = true;
41123                         this.controller = 'commandController';
41124                         this.controllerAs = 'ctrl';
41125                         this.bindToController = {
41126                             index: '=',
41127                             name: '=',
41128                             remove: '&',
41129                             list: '='
41130                         };
41131                         this.templateUrl = 'templates/command.html';
41132                     }
41133                     Command.Factory = function () {
41134                         var directive = function () {
41135                             return new Command();
41136                         };
41137                         directive.$inject = [];
41138                         return directive;
41139                     };
41140                     return Command;
41141                 })();
41142                 directives.Command = Command;
41143                 var CommandController = (function () {
41144                     function CommandController(APIEndPoint, $scope, MyModal) {
41145                         this.APIEndPoint = APIEndPoint;
41146                         this.$scope = $scope;
41147                         this.MyModal = MyModal;
41148                         var controller = this;
41149                         this.APIEndPoint
41150                             .getOptionControlFile('mrcImageNoiseAdd')
41151                             .$promise
41152                             .then(function (result) {
41153                             controller.options = result.info;
41154                         });
41155                         this.APIEndPoint
41156                             .getDirectories()
41157                             .$promise
41158                             .then(function (result) {
41159                             controller.dirs = result.info;
41160                         });
41161                         this.heading = "[" + this.index + "]: dcdFilePring";
41162                         this.isOpen = true;
41163                         this.$scope.$on('close', function () {
41164                             controller.isOpen = false;
41165                         });
41166                     }
41167                     CommandController.prototype.submit = function () {
41168                         var opt = [];
41169                         angular.forEach(this.options, function (option) {
41170                             var obj = {
41171                                 name: option.option,
41172                                 arguments: []
41173                             };
41174                             angular.forEach(option.arg, function (arg) {
41175                                 if (arg.input) {
41176                                     if (typeof arg.input === 'object') {
41177                                         obj.arguments.push(arg.input.name);
41178                                     }
41179                                     else {
41180                                         obj.arguments.push(arg.input);
41181                                     }
41182                                 }
41183                             });
41184                             if (obj.arguments.length > 0) {
41185                                 opt.push(obj);
41186                             }
41187                         });
41188                         var execObj = {
41189                             command: this.name,
41190                             workspace: this.workspace.fileId,
41191                             options: opt
41192                         };
41193                         this.APIEndPoint
41194                             .execute(JSON.stringify(execObj))
41195                             .then(function (result) {
41196                             console.log(result);
41197                         });
41198                     };
41199                     CommandController.prototype.removeMySelf = function (index) {
41200                         this.remove()(index, this.list);
41201                     };
41202                     CommandController.prototype.reloadFiles = function () {
41203                         var _this = this;
41204                         var fileId = this.workspace.fileId;
41205                         this.APIEndPoint
41206                             .getFiles(fileId)
41207                             .$promise
41208                             .then(function (result) {
41209                             var status = result.status;
41210                             if (status === 'success') {
41211                                 _this.files = result.info;
41212                             }
41213                             else {
41214                                 console.log(result.message);
41215                             }
41216                         });
41217                     };
41218                     CommandController.prototype.debug = function () {
41219                         this.MyModal.preview();
41220                     };
41221                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
41222                     return CommandController;
41223                 })();
41224                 directives.CommandController = CommandController;
41225             })(directives = app.directives || (app.directives = {}));
41226         })(app || (app = {}));
41227         var app;
41228         (function (app) {
41229             var directives;
41230             (function (directives) {
41231                 var HeaderMenu = (function () {
41232                     function HeaderMenu() {
41233                         this.restrict = 'E';
41234                         this.replace = true;
41235                         this.templateUrl = 'templates/header-menu.html';
41236                     }
41237                     HeaderMenu.Factory = function () {
41238                         var directive = function () {
41239                             return new HeaderMenu();
41240                         };
41241                         return directive;
41242                     };
41243                     return HeaderMenu;
41244                 })();
41245                 directives.HeaderMenu = HeaderMenu;
41246             })(directives = app.directives || (app.directives = {}));
41247         })(app || (app = {}));
41248         var app;
41249         (function (app) {
41250             var directives;
41251             (function (directives) {
41252                 var Option = (function () {
41253                     function Option() {
41254                         this.restrict = 'E';
41255                         this.replace = true;
41256                         this.controller = 'optionController';
41257                         this.bindToController = {
41258                             info: '=',
41259                             files: '='
41260                         };
41261                         this.scope = true;
41262                         this.templateUrl = 'templates/option.html';
41263                         this.controllerAs = 'ctrl';
41264                     }
41265                     Option.Factory = function () {
41266                         var directive = function () {
41267                             return new Option();
41268                         };
41269                         directive.$inject = [];
41270                         return directive;
41271                     };
41272                     return Option;
41273                 })();
41274                 directives.Option = Option;
41275                 var OptionController = (function () {
41276                     function OptionController() {
41277                         var controller = this;
41278                         angular.forEach(controller.info.arg, function (arg) {
41279                             if (arg.initialValue) {
41280                                 if (arg.formType === 'number') {
41281                                     arg.input = parseInt(arg.initialValue);
41282                                 }
41283                                 else {
41284                                     arg.input = arg.initialValue;
41285                                 }
41286                             }
41287                         });
41288                     }
41289                     OptionController.$inject = [];
41290                     return OptionController;
41291                 })();
41292                 directives.OptionController = OptionController;
41293             })(directives = app.directives || (app.directives = {}));
41294         })(app || (app = {}));
41295         var app;
41296         (function (app) {
41297             var directives;
41298             (function (directives) {
41299                 var Directory = (function () {
41300                     function Directory() {
41301                         this.restrict = 'E';
41302                         this.replace = true;
41303                         this.controller = 'directoryController';
41304                         this.controllerAs = 'ctrl';
41305                         this.bindToController = {
41306                             info: '=',
41307                             add: '&',
41308                             list: '=',
41309                             files: '='
41310                         };
41311                         this.templateUrl = 'templates/directory.html';
41312                     }
41313                     Directory.Factory = function () {
41314                         var directive = function () {
41315                             return new Directory();
41316                         };
41317                         return directive;
41318                     };
41319                     return Directory;
41320                 })();
41321                 directives.Directory = Directory;
41322                 var DirectoryController = (function () {
41323                     function DirectoryController(APIEndPoint, $scope) {
41324                         this.APIEndPoint = APIEndPoint;
41325                         this.$scope = $scope;
41326                         var controller = this;
41327                         this.APIEndPoint
41328                             .getFiles(this.info.fileId)
41329                             .$promise
41330                             .then(function (result) {
41331                             if (result.status === 'success') {
41332                                 controller.files = result.info;
41333                                 angular.forEach(result.info, function (file) {
41334                                     if (file.fileType === '0') {
41335                                         var o = file;
41336                                         if (controller.info.path === '/') {
41337                                             o.path = '/' + file.name;
41338                                         }
41339                                         else {
41340                                             o.path = controller.info.path + '/' + file.name;
41341                                         }
41342                                         controller.add()(o, controller.list);
41343                                     }
41344                                 });
41345                             }
41346                             ;
41347                         });
41348                     }
41349                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41350                     return DirectoryController;
41351                 })();
41352                 directives.DirectoryController = DirectoryController;
41353             })(directives = app.directives || (app.directives = {}));
41354         })(app || (app = {}));
41355         var app;
41356         (function (app) {
41357             var controllers;
41358             (function (controllers) {
41359                 var Execution = (function () {
41360                     function Execution(MyModal, $scope) {
41361                         this.MyModal = MyModal;
41362                         this.$scope = $scope;
41363                         this.commandInfoList = [];
41364                     }
41365                     ;
41366                     Execution.prototype.add = function () {
41367                         this.$scope.$broadcast('close');
41368                         var commandInfoList = this.commandInfoList;
41369                         var commandInstance = this.MyModal.selectCommand();
41370                         commandInstance
41371                             .result
41372                             .then(function (command) {
41373                             commandInfoList.push(new app.declares.CommandInfo(command));
41374                         });
41375                     };
41376                     Execution.prototype.open = function () {
41377                         var result = this.MyModal.open('SelectCommand');
41378                         console.log(result);
41379                     };
41380                     Execution.prototype.remove = function (index, list) {
41381                         list.splice(index, 1);
41382                     };
41383                     Execution.prototype.close = function () {
41384                         console.log("close");
41385                     };
41386                     Execution.$inject = ['MyModal', '$scope'];
41387                     return Execution;
41388                 })();
41389                 controllers.Execution = Execution;
41390             })(controllers = app.controllers || (app.controllers = {}));
41391         })(app || (app = {}));
41392         var app;
41393         (function (app) {
41394             var controllers;
41395             (function (controllers) {
41396                 var Workspace = (function () {
41397                     function Workspace($scope, APIEndPoint, MyModal) {
41398                         this.$scope = $scope;
41399                         this.APIEndPoint = APIEndPoint;
41400                         this.MyModal = MyModal;
41401                         this.directoryList = [];
41402                         var controller = this;
41403                         var directoryList = this.directoryList;
41404                         var o = {
41405                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41406                             name: '',
41407                             parentId: '',
41408                             fileType: '',
41409                             createdAt: '',
41410                             updatedAt: '',
41411                             path: '/'
41412                         };
41413                         directoryList.push(o);
41414                     }
41415                     Workspace.prototype.addDirectory = function (info, directoryList) {
41416                         directoryList.push(info);
41417                     };
41418                     Workspace.prototype.debug = function () {
41419                         this.MyModal.preview();
41420                     };
41421                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
41422                     return Workspace;
41423                 })();
41424                 controllers.Workspace = Workspace;
41425             })(controllers = app.controllers || (app.controllers = {}));
41426         })(app || (app = {}));
41427         var app;
41428         (function (app) {
41429             var controllers;
41430             (function (controllers) {
41431                 var History = (function () {
41432                     function History($scope) {
41433                         this.page = "History";
41434                     }
41435                     History.$inject = ['$scope'];
41436                     return History;
41437                 })();
41438                 controllers.History = History;
41439             })(controllers = app.controllers || (app.controllers = {}));
41440         })(app || (app = {}));
41441         var app;
41442         (function (app) {
41443             var controllers;
41444             (function (controllers) {
41445                 var SelectCommand = (function () {
41446                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
41447                         this.APIEndPoint = APIEndPoint;
41448                         this.$modalInstance = $modalInstance;
41449                         var controller = this;
41450                         this.APIEndPoint
41451                             .getTags()
41452                             .$promise.then(function (result) {
41453                             controller.tags = result.info;
41454                         });
41455                         this.APIEndPoint
41456                             .getCommands()
41457                             .$promise.then(function (result) {
41458                             controller.commands = result.info;
41459                         });
41460                         this.currentTag = 'all';
41461                     }
41462                     SelectCommand.prototype.changeTag = function (tag) {
41463                         this.currentTag = tag;
41464                     };
41465                     SelectCommand.prototype.selectCommand = function (command) {
41466                         this.$modalInstance.close(command);
41467                     };
41468                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
41469                     return SelectCommand;
41470                 })();
41471                 controllers.SelectCommand = SelectCommand;
41472             })(controllers = app.controllers || (app.controllers = {}));
41473         })(app || (app = {}));
41474         var app;
41475         (function (app) {
41476             var controllers;
41477             (function (controllers) {
41478                 var Preview = (function () {
41479                     function Preview($scope, APIEndPoint, $modalInstance) {
41480                         this.APIEndPoint = APIEndPoint;
41481                         this.$modalInstance = $modalInstance;
41482                         var controller = this;
41483                         console.log('preview');
41484                     }
41485                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
41486                     return Preview;
41487                 })();
41488                 controllers.Preview = Preview;
41489             })(controllers = app.controllers || (app.controllers = {}));
41490         })(app || (app = {}));
41491         var filters;
41492         (function (filters) {
41493             function Tag() {
41494                 return function (commands, tag) {
41495                     var result = [];
41496                     angular.forEach(commands, function (command) {
41497                         var flag = false;
41498                         angular.forEach(command.tags, function (value) {
41499                             if (tag === value)
41500                                 flag = true;
41501                         });
41502                         if (flag)
41503                             result.push(command);
41504                     });
41505                     return result;
41506                 };
41507             }
41508             filters.Tag = Tag;
41509         })(filters || (filters = {}));
41510         var app;
41511         (function (app) {
41512             'use strict';
41513             var appName = 'zephyr';
41514             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41515             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41516                 $urlRouterProvider.otherwise('/execution');
41517                 $locationProvider.html5Mode({
41518                     enabled: true,
41519                     requireBase: false
41520                 });
41521                 $stateProvider
41522                     .state('execution', {
41523                     url: '/execution',
41524                     templateUrl: 'templates/execution.html',
41525                     controller: 'executionController',
41526                     controllerAs: 'c'
41527                 })
41528                     .state('workspace', {
41529                     url: '/workspace',
41530                     templateUrl: 'templates/workspace.html',
41531                     controller: 'workspaceController',
41532                     controllerAs: 'c'
41533                 })
41534                     .state('history', {
41535                     url: '/history',
41536                     templateUrl: 'templates/history.html',
41537                     controller: 'historyController',
41538                     controllerAs: 'c'
41539                 });
41540             });
41541             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41542             app.zephyr.filter('Tag', filters.Tag);
41543             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
41544             app.zephyr.controller('previewController', app.controllers.Preview);
41545             app.zephyr.service('MyModal', app.services.MyModal);
41546             app.zephyr.controller('executionController', app.controllers.Execution);
41547             app.zephyr.controller('workspaceController', app.controllers.Workspace);
41548             app.zephyr.controller('historyController', app.controllers.History);
41549             app.zephyr.controller('commandController', app.directives.CommandController);
41550             app.zephyr.controller('optionController', app.directives.OptionController);
41551             app.zephyr.controller('directoryController', app.directives.DirectoryController);
41552             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41553             app.zephyr.directive('command', app.directives.Command.Factory());
41554             app.zephyr.directive('option', app.directives.Option.Factory());
41555             app.zephyr.directive('directory', app.directives.Directory.Factory());
41556         })(app || (app = {}));
41557
41558
41559 /***/ },
41560 /* 9 */
41561 /***/ function(module, exports) {
41562
41563         var app;
41564         (function (app) {
41565             var declares;
41566             (function (declares) {
41567                 var CommandInfo = (function () {
41568                     function CommandInfo(name) {
41569                         this.name = name;
41570                     }
41571                     return CommandInfo;
41572                 })();
41573                 declares.CommandInfo = CommandInfo;
41574             })(declares = app.declares || (app.declares = {}));
41575         })(app || (app = {}));
41576         var app;
41577         (function (app) {
41578             var services;
41579             (function (services) {
41580                 var APIEndPoint = (function () {
41581                     function APIEndPoint($resource, $http) {
41582                         this.$resource = $resource;
41583                         this.$http = $http;
41584                     }
41585                     APIEndPoint.prototype.resource = function (endPoint, data) {
41586                         var customAction = {
41587                             method: 'GET',
41588                             isArray: false
41589                         };
41590                         var execute = {
41591                             method: 'POST',
41592                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
41593                         };
41594                         return this.$resource(endPoint, {}, { execute: execute });
41595                     };
41596                     APIEndPoint.prototype.getOptionControlFile = function (command) {
41597                         var endPoint = '/api/v1/optionControlFile/' + command;
41598                         return this.resource(endPoint, {}).get();
41599                     };
41600                     APIEndPoint.prototype.getFiles = function (fileId) {
41601                         var endPoint = '/api/v1/workspace';
41602                         if (fileId) {
41603                             endPoint += '/' + fileId;
41604                         }
41605                         return this.resource(endPoint, {}).get();
41606                     };
41607                     APIEndPoint.prototype.getDirectories = function () {
41608                         var endPoint = '/api/v1/all/workspace/directory';
41609                         return this.resource(endPoint, {}).get();
41610                     };
41611                     APIEndPoint.prototype.getTags = function () {
41612                         var endPoint = '/api/v1/tagList';
41613                         return this.resource(endPoint, {}).get();
41614                     };
41615                     APIEndPoint.prototype.getCommands = function () {
41616                         var endPoint = '/api/v1/commandList';
41617                         return this.resource(endPoint, {}).get();
41618                     };
41619                     APIEndPoint.prototype.execute = function (data) {
41620                         var endPoint = '/api/v1/execution';
41621                         var fd = new FormData();
41622                         fd.append('data', data);
41623                         return this.$http.post(endPoint, fd, {
41624                             headers: { 'Content-Type': undefined },
41625                             transformRequest: angular.identity
41626                         });
41627                     };
41628                     return APIEndPoint;
41629                 })();
41630                 services.APIEndPoint = APIEndPoint;
41631             })(services = app.services || (app.services = {}));
41632         })(app || (app = {}));
41633         var app;
41634         (function (app) {
41635             var services;
41636             (function (services) {
41637                 var MyModal = (function () {
41638                     function MyModal($uibModal) {
41639                         this.$uibModal = $uibModal;
41640                         this.modalOption = {
41641                             backdrop: true,
41642                             controller: null,
41643                             templateUrl: null,
41644                             size: null
41645                         };
41646                     }
41647                     MyModal.prototype.open = function (modalName) {
41648                         if (modalName === 'SelectCommand') {
41649                             this.modalOption.templateUrl = 'templates/select-command.html';
41650                             this.modalOption.size = 'lg';
41651                         }
41652                         return this.$uibModal.open(this.modalOption);
41653                     };
41654                     MyModal.prototype.selectCommand = function () {
41655                         this.modalOption.templateUrl = 'templates/select-command.html';
41656                         this.modalOption.controller = 'selectCommandController';
41657                         this.modalOption.controllerAs = 'c';
41658                         this.modalOption.size = 'lg';
41659                         return this.$uibModal.open(this.modalOption);
41660                     };
41661                     MyModal.prototype.preview = function () {
41662                         this.modalOption.templateUrl = 'templates/preview.html';
41663                         this.modalOption.controller = 'previewController';
41664                         this.modalOption.controllerAs = 'c';
41665                         this.modalOption.size = 'lg';
41666                         return this.$uibModal.open(this.modalOption);
41667                     };
41668                     MyModal.$inject = ['$uibModal'];
41669                     return MyModal;
41670                 })();
41671                 services.MyModal = MyModal;
41672             })(services = app.services || (app.services = {}));
41673         })(app || (app = {}));
41674         var app;
41675         (function (app) {
41676             var directives;
41677             (function (directives) {
41678                 var Command = (function () {
41679                     function Command() {
41680                         this.restrict = 'E';
41681                         this.replace = true;
41682                         this.scope = true;
41683                         this.controller = 'commandController';
41684                         this.controllerAs = 'ctrl';
41685                         this.bindToController = {
41686                             index: '=',
41687                             name: '=',
41688                             remove: '&',
41689                             list: '='
41690                         };
41691                         this.templateUrl = 'templates/command.html';
41692                     }
41693                     Command.Factory = function () {
41694                         var directive = function () {
41695                             return new Command();
41696                         };
41697                         directive.$inject = [];
41698                         return directive;
41699                     };
41700                     return Command;
41701                 })();
41702                 directives.Command = Command;
41703                 var CommandController = (function () {
41704                     function CommandController(APIEndPoint, $scope, MyModal) {
41705                         this.APIEndPoint = APIEndPoint;
41706                         this.$scope = $scope;
41707                         this.MyModal = MyModal;
41708                         var controller = this;
41709                         this.APIEndPoint
41710                             .getOptionControlFile('mrcImageNoiseAdd')
41711                             .$promise
41712                             .then(function (result) {
41713                             controller.options = result.info;
41714                         });
41715                         this.APIEndPoint
41716                             .getDirectories()
41717                             .$promise
41718                             .then(function (result) {
41719                             controller.dirs = result.info;
41720                         });
41721                         this.heading = "[" + this.index + "]: dcdFilePring";
41722                         this.isOpen = true;
41723                         this.$scope.$on('close', function () {
41724                             controller.isOpen = false;
41725                         });
41726                     }
41727                     CommandController.prototype.submit = function () {
41728                         var opt = [];
41729                         angular.forEach(this.options, function (option) {
41730                             var obj = {
41731                                 name: option.option,
41732                                 arguments: []
41733                             };
41734                             angular.forEach(option.arg, function (arg) {
41735                                 if (arg.input) {
41736                                     if (typeof arg.input === 'object') {
41737                                         obj.arguments.push(arg.input.name);
41738                                     }
41739                                     else {
41740                                         obj.arguments.push(arg.input);
41741                                     }
41742                                 }
41743                             });
41744                             if (obj.arguments.length > 0) {
41745                                 opt.push(obj);
41746                             }
41747                         });
41748                         var execObj = {
41749                             command: this.name,
41750                             workspace: this.workspace.fileId,
41751                             options: opt
41752                         };
41753                         this.APIEndPoint
41754                             .execute(JSON.stringify(execObj))
41755                             .then(function (result) {
41756                             console.log(result);
41757                         });
41758                     };
41759                     CommandController.prototype.removeMySelf = function (index) {
41760                         this.remove()(index, this.list);
41761                     };
41762                     CommandController.prototype.reloadFiles = function () {
41763                         var _this = this;
41764                         var fileId = this.workspace.fileId;
41765                         this.APIEndPoint
41766                             .getFiles(fileId)
41767                             .$promise
41768                             .then(function (result) {
41769                             var status = result.status;
41770                             if (status === 'success') {
41771                                 _this.files = result.info;
41772                             }
41773                             else {
41774                                 console.log(result.message);
41775                             }
41776                         });
41777                     };
41778                     CommandController.prototype.debug = function () {
41779                         this.MyModal.preview();
41780                     };
41781                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
41782                     return CommandController;
41783                 })();
41784                 directives.CommandController = CommandController;
41785             })(directives = app.directives || (app.directives = {}));
41786         })(app || (app = {}));
41787         var app;
41788         (function (app) {
41789             var directives;
41790             (function (directives) {
41791                 var HeaderMenu = (function () {
41792                     function HeaderMenu() {
41793                         this.restrict = 'E';
41794                         this.replace = true;
41795                         this.templateUrl = 'templates/header-menu.html';
41796                     }
41797                     HeaderMenu.Factory = function () {
41798                         var directive = function () {
41799                             return new HeaderMenu();
41800                         };
41801                         return directive;
41802                     };
41803                     return HeaderMenu;
41804                 })();
41805                 directives.HeaderMenu = HeaderMenu;
41806             })(directives = app.directives || (app.directives = {}));
41807         })(app || (app = {}));
41808         var app;
41809         (function (app) {
41810             var directives;
41811             (function (directives) {
41812                 var Option = (function () {
41813                     function Option() {
41814                         this.restrict = 'E';
41815                         this.replace = true;
41816                         this.controller = 'optionController';
41817                         this.bindToController = {
41818                             info: '=',
41819                             files: '='
41820                         };
41821                         this.scope = true;
41822                         this.templateUrl = 'templates/option.html';
41823                         this.controllerAs = 'ctrl';
41824                     }
41825                     Option.Factory = function () {
41826                         var directive = function () {
41827                             return new Option();
41828                         };
41829                         directive.$inject = [];
41830                         return directive;
41831                     };
41832                     return Option;
41833                 })();
41834                 directives.Option = Option;
41835                 var OptionController = (function () {
41836                     function OptionController() {
41837                         var controller = this;
41838                         angular.forEach(controller.info.arg, function (arg) {
41839                             if (arg.initialValue) {
41840                                 if (arg.formType === 'number') {
41841                                     arg.input = parseInt(arg.initialValue);
41842                                 }
41843                                 else {
41844                                     arg.input = arg.initialValue;
41845                                 }
41846                             }
41847                         });
41848                     }
41849                     OptionController.$inject = [];
41850                     return OptionController;
41851                 })();
41852                 directives.OptionController = OptionController;
41853             })(directives = app.directives || (app.directives = {}));
41854         })(app || (app = {}));
41855         var app;
41856         (function (app) {
41857             var directives;
41858             (function (directives) {
41859                 var Directory = (function () {
41860                     function Directory() {
41861                         this.restrict = 'E';
41862                         this.replace = true;
41863                         this.controller = 'directoryController';
41864                         this.controllerAs = 'ctrl';
41865                         this.bindToController = {
41866                             info: '=',
41867                             add: '&',
41868                             list: '=',
41869                             files: '='
41870                         };
41871                         this.templateUrl = 'templates/directory.html';
41872                     }
41873                     Directory.Factory = function () {
41874                         var directive = function () {
41875                             return new Directory();
41876                         };
41877                         return directive;
41878                     };
41879                     return Directory;
41880                 })();
41881                 directives.Directory = Directory;
41882                 var DirectoryController = (function () {
41883                     function DirectoryController(APIEndPoint, $scope) {
41884                         this.APIEndPoint = APIEndPoint;
41885                         this.$scope = $scope;
41886                         var controller = this;
41887                         this.APIEndPoint
41888                             .getFiles(this.info.fileId)
41889                             .$promise
41890                             .then(function (result) {
41891                             if (result.status === 'success') {
41892                                 controller.files = result.info;
41893                                 angular.forEach(result.info, function (file) {
41894                                     if (file.fileType === '0') {
41895                                         var o = file;
41896                                         if (controller.info.path === '/') {
41897                                             o.path = '/' + file.name;
41898                                         }
41899                                         else {
41900                                             o.path = controller.info.path + '/' + file.name;
41901                                         }
41902                                         controller.add()(o, controller.list);
41903                                     }
41904                                 });
41905                             }
41906                             ;
41907                         });
41908                     }
41909                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
41910                     return DirectoryController;
41911                 })();
41912                 directives.DirectoryController = DirectoryController;
41913             })(directives = app.directives || (app.directives = {}));
41914         })(app || (app = {}));
41915         var app;
41916         (function (app) {
41917             var controllers;
41918             (function (controllers) {
41919                 var Execution = (function () {
41920                     function Execution(MyModal, $scope) {
41921                         this.MyModal = MyModal;
41922                         this.$scope = $scope;
41923                         this.commandInfoList = [];
41924                     }
41925                     ;
41926                     Execution.prototype.add = function () {
41927                         this.$scope.$broadcast('close');
41928                         var commandInfoList = this.commandInfoList;
41929                         var commandInstance = this.MyModal.selectCommand();
41930                         commandInstance
41931                             .result
41932                             .then(function (command) {
41933                             commandInfoList.push(new app.declares.CommandInfo(command));
41934                         });
41935                     };
41936                     Execution.prototype.open = function () {
41937                         var result = this.MyModal.open('SelectCommand');
41938                         console.log(result);
41939                     };
41940                     Execution.prototype.remove = function (index, list) {
41941                         list.splice(index, 1);
41942                     };
41943                     Execution.prototype.close = function () {
41944                         console.log("close");
41945                     };
41946                     Execution.$inject = ['MyModal', '$scope'];
41947                     return Execution;
41948                 })();
41949                 controllers.Execution = Execution;
41950             })(controllers = app.controllers || (app.controllers = {}));
41951         })(app || (app = {}));
41952         var app;
41953         (function (app) {
41954             var controllers;
41955             (function (controllers) {
41956                 var Workspace = (function () {
41957                     function Workspace($scope, APIEndPoint, MyModal) {
41958                         this.$scope = $scope;
41959                         this.APIEndPoint = APIEndPoint;
41960                         this.MyModal = MyModal;
41961                         this.directoryList = [];
41962                         var controller = this;
41963                         var directoryList = this.directoryList;
41964                         var o = {
41965                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
41966                             name: '',
41967                             parentId: '',
41968                             fileType: '',
41969                             createdAt: '',
41970                             updatedAt: '',
41971                             path: '/'
41972                         };
41973                         directoryList.push(o);
41974                     }
41975                     Workspace.prototype.addDirectory = function (info, directoryList) {
41976                         directoryList.push(info);
41977                     };
41978                     Workspace.prototype.debug = function () {
41979                         this.MyModal.preview();
41980                     };
41981                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
41982                     return Workspace;
41983                 })();
41984                 controllers.Workspace = Workspace;
41985             })(controllers = app.controllers || (app.controllers = {}));
41986         })(app || (app = {}));
41987         var app;
41988         (function (app) {
41989             var controllers;
41990             (function (controllers) {
41991                 var History = (function () {
41992                     function History($scope) {
41993                         this.page = "History";
41994                     }
41995                     History.$inject = ['$scope'];
41996                     return History;
41997                 })();
41998                 controllers.History = History;
41999             })(controllers = app.controllers || (app.controllers = {}));
42000         })(app || (app = {}));
42001         var app;
42002         (function (app) {
42003             var controllers;
42004             (function (controllers) {
42005                 var SelectCommand = (function () {
42006                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
42007                         this.APIEndPoint = APIEndPoint;
42008                         this.$modalInstance = $modalInstance;
42009                         var controller = this;
42010                         this.APIEndPoint
42011                             .getTags()
42012                             .$promise.then(function (result) {
42013                             controller.tags = result.info;
42014                         });
42015                         this.APIEndPoint
42016                             .getCommands()
42017                             .$promise.then(function (result) {
42018                             controller.commands = result.info;
42019                         });
42020                         this.currentTag = 'all';
42021                     }
42022                     SelectCommand.prototype.changeTag = function (tag) {
42023                         this.currentTag = tag;
42024                     };
42025                     SelectCommand.prototype.selectCommand = function (command) {
42026                         this.$modalInstance.close(command);
42027                     };
42028                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
42029                     return SelectCommand;
42030                 })();
42031                 controllers.SelectCommand = SelectCommand;
42032             })(controllers = app.controllers || (app.controllers = {}));
42033         })(app || (app = {}));
42034         var app;
42035         (function (app) {
42036             var controllers;
42037             (function (controllers) {
42038                 var Preview = (function () {
42039                     function Preview($scope, APIEndPoint, $modalInstance) {
42040                         this.APIEndPoint = APIEndPoint;
42041                         this.$modalInstance = $modalInstance;
42042                         var controller = this;
42043                         console.log('preview');
42044                     }
42045                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
42046                     return Preview;
42047                 })();
42048                 controllers.Preview = Preview;
42049             })(controllers = app.controllers || (app.controllers = {}));
42050         })(app || (app = {}));
42051         var filters;
42052         (function (filters) {
42053             function Tag() {
42054                 return function (commands, tag) {
42055                     var result = [];
42056                     angular.forEach(commands, function (command) {
42057                         var flag = false;
42058                         angular.forEach(command.tags, function (value) {
42059                             if (tag === value)
42060                                 flag = true;
42061                         });
42062                         if (flag)
42063                             result.push(command);
42064                     });
42065                     return result;
42066                 };
42067             }
42068             filters.Tag = Tag;
42069         })(filters || (filters = {}));
42070         var app;
42071         (function (app) {
42072             'use strict';
42073             var appName = 'zephyr';
42074             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42075             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42076                 $urlRouterProvider.otherwise('/execution');
42077                 $locationProvider.html5Mode({
42078                     enabled: true,
42079                     requireBase: false
42080                 });
42081                 $stateProvider
42082                     .state('execution', {
42083                     url: '/execution',
42084                     templateUrl: 'templates/execution.html',
42085                     controller: 'executionController',
42086                     controllerAs: 'c'
42087                 })
42088                     .state('workspace', {
42089                     url: '/workspace',
42090                     templateUrl: 'templates/workspace.html',
42091                     controller: 'workspaceController',
42092                     controllerAs: 'c'
42093                 })
42094                     .state('history', {
42095                     url: '/history',
42096                     templateUrl: 'templates/history.html',
42097                     controller: 'historyController',
42098                     controllerAs: 'c'
42099                 });
42100             });
42101             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42102             app.zephyr.filter('Tag', filters.Tag);
42103             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
42104             app.zephyr.controller('previewController', app.controllers.Preview);
42105             app.zephyr.service('MyModal', app.services.MyModal);
42106             app.zephyr.controller('executionController', app.controllers.Execution);
42107             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42108             app.zephyr.controller('historyController', app.controllers.History);
42109             app.zephyr.controller('commandController', app.directives.CommandController);
42110             app.zephyr.controller('optionController', app.directives.OptionController);
42111             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42112             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42113             app.zephyr.directive('command', app.directives.Command.Factory());
42114             app.zephyr.directive('option', app.directives.Option.Factory());
42115             app.zephyr.directive('directory', app.directives.Directory.Factory());
42116         })(app || (app = {}));
42117
42118
42119 /***/ },
42120 /* 10 */
42121 /***/ function(module, exports) {
42122
42123         var app;
42124         (function (app) {
42125             var declares;
42126             (function (declares) {
42127                 var CommandInfo = (function () {
42128                     function CommandInfo(name) {
42129                         this.name = name;
42130                     }
42131                     return CommandInfo;
42132                 })();
42133                 declares.CommandInfo = CommandInfo;
42134             })(declares = app.declares || (app.declares = {}));
42135         })(app || (app = {}));
42136         var app;
42137         (function (app) {
42138             var services;
42139             (function (services) {
42140                 var APIEndPoint = (function () {
42141                     function APIEndPoint($resource, $http) {
42142                         this.$resource = $resource;
42143                         this.$http = $http;
42144                     }
42145                     APIEndPoint.prototype.resource = function (endPoint, data) {
42146                         var customAction = {
42147                             method: 'GET',
42148                             isArray: false
42149                         };
42150                         var execute = {
42151                             method: 'POST',
42152                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
42153                         };
42154                         return this.$resource(endPoint, {}, { execute: execute });
42155                     };
42156                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42157                         var endPoint = '/api/v1/optionControlFile/' + command;
42158                         return this.resource(endPoint, {}).get();
42159                     };
42160                     APIEndPoint.prototype.getFiles = function (fileId) {
42161                         var endPoint = '/api/v1/workspace';
42162                         if (fileId) {
42163                             endPoint += '/' + fileId;
42164                         }
42165                         return this.resource(endPoint, {}).get();
42166                     };
42167                     APIEndPoint.prototype.getDirectories = function () {
42168                         var endPoint = '/api/v1/all/workspace/directory';
42169                         return this.resource(endPoint, {}).get();
42170                     };
42171                     APIEndPoint.prototype.getTags = function () {
42172                         var endPoint = '/api/v1/tagList';
42173                         return this.resource(endPoint, {}).get();
42174                     };
42175                     APIEndPoint.prototype.getCommands = function () {
42176                         var endPoint = '/api/v1/commandList';
42177                         return this.resource(endPoint, {}).get();
42178                     };
42179                     APIEndPoint.prototype.execute = function (data) {
42180                         var endPoint = '/api/v1/execution';
42181                         var fd = new FormData();
42182                         fd.append('data', data);
42183                         return this.$http.post(endPoint, fd, {
42184                             headers: { 'Content-Type': undefined },
42185                             transformRequest: angular.identity
42186                         });
42187                     };
42188                     return APIEndPoint;
42189                 })();
42190                 services.APIEndPoint = APIEndPoint;
42191             })(services = app.services || (app.services = {}));
42192         })(app || (app = {}));
42193         var app;
42194         (function (app) {
42195             var services;
42196             (function (services) {
42197                 var MyModal = (function () {
42198                     function MyModal($uibModal) {
42199                         this.$uibModal = $uibModal;
42200                         this.modalOption = {
42201                             backdrop: true,
42202                             controller: null,
42203                             templateUrl: null,
42204                             size: null
42205                         };
42206                     }
42207                     MyModal.prototype.open = function (modalName) {
42208                         if (modalName === 'SelectCommand') {
42209                             this.modalOption.templateUrl = 'templates/select-command.html';
42210                             this.modalOption.size = 'lg';
42211                         }
42212                         return this.$uibModal.open(this.modalOption);
42213                     };
42214                     MyModal.prototype.selectCommand = function () {
42215                         this.modalOption.templateUrl = 'templates/select-command.html';
42216                         this.modalOption.controller = 'selectCommandController';
42217                         this.modalOption.controllerAs = 'c';
42218                         this.modalOption.size = 'lg';
42219                         return this.$uibModal.open(this.modalOption);
42220                     };
42221                     MyModal.prototype.preview = function () {
42222                         this.modalOption.templateUrl = 'templates/preview.html';
42223                         this.modalOption.controller = 'previewController';
42224                         this.modalOption.controllerAs = 'c';
42225                         this.modalOption.size = 'lg';
42226                         return this.$uibModal.open(this.modalOption);
42227                     };
42228                     MyModal.$inject = ['$uibModal'];
42229                     return MyModal;
42230                 })();
42231                 services.MyModal = MyModal;
42232             })(services = app.services || (app.services = {}));
42233         })(app || (app = {}));
42234         var app;
42235         (function (app) {
42236             var directives;
42237             (function (directives) {
42238                 var Command = (function () {
42239                     function Command() {
42240                         this.restrict = 'E';
42241                         this.replace = true;
42242                         this.scope = true;
42243                         this.controller = 'commandController';
42244                         this.controllerAs = 'ctrl';
42245                         this.bindToController = {
42246                             index: '=',
42247                             name: '=',
42248                             remove: '&',
42249                             list: '='
42250                         };
42251                         this.templateUrl = 'templates/command.html';
42252                     }
42253                     Command.Factory = function () {
42254                         var directive = function () {
42255                             return new Command();
42256                         };
42257                         directive.$inject = [];
42258                         return directive;
42259                     };
42260                     return Command;
42261                 })();
42262                 directives.Command = Command;
42263                 var CommandController = (function () {
42264                     function CommandController(APIEndPoint, $scope, MyModal) {
42265                         this.APIEndPoint = APIEndPoint;
42266                         this.$scope = $scope;
42267                         this.MyModal = MyModal;
42268                         var controller = this;
42269                         this.APIEndPoint
42270                             .getOptionControlFile('mrcImageNoiseAdd')
42271                             .$promise
42272                             .then(function (result) {
42273                             controller.options = result.info;
42274                         });
42275                         this.APIEndPoint
42276                             .getDirectories()
42277                             .$promise
42278                             .then(function (result) {
42279                             controller.dirs = result.info;
42280                         });
42281                         this.heading = "[" + this.index + "]: dcdFilePring";
42282                         this.isOpen = true;
42283                         this.$scope.$on('close', function () {
42284                             controller.isOpen = false;
42285                         });
42286                     }
42287                     CommandController.prototype.submit = function () {
42288                         var opt = [];
42289                         angular.forEach(this.options, function (option) {
42290                             var obj = {
42291                                 name: option.option,
42292                                 arguments: []
42293                             };
42294                             angular.forEach(option.arg, function (arg) {
42295                                 if (arg.input) {
42296                                     if (typeof arg.input === 'object') {
42297                                         obj.arguments.push(arg.input.name);
42298                                     }
42299                                     else {
42300                                         obj.arguments.push(arg.input);
42301                                     }
42302                                 }
42303                             });
42304                             if (obj.arguments.length > 0) {
42305                                 opt.push(obj);
42306                             }
42307                         });
42308                         var execObj = {
42309                             command: this.name,
42310                             workspace: this.workspace.fileId,
42311                             options: opt
42312                         };
42313                         this.APIEndPoint
42314                             .execute(JSON.stringify(execObj))
42315                             .then(function (result) {
42316                             console.log(result);
42317                         });
42318                     };
42319                     CommandController.prototype.removeMySelf = function (index) {
42320                         this.remove()(index, this.list);
42321                     };
42322                     CommandController.prototype.reloadFiles = function () {
42323                         var _this = this;
42324                         var fileId = this.workspace.fileId;
42325                         this.APIEndPoint
42326                             .getFiles(fileId)
42327                             .$promise
42328                             .then(function (result) {
42329                             var status = result.status;
42330                             if (status === 'success') {
42331                                 _this.files = result.info;
42332                             }
42333                             else {
42334                                 console.log(result.message);
42335                             }
42336                         });
42337                     };
42338                     CommandController.prototype.debug = function () {
42339                         this.MyModal.preview();
42340                     };
42341                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
42342                     return CommandController;
42343                 })();
42344                 directives.CommandController = CommandController;
42345             })(directives = app.directives || (app.directives = {}));
42346         })(app || (app = {}));
42347         var app;
42348         (function (app) {
42349             var directives;
42350             (function (directives) {
42351                 var HeaderMenu = (function () {
42352                     function HeaderMenu() {
42353                         this.restrict = 'E';
42354                         this.replace = true;
42355                         this.templateUrl = 'templates/header-menu.html';
42356                     }
42357                     HeaderMenu.Factory = function () {
42358                         var directive = function () {
42359                             return new HeaderMenu();
42360                         };
42361                         return directive;
42362                     };
42363                     return HeaderMenu;
42364                 })();
42365                 directives.HeaderMenu = HeaderMenu;
42366             })(directives = app.directives || (app.directives = {}));
42367         })(app || (app = {}));
42368         var app;
42369         (function (app) {
42370             var directives;
42371             (function (directives) {
42372                 var Option = (function () {
42373                     function Option() {
42374                         this.restrict = 'E';
42375                         this.replace = true;
42376                         this.controller = 'optionController';
42377                         this.bindToController = {
42378                             info: '=',
42379                             files: '='
42380                         };
42381                         this.scope = true;
42382                         this.templateUrl = 'templates/option.html';
42383                         this.controllerAs = 'ctrl';
42384                     }
42385                     Option.Factory = function () {
42386                         var directive = function () {
42387                             return new Option();
42388                         };
42389                         directive.$inject = [];
42390                         return directive;
42391                     };
42392                     return Option;
42393                 })();
42394                 directives.Option = Option;
42395                 var OptionController = (function () {
42396                     function OptionController() {
42397                         var controller = this;
42398                         angular.forEach(controller.info.arg, function (arg) {
42399                             if (arg.initialValue) {
42400                                 if (arg.formType === 'number') {
42401                                     arg.input = parseInt(arg.initialValue);
42402                                 }
42403                                 else {
42404                                     arg.input = arg.initialValue;
42405                                 }
42406                             }
42407                         });
42408                     }
42409                     OptionController.$inject = [];
42410                     return OptionController;
42411                 })();
42412                 directives.OptionController = OptionController;
42413             })(directives = app.directives || (app.directives = {}));
42414         })(app || (app = {}));
42415         var app;
42416         (function (app) {
42417             var directives;
42418             (function (directives) {
42419                 var Directory = (function () {
42420                     function Directory() {
42421                         this.restrict = 'E';
42422                         this.replace = true;
42423                         this.controller = 'directoryController';
42424                         this.controllerAs = 'ctrl';
42425                         this.bindToController = {
42426                             info: '=',
42427                             add: '&',
42428                             list: '=',
42429                             files: '='
42430                         };
42431                         this.templateUrl = 'templates/directory.html';
42432                     }
42433                     Directory.Factory = function () {
42434                         var directive = function () {
42435                             return new Directory();
42436                         };
42437                         return directive;
42438                     };
42439                     return Directory;
42440                 })();
42441                 directives.Directory = Directory;
42442                 var DirectoryController = (function () {
42443                     function DirectoryController(APIEndPoint, $scope) {
42444                         this.APIEndPoint = APIEndPoint;
42445                         this.$scope = $scope;
42446                         var controller = this;
42447                         this.APIEndPoint
42448                             .getFiles(this.info.fileId)
42449                             .$promise
42450                             .then(function (result) {
42451                             if (result.status === 'success') {
42452                                 controller.files = result.info;
42453                                 angular.forEach(result.info, function (file) {
42454                                     if (file.fileType === '0') {
42455                                         var o = file;
42456                                         if (controller.info.path === '/') {
42457                                             o.path = '/' + file.name;
42458                                         }
42459                                         else {
42460                                             o.path = controller.info.path + '/' + file.name;
42461                                         }
42462                                         controller.add()(o, controller.list);
42463                                     }
42464                                 });
42465                             }
42466                             ;
42467                         });
42468                     }
42469                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
42470                     return DirectoryController;
42471                 })();
42472                 directives.DirectoryController = DirectoryController;
42473             })(directives = app.directives || (app.directives = {}));
42474         })(app || (app = {}));
42475         var app;
42476         (function (app) {
42477             var controllers;
42478             (function (controllers) {
42479                 var Execution = (function () {
42480                     function Execution(MyModal, $scope) {
42481                         this.MyModal = MyModal;
42482                         this.$scope = $scope;
42483                         this.commandInfoList = [];
42484                     }
42485                     ;
42486                     Execution.prototype.add = function () {
42487                         this.$scope.$broadcast('close');
42488                         var commandInfoList = this.commandInfoList;
42489                         var commandInstance = this.MyModal.selectCommand();
42490                         commandInstance
42491                             .result
42492                             .then(function (command) {
42493                             commandInfoList.push(new app.declares.CommandInfo(command));
42494                         });
42495                     };
42496                     Execution.prototype.open = function () {
42497                         var result = this.MyModal.open('SelectCommand');
42498                         console.log(result);
42499                     };
42500                     Execution.prototype.remove = function (index, list) {
42501                         list.splice(index, 1);
42502                     };
42503                     Execution.prototype.close = function () {
42504                         console.log("close");
42505                     };
42506                     Execution.$inject = ['MyModal', '$scope'];
42507                     return Execution;
42508                 })();
42509                 controllers.Execution = Execution;
42510             })(controllers = app.controllers || (app.controllers = {}));
42511         })(app || (app = {}));
42512         var app;
42513         (function (app) {
42514             var controllers;
42515             (function (controllers) {
42516                 var Workspace = (function () {
42517                     function Workspace($scope, APIEndPoint, MyModal) {
42518                         this.$scope = $scope;
42519                         this.APIEndPoint = APIEndPoint;
42520                         this.MyModal = MyModal;
42521                         this.directoryList = [];
42522                         var controller = this;
42523                         var directoryList = this.directoryList;
42524                         var o = {
42525                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
42526                             name: '',
42527                             parentId: '',
42528                             fileType: '',
42529                             createdAt: '',
42530                             updatedAt: '',
42531                             path: '/'
42532                         };
42533                         directoryList.push(o);
42534                     }
42535                     Workspace.prototype.addDirectory = function (info, directoryList) {
42536                         directoryList.push(info);
42537                     };
42538                     Workspace.prototype.debug = function () {
42539                         this.MyModal.preview();
42540                     };
42541                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
42542                     return Workspace;
42543                 })();
42544                 controllers.Workspace = Workspace;
42545             })(controllers = app.controllers || (app.controllers = {}));
42546         })(app || (app = {}));
42547         var app;
42548         (function (app) {
42549             var controllers;
42550             (function (controllers) {
42551                 var History = (function () {
42552                     function History($scope) {
42553                         this.page = "History";
42554                     }
42555                     History.$inject = ['$scope'];
42556                     return History;
42557                 })();
42558                 controllers.History = History;
42559             })(controllers = app.controllers || (app.controllers = {}));
42560         })(app || (app = {}));
42561         var app;
42562         (function (app) {
42563             var controllers;
42564             (function (controllers) {
42565                 var SelectCommand = (function () {
42566                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
42567                         this.APIEndPoint = APIEndPoint;
42568                         this.$modalInstance = $modalInstance;
42569                         var controller = this;
42570                         this.APIEndPoint
42571                             .getTags()
42572                             .$promise.then(function (result) {
42573                             controller.tags = result.info;
42574                         });
42575                         this.APIEndPoint
42576                             .getCommands()
42577                             .$promise.then(function (result) {
42578                             controller.commands = result.info;
42579                         });
42580                         this.currentTag = 'all';
42581                     }
42582                     SelectCommand.prototype.changeTag = function (tag) {
42583                         this.currentTag = tag;
42584                     };
42585                     SelectCommand.prototype.selectCommand = function (command) {
42586                         this.$modalInstance.close(command);
42587                     };
42588                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
42589                     return SelectCommand;
42590                 })();
42591                 controllers.SelectCommand = SelectCommand;
42592             })(controllers = app.controllers || (app.controllers = {}));
42593         })(app || (app = {}));
42594         var app;
42595         (function (app) {
42596             var controllers;
42597             (function (controllers) {
42598                 var Preview = (function () {
42599                     function Preview($scope, APIEndPoint, $modalInstance) {
42600                         this.APIEndPoint = APIEndPoint;
42601                         this.$modalInstance = $modalInstance;
42602                         var controller = this;
42603                         console.log('preview');
42604                     }
42605                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
42606                     return Preview;
42607                 })();
42608                 controllers.Preview = Preview;
42609             })(controllers = app.controllers || (app.controllers = {}));
42610         })(app || (app = {}));
42611         var filters;
42612         (function (filters) {
42613             function Tag() {
42614                 return function (commands, tag) {
42615                     var result = [];
42616                     angular.forEach(commands, function (command) {
42617                         var flag = false;
42618                         angular.forEach(command.tags, function (value) {
42619                             if (tag === value)
42620                                 flag = true;
42621                         });
42622                         if (flag)
42623                             result.push(command);
42624                     });
42625                     return result;
42626                 };
42627             }
42628             filters.Tag = Tag;
42629         })(filters || (filters = {}));
42630         var app;
42631         (function (app) {
42632             'use strict';
42633             var appName = 'zephyr';
42634             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42635             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42636                 $urlRouterProvider.otherwise('/execution');
42637                 $locationProvider.html5Mode({
42638                     enabled: true,
42639                     requireBase: false
42640                 });
42641                 $stateProvider
42642                     .state('execution', {
42643                     url: '/execution',
42644                     templateUrl: 'templates/execution.html',
42645                     controller: 'executionController',
42646                     controllerAs: 'c'
42647                 })
42648                     .state('workspace', {
42649                     url: '/workspace',
42650                     templateUrl: 'templates/workspace.html',
42651                     controller: 'workspaceController',
42652                     controllerAs: 'c'
42653                 })
42654                     .state('history', {
42655                     url: '/history',
42656                     templateUrl: 'templates/history.html',
42657                     controller: 'historyController',
42658                     controllerAs: 'c'
42659                 });
42660             });
42661             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42662             app.zephyr.filter('Tag', filters.Tag);
42663             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
42664             app.zephyr.controller('previewController', app.controllers.Preview);
42665             app.zephyr.service('MyModal', app.services.MyModal);
42666             app.zephyr.controller('executionController', app.controllers.Execution);
42667             app.zephyr.controller('workspaceController', app.controllers.Workspace);
42668             app.zephyr.controller('historyController', app.controllers.History);
42669             app.zephyr.controller('commandController', app.directives.CommandController);
42670             app.zephyr.controller('optionController', app.directives.OptionController);
42671             app.zephyr.controller('directoryController', app.directives.DirectoryController);
42672             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42673             app.zephyr.directive('command', app.directives.Command.Factory());
42674             app.zephyr.directive('option', app.directives.Option.Factory());
42675             app.zephyr.directive('directory', app.directives.Directory.Factory());
42676         })(app || (app = {}));
42677
42678
42679 /***/ },
42680 /* 11 */
42681 /***/ function(module, exports) {
42682
42683         var app;
42684         (function (app) {
42685             var declares;
42686             (function (declares) {
42687                 var CommandInfo = (function () {
42688                     function CommandInfo(name) {
42689                         this.name = name;
42690                     }
42691                     return CommandInfo;
42692                 })();
42693                 declares.CommandInfo = CommandInfo;
42694             })(declares = app.declares || (app.declares = {}));
42695         })(app || (app = {}));
42696         var app;
42697         (function (app) {
42698             var services;
42699             (function (services) {
42700                 var APIEndPoint = (function () {
42701                     function APIEndPoint($resource, $http) {
42702                         this.$resource = $resource;
42703                         this.$http = $http;
42704                     }
42705                     APIEndPoint.prototype.resource = function (endPoint, data) {
42706                         var customAction = {
42707                             method: 'GET',
42708                             isArray: false
42709                         };
42710                         var execute = {
42711                             method: 'POST',
42712                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
42713                         };
42714                         return this.$resource(endPoint, {}, { execute: execute });
42715                     };
42716                     APIEndPoint.prototype.getOptionControlFile = function (command) {
42717                         var endPoint = '/api/v1/optionControlFile/' + command;
42718                         return this.resource(endPoint, {}).get();
42719                     };
42720                     APIEndPoint.prototype.getFiles = function (fileId) {
42721                         var endPoint = '/api/v1/workspace';
42722                         if (fileId) {
42723                             endPoint += '/' + fileId;
42724                         }
42725                         return this.resource(endPoint, {}).get();
42726                     };
42727                     APIEndPoint.prototype.getDirectories = function () {
42728                         var endPoint = '/api/v1/all/workspace/directory';
42729                         return this.resource(endPoint, {}).get();
42730                     };
42731                     APIEndPoint.prototype.getTags = function () {
42732                         var endPoint = '/api/v1/tagList';
42733                         return this.resource(endPoint, {}).get();
42734                     };
42735                     APIEndPoint.prototype.getCommands = function () {
42736                         var endPoint = '/api/v1/commandList';
42737                         return this.resource(endPoint, {}).get();
42738                     };
42739                     APIEndPoint.prototype.execute = function (data) {
42740                         var endPoint = '/api/v1/execution';
42741                         var fd = new FormData();
42742                         fd.append('data', data);
42743                         return this.$http.post(endPoint, fd, {
42744                             headers: { 'Content-Type': undefined },
42745                             transformRequest: angular.identity
42746                         });
42747                     };
42748                     return APIEndPoint;
42749                 })();
42750                 services.APIEndPoint = APIEndPoint;
42751             })(services = app.services || (app.services = {}));
42752         })(app || (app = {}));
42753         var app;
42754         (function (app) {
42755             var services;
42756             (function (services) {
42757                 var MyModal = (function () {
42758                     function MyModal($uibModal) {
42759                         this.$uibModal = $uibModal;
42760                         this.modalOption = {
42761                             backdrop: true,
42762                             controller: null,
42763                             templateUrl: null,
42764                             size: null
42765                         };
42766                     }
42767                     MyModal.prototype.open = function (modalName) {
42768                         if (modalName === 'SelectCommand') {
42769                             this.modalOption.templateUrl = 'templates/select-command.html';
42770                             this.modalOption.size = 'lg';
42771                         }
42772                         return this.$uibModal.open(this.modalOption);
42773                     };
42774                     MyModal.prototype.selectCommand = function () {
42775                         this.modalOption.templateUrl = 'templates/select-command.html';
42776                         this.modalOption.controller = 'selectCommandController';
42777                         this.modalOption.controllerAs = 'c';
42778                         this.modalOption.size = 'lg';
42779                         return this.$uibModal.open(this.modalOption);
42780                     };
42781                     MyModal.prototype.preview = function () {
42782                         this.modalOption.templateUrl = 'templates/preview.html';
42783                         this.modalOption.controller = 'previewController';
42784                         this.modalOption.controllerAs = 'c';
42785                         this.modalOption.size = 'lg';
42786                         return this.$uibModal.open(this.modalOption);
42787                     };
42788                     MyModal.$inject = ['$uibModal'];
42789                     return MyModal;
42790                 })();
42791                 services.MyModal = MyModal;
42792             })(services = app.services || (app.services = {}));
42793         })(app || (app = {}));
42794         var app;
42795         (function (app) {
42796             var directives;
42797             (function (directives) {
42798                 var Command = (function () {
42799                     function Command() {
42800                         this.restrict = 'E';
42801                         this.replace = true;
42802                         this.scope = true;
42803                         this.controller = 'commandController';
42804                         this.controllerAs = 'ctrl';
42805                         this.bindToController = {
42806                             index: '=',
42807                             name: '=',
42808                             remove: '&',
42809                             list: '='
42810                         };
42811                         this.templateUrl = 'templates/command.html';
42812                     }
42813                     Command.Factory = function () {
42814                         var directive = function () {
42815                             return new Command();
42816                         };
42817                         directive.$inject = [];
42818                         return directive;
42819                     };
42820                     return Command;
42821                 })();
42822                 directives.Command = Command;
42823                 var CommandController = (function () {
42824                     function CommandController(APIEndPoint, $scope, MyModal) {
42825                         this.APIEndPoint = APIEndPoint;
42826                         this.$scope = $scope;
42827                         this.MyModal = MyModal;
42828                         var controller = this;
42829                         this.APIEndPoint
42830                             .getOptionControlFile('mrcImageNoiseAdd')
42831                             .$promise
42832                             .then(function (result) {
42833                             controller.options = result.info;
42834                         });
42835                         this.APIEndPoint
42836                             .getDirectories()
42837                             .$promise
42838                             .then(function (result) {
42839                             controller.dirs = result.info;
42840                         });
42841                         this.heading = "[" + this.index + "]: dcdFilePring";
42842                         this.isOpen = true;
42843                         this.$scope.$on('close', function () {
42844                             controller.isOpen = false;
42845                         });
42846                     }
42847                     CommandController.prototype.submit = function () {
42848                         var opt = [];
42849                         angular.forEach(this.options, function (option) {
42850                             var obj = {
42851                                 name: option.option,
42852                                 arguments: []
42853                             };
42854                             angular.forEach(option.arg, function (arg) {
42855                                 if (arg.input) {
42856                                     if (typeof arg.input === 'object') {
42857                                         obj.arguments.push(arg.input.name);
42858                                     }
42859                                     else {
42860                                         obj.arguments.push(arg.input);
42861                                     }
42862                                 }
42863                             });
42864                             if (obj.arguments.length > 0) {
42865                                 opt.push(obj);
42866                             }
42867                         });
42868                         var execObj = {
42869                             command: this.name,
42870                             workspace: this.workspace.fileId,
42871                             options: opt
42872                         };
42873                         this.APIEndPoint
42874                             .execute(JSON.stringify(execObj))
42875                             .then(function (result) {
42876                             console.log(result);
42877                         });
42878                     };
42879                     CommandController.prototype.removeMySelf = function (index) {
42880                         this.remove()(index, this.list);
42881                     };
42882                     CommandController.prototype.reloadFiles = function () {
42883                         var _this = this;
42884                         var fileId = this.workspace.fileId;
42885                         this.APIEndPoint
42886                             .getFiles(fileId)
42887                             .$promise
42888                             .then(function (result) {
42889                             var status = result.status;
42890                             if (status === 'success') {
42891                                 _this.files = result.info;
42892                             }
42893                             else {
42894                                 console.log(result.message);
42895                             }
42896                         });
42897                     };
42898                     CommandController.prototype.debug = function () {
42899                         this.MyModal.preview();
42900                     };
42901                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
42902                     return CommandController;
42903                 })();
42904                 directives.CommandController = CommandController;
42905             })(directives = app.directives || (app.directives = {}));
42906         })(app || (app = {}));
42907         var app;
42908         (function (app) {
42909             var directives;
42910             (function (directives) {
42911                 var HeaderMenu = (function () {
42912                     function HeaderMenu() {
42913                         this.restrict = 'E';
42914                         this.replace = true;
42915                         this.templateUrl = 'templates/header-menu.html';
42916                     }
42917                     HeaderMenu.Factory = function () {
42918                         var directive = function () {
42919                             return new HeaderMenu();
42920                         };
42921                         return directive;
42922                     };
42923                     return HeaderMenu;
42924                 })();
42925                 directives.HeaderMenu = HeaderMenu;
42926             })(directives = app.directives || (app.directives = {}));
42927         })(app || (app = {}));
42928         var app;
42929         (function (app) {
42930             var directives;
42931             (function (directives) {
42932                 var Option = (function () {
42933                     function Option() {
42934                         this.restrict = 'E';
42935                         this.replace = true;
42936                         this.controller = 'optionController';
42937                         this.bindToController = {
42938                             info: '=',
42939                             files: '='
42940                         };
42941                         this.scope = true;
42942                         this.templateUrl = 'templates/option.html';
42943                         this.controllerAs = 'ctrl';
42944                     }
42945                     Option.Factory = function () {
42946                         var directive = function () {
42947                             return new Option();
42948                         };
42949                         directive.$inject = [];
42950                         return directive;
42951                     };
42952                     return Option;
42953                 })();
42954                 directives.Option = Option;
42955                 var OptionController = (function () {
42956                     function OptionController() {
42957                         var controller = this;
42958                         angular.forEach(controller.info.arg, function (arg) {
42959                             if (arg.initialValue) {
42960                                 if (arg.formType === 'number') {
42961                                     arg.input = parseInt(arg.initialValue);
42962                                 }
42963                                 else {
42964                                     arg.input = arg.initialValue;
42965                                 }
42966                             }
42967                         });
42968                     }
42969                     OptionController.$inject = [];
42970                     return OptionController;
42971                 })();
42972                 directives.OptionController = OptionController;
42973             })(directives = app.directives || (app.directives = {}));
42974         })(app || (app = {}));
42975         var app;
42976         (function (app) {
42977             var directives;
42978             (function (directives) {
42979                 var Directory = (function () {
42980                     function Directory() {
42981                         this.restrict = 'E';
42982                         this.replace = true;
42983                         this.controller = 'directoryController';
42984                         this.controllerAs = 'ctrl';
42985                         this.bindToController = {
42986                             info: '=',
42987                             add: '&',
42988                             list: '=',
42989                             files: '='
42990                         };
42991                         this.templateUrl = 'templates/directory.html';
42992                     }
42993                     Directory.Factory = function () {
42994                         var directive = function () {
42995                             return new Directory();
42996                         };
42997                         return directive;
42998                     };
42999                     return Directory;
43000                 })();
43001                 directives.Directory = Directory;
43002                 var DirectoryController = (function () {
43003                     function DirectoryController(APIEndPoint, $scope) {
43004                         this.APIEndPoint = APIEndPoint;
43005                         this.$scope = $scope;
43006                         var controller = this;
43007                         this.APIEndPoint
43008                             .getFiles(this.info.fileId)
43009                             .$promise
43010                             .then(function (result) {
43011                             if (result.status === 'success') {
43012                                 controller.files = result.info;
43013                                 angular.forEach(result.info, function (file) {
43014                                     if (file.fileType === '0') {
43015                                         var o = file;
43016                                         if (controller.info.path === '/') {
43017                                             o.path = '/' + file.name;
43018                                         }
43019                                         else {
43020                                             o.path = controller.info.path + '/' + file.name;
43021                                         }
43022                                         controller.add()(o, controller.list);
43023                                     }
43024                                 });
43025                             }
43026                             ;
43027                         });
43028                     }
43029                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43030                     return DirectoryController;
43031                 })();
43032                 directives.DirectoryController = DirectoryController;
43033             })(directives = app.directives || (app.directives = {}));
43034         })(app || (app = {}));
43035         var app;
43036         (function (app) {
43037             var controllers;
43038             (function (controllers) {
43039                 var Execution = (function () {
43040                     function Execution(MyModal, $scope) {
43041                         this.MyModal = MyModal;
43042                         this.$scope = $scope;
43043                         this.commandInfoList = [];
43044                     }
43045                     ;
43046                     Execution.prototype.add = function () {
43047                         this.$scope.$broadcast('close');
43048                         var commandInfoList = this.commandInfoList;
43049                         var commandInstance = this.MyModal.selectCommand();
43050                         commandInstance
43051                             .result
43052                             .then(function (command) {
43053                             commandInfoList.push(new app.declares.CommandInfo(command));
43054                         });
43055                     };
43056                     Execution.prototype.open = function () {
43057                         var result = this.MyModal.open('SelectCommand');
43058                         console.log(result);
43059                     };
43060                     Execution.prototype.remove = function (index, list) {
43061                         list.splice(index, 1);
43062                     };
43063                     Execution.prototype.close = function () {
43064                         console.log("close");
43065                     };
43066                     Execution.$inject = ['MyModal', '$scope'];
43067                     return Execution;
43068                 })();
43069                 controllers.Execution = Execution;
43070             })(controllers = app.controllers || (app.controllers = {}));
43071         })(app || (app = {}));
43072         var app;
43073         (function (app) {
43074             var controllers;
43075             (function (controllers) {
43076                 var Workspace = (function () {
43077                     function Workspace($scope, APIEndPoint, MyModal) {
43078                         this.$scope = $scope;
43079                         this.APIEndPoint = APIEndPoint;
43080                         this.MyModal = MyModal;
43081                         this.directoryList = [];
43082                         var controller = this;
43083                         var directoryList = this.directoryList;
43084                         var o = {
43085                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43086                             name: '',
43087                             parentId: '',
43088                             fileType: '',
43089                             createdAt: '',
43090                             updatedAt: '',
43091                             path: '/'
43092                         };
43093                         directoryList.push(o);
43094                     }
43095                     Workspace.prototype.addDirectory = function (info, directoryList) {
43096                         directoryList.push(info);
43097                     };
43098                     Workspace.prototype.debug = function () {
43099                         this.MyModal.preview();
43100                     };
43101                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
43102                     return Workspace;
43103                 })();
43104                 controllers.Workspace = Workspace;
43105             })(controllers = app.controllers || (app.controllers = {}));
43106         })(app || (app = {}));
43107         var app;
43108         (function (app) {
43109             var controllers;
43110             (function (controllers) {
43111                 var History = (function () {
43112                     function History($scope) {
43113                         this.page = "History";
43114                     }
43115                     History.$inject = ['$scope'];
43116                     return History;
43117                 })();
43118                 controllers.History = History;
43119             })(controllers = app.controllers || (app.controllers = {}));
43120         })(app || (app = {}));
43121         var app;
43122         (function (app) {
43123             var controllers;
43124             (function (controllers) {
43125                 var SelectCommand = (function () {
43126                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
43127                         this.APIEndPoint = APIEndPoint;
43128                         this.$modalInstance = $modalInstance;
43129                         var controller = this;
43130                         this.APIEndPoint
43131                             .getTags()
43132                             .$promise.then(function (result) {
43133                             controller.tags = result.info;
43134                         });
43135                         this.APIEndPoint
43136                             .getCommands()
43137                             .$promise.then(function (result) {
43138                             controller.commands = result.info;
43139                         });
43140                         this.currentTag = 'all';
43141                     }
43142                     SelectCommand.prototype.changeTag = function (tag) {
43143                         this.currentTag = tag;
43144                     };
43145                     SelectCommand.prototype.selectCommand = function (command) {
43146                         this.$modalInstance.close(command);
43147                     };
43148                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
43149                     return SelectCommand;
43150                 })();
43151                 controllers.SelectCommand = SelectCommand;
43152             })(controllers = app.controllers || (app.controllers = {}));
43153         })(app || (app = {}));
43154         var app;
43155         (function (app) {
43156             var controllers;
43157             (function (controllers) {
43158                 var Preview = (function () {
43159                     function Preview($scope, APIEndPoint, $modalInstance) {
43160                         this.APIEndPoint = APIEndPoint;
43161                         this.$modalInstance = $modalInstance;
43162                         var controller = this;
43163                         console.log('preview');
43164                     }
43165                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
43166                     return Preview;
43167                 })();
43168                 controllers.Preview = Preview;
43169             })(controllers = app.controllers || (app.controllers = {}));
43170         })(app || (app = {}));
43171         var filters;
43172         (function (filters) {
43173             function Tag() {
43174                 return function (commands, tag) {
43175                     var result = [];
43176                     angular.forEach(commands, function (command) {
43177                         var flag = false;
43178                         angular.forEach(command.tags, function (value) {
43179                             if (tag === value)
43180                                 flag = true;
43181                         });
43182                         if (flag)
43183                             result.push(command);
43184                     });
43185                     return result;
43186                 };
43187             }
43188             filters.Tag = Tag;
43189         })(filters || (filters = {}));
43190         var app;
43191         (function (app) {
43192             'use strict';
43193             var appName = 'zephyr';
43194             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43195             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43196                 $urlRouterProvider.otherwise('/execution');
43197                 $locationProvider.html5Mode({
43198                     enabled: true,
43199                     requireBase: false
43200                 });
43201                 $stateProvider
43202                     .state('execution', {
43203                     url: '/execution',
43204                     templateUrl: 'templates/execution.html',
43205                     controller: 'executionController',
43206                     controllerAs: 'c'
43207                 })
43208                     .state('workspace', {
43209                     url: '/workspace',
43210                     templateUrl: 'templates/workspace.html',
43211                     controller: 'workspaceController',
43212                     controllerAs: 'c'
43213                 })
43214                     .state('history', {
43215                     url: '/history',
43216                     templateUrl: 'templates/history.html',
43217                     controller: 'historyController',
43218                     controllerAs: 'c'
43219                 });
43220             });
43221             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43222             app.zephyr.filter('Tag', filters.Tag);
43223             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
43224             app.zephyr.controller('previewController', app.controllers.Preview);
43225             app.zephyr.service('MyModal', app.services.MyModal);
43226             app.zephyr.controller('executionController', app.controllers.Execution);
43227             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43228             app.zephyr.controller('historyController', app.controllers.History);
43229             app.zephyr.controller('commandController', app.directives.CommandController);
43230             app.zephyr.controller('optionController', app.directives.OptionController);
43231             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43232             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43233             app.zephyr.directive('command', app.directives.Command.Factory());
43234             app.zephyr.directive('option', app.directives.Option.Factory());
43235             app.zephyr.directive('directory', app.directives.Directory.Factory());
43236         })(app || (app = {}));
43237
43238
43239 /***/ },
43240 /* 12 */
43241 /***/ function(module, exports) {
43242
43243         var app;
43244         (function (app) {
43245             var declares;
43246             (function (declares) {
43247                 var CommandInfo = (function () {
43248                     function CommandInfo(name) {
43249                         this.name = name;
43250                     }
43251                     return CommandInfo;
43252                 })();
43253                 declares.CommandInfo = CommandInfo;
43254             })(declares = app.declares || (app.declares = {}));
43255         })(app || (app = {}));
43256         var app;
43257         (function (app) {
43258             var services;
43259             (function (services) {
43260                 var APIEndPoint = (function () {
43261                     function APIEndPoint($resource, $http) {
43262                         this.$resource = $resource;
43263                         this.$http = $http;
43264                     }
43265                     APIEndPoint.prototype.resource = function (endPoint, data) {
43266                         var customAction = {
43267                             method: 'GET',
43268                             isArray: false
43269                         };
43270                         var execute = {
43271                             method: 'POST',
43272                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
43273                         };
43274                         return this.$resource(endPoint, {}, { execute: execute });
43275                     };
43276                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43277                         var endPoint = '/api/v1/optionControlFile/' + command;
43278                         return this.resource(endPoint, {}).get();
43279                     };
43280                     APIEndPoint.prototype.getFiles = function (fileId) {
43281                         var endPoint = '/api/v1/workspace';
43282                         if (fileId) {
43283                             endPoint += '/' + fileId;
43284                         }
43285                         return this.resource(endPoint, {}).get();
43286                     };
43287                     APIEndPoint.prototype.getDirectories = function () {
43288                         var endPoint = '/api/v1/all/workspace/directory';
43289                         return this.resource(endPoint, {}).get();
43290                     };
43291                     APIEndPoint.prototype.getTags = function () {
43292                         var endPoint = '/api/v1/tagList';
43293                         return this.resource(endPoint, {}).get();
43294                     };
43295                     APIEndPoint.prototype.getCommands = function () {
43296                         var endPoint = '/api/v1/commandList';
43297                         return this.resource(endPoint, {}).get();
43298                     };
43299                     APIEndPoint.prototype.execute = function (data) {
43300                         var endPoint = '/api/v1/execution';
43301                         var fd = new FormData();
43302                         fd.append('data', data);
43303                         return this.$http.post(endPoint, fd, {
43304                             headers: { 'Content-Type': undefined },
43305                             transformRequest: angular.identity
43306                         });
43307                     };
43308                     return APIEndPoint;
43309                 })();
43310                 services.APIEndPoint = APIEndPoint;
43311             })(services = app.services || (app.services = {}));
43312         })(app || (app = {}));
43313         var app;
43314         (function (app) {
43315             var services;
43316             (function (services) {
43317                 var MyModal = (function () {
43318                     function MyModal($uibModal) {
43319                         this.$uibModal = $uibModal;
43320                         this.modalOption = {
43321                             backdrop: true,
43322                             controller: null,
43323                             templateUrl: null,
43324                             size: null
43325                         };
43326                     }
43327                     MyModal.prototype.open = function (modalName) {
43328                         if (modalName === 'SelectCommand') {
43329                             this.modalOption.templateUrl = 'templates/select-command.html';
43330                             this.modalOption.size = 'lg';
43331                         }
43332                         return this.$uibModal.open(this.modalOption);
43333                     };
43334                     MyModal.prototype.selectCommand = function () {
43335                         this.modalOption.templateUrl = 'templates/select-command.html';
43336                         this.modalOption.controller = 'selectCommandController';
43337                         this.modalOption.controllerAs = 'c';
43338                         this.modalOption.size = 'lg';
43339                         return this.$uibModal.open(this.modalOption);
43340                     };
43341                     MyModal.prototype.preview = function () {
43342                         this.modalOption.templateUrl = 'templates/preview.html';
43343                         this.modalOption.controller = 'previewController';
43344                         this.modalOption.controllerAs = 'c';
43345                         this.modalOption.size = 'lg';
43346                         return this.$uibModal.open(this.modalOption);
43347                     };
43348                     MyModal.$inject = ['$uibModal'];
43349                     return MyModal;
43350                 })();
43351                 services.MyModal = MyModal;
43352             })(services = app.services || (app.services = {}));
43353         })(app || (app = {}));
43354         var app;
43355         (function (app) {
43356             var directives;
43357             (function (directives) {
43358                 var Command = (function () {
43359                     function Command() {
43360                         this.restrict = 'E';
43361                         this.replace = true;
43362                         this.scope = true;
43363                         this.controller = 'commandController';
43364                         this.controllerAs = 'ctrl';
43365                         this.bindToController = {
43366                             index: '=',
43367                             name: '=',
43368                             remove: '&',
43369                             list: '='
43370                         };
43371                         this.templateUrl = 'templates/command.html';
43372                     }
43373                     Command.Factory = function () {
43374                         var directive = function () {
43375                             return new Command();
43376                         };
43377                         directive.$inject = [];
43378                         return directive;
43379                     };
43380                     return Command;
43381                 })();
43382                 directives.Command = Command;
43383                 var CommandController = (function () {
43384                     function CommandController(APIEndPoint, $scope, MyModal) {
43385                         this.APIEndPoint = APIEndPoint;
43386                         this.$scope = $scope;
43387                         this.MyModal = MyModal;
43388                         var controller = this;
43389                         this.APIEndPoint
43390                             .getOptionControlFile('mrcImageNoiseAdd')
43391                             .$promise
43392                             .then(function (result) {
43393                             controller.options = result.info;
43394                         });
43395                         this.APIEndPoint
43396                             .getDirectories()
43397                             .$promise
43398                             .then(function (result) {
43399                             controller.dirs = result.info;
43400                         });
43401                         this.heading = "[" + this.index + "]: dcdFilePring";
43402                         this.isOpen = true;
43403                         this.$scope.$on('close', function () {
43404                             controller.isOpen = false;
43405                         });
43406                     }
43407                     CommandController.prototype.submit = function () {
43408                         var opt = [];
43409                         angular.forEach(this.options, function (option) {
43410                             var obj = {
43411                                 name: option.option,
43412                                 arguments: []
43413                             };
43414                             angular.forEach(option.arg, function (arg) {
43415                                 if (arg.input) {
43416                                     if (typeof arg.input === 'object') {
43417                                         obj.arguments.push(arg.input.name);
43418                                     }
43419                                     else {
43420                                         obj.arguments.push(arg.input);
43421                                     }
43422                                 }
43423                             });
43424                             if (obj.arguments.length > 0) {
43425                                 opt.push(obj);
43426                             }
43427                         });
43428                         var execObj = {
43429                             command: this.name,
43430                             workspace: this.workspace.fileId,
43431                             options: opt
43432                         };
43433                         this.APIEndPoint
43434                             .execute(JSON.stringify(execObj))
43435                             .then(function (result) {
43436                             console.log(result);
43437                         });
43438                     };
43439                     CommandController.prototype.removeMySelf = function (index) {
43440                         this.remove()(index, this.list);
43441                     };
43442                     CommandController.prototype.reloadFiles = function () {
43443                         var _this = this;
43444                         var fileId = this.workspace.fileId;
43445                         this.APIEndPoint
43446                             .getFiles(fileId)
43447                             .$promise
43448                             .then(function (result) {
43449                             var status = result.status;
43450                             if (status === 'success') {
43451                                 _this.files = result.info;
43452                             }
43453                             else {
43454                                 console.log(result.message);
43455                             }
43456                         });
43457                     };
43458                     CommandController.prototype.debug = function () {
43459                         this.MyModal.preview();
43460                     };
43461                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
43462                     return CommandController;
43463                 })();
43464                 directives.CommandController = CommandController;
43465             })(directives = app.directives || (app.directives = {}));
43466         })(app || (app = {}));
43467         var app;
43468         (function (app) {
43469             var directives;
43470             (function (directives) {
43471                 var HeaderMenu = (function () {
43472                     function HeaderMenu() {
43473                         this.restrict = 'E';
43474                         this.replace = true;
43475                         this.templateUrl = 'templates/header-menu.html';
43476                     }
43477                     HeaderMenu.Factory = function () {
43478                         var directive = function () {
43479                             return new HeaderMenu();
43480                         };
43481                         return directive;
43482                     };
43483                     return HeaderMenu;
43484                 })();
43485                 directives.HeaderMenu = HeaderMenu;
43486             })(directives = app.directives || (app.directives = {}));
43487         })(app || (app = {}));
43488         var app;
43489         (function (app) {
43490             var directives;
43491             (function (directives) {
43492                 var Option = (function () {
43493                     function Option() {
43494                         this.restrict = 'E';
43495                         this.replace = true;
43496                         this.controller = 'optionController';
43497                         this.bindToController = {
43498                             info: '=',
43499                             files: '='
43500                         };
43501                         this.scope = true;
43502                         this.templateUrl = 'templates/option.html';
43503                         this.controllerAs = 'ctrl';
43504                     }
43505                     Option.Factory = function () {
43506                         var directive = function () {
43507                             return new Option();
43508                         };
43509                         directive.$inject = [];
43510                         return directive;
43511                     };
43512                     return Option;
43513                 })();
43514                 directives.Option = Option;
43515                 var OptionController = (function () {
43516                     function OptionController() {
43517                         var controller = this;
43518                         angular.forEach(controller.info.arg, function (arg) {
43519                             if (arg.initialValue) {
43520                                 if (arg.formType === 'number') {
43521                                     arg.input = parseInt(arg.initialValue);
43522                                 }
43523                                 else {
43524                                     arg.input = arg.initialValue;
43525                                 }
43526                             }
43527                         });
43528                     }
43529                     OptionController.$inject = [];
43530                     return OptionController;
43531                 })();
43532                 directives.OptionController = OptionController;
43533             })(directives = app.directives || (app.directives = {}));
43534         })(app || (app = {}));
43535         var app;
43536         (function (app) {
43537             var directives;
43538             (function (directives) {
43539                 var Directory = (function () {
43540                     function Directory() {
43541                         this.restrict = 'E';
43542                         this.replace = true;
43543                         this.controller = 'directoryController';
43544                         this.controllerAs = 'ctrl';
43545                         this.bindToController = {
43546                             info: '=',
43547                             add: '&',
43548                             list: '=',
43549                             files: '='
43550                         };
43551                         this.templateUrl = 'templates/directory.html';
43552                     }
43553                     Directory.Factory = function () {
43554                         var directive = function () {
43555                             return new Directory();
43556                         };
43557                         return directive;
43558                     };
43559                     return Directory;
43560                 })();
43561                 directives.Directory = Directory;
43562                 var DirectoryController = (function () {
43563                     function DirectoryController(APIEndPoint, $scope) {
43564                         this.APIEndPoint = APIEndPoint;
43565                         this.$scope = $scope;
43566                         var controller = this;
43567                         this.APIEndPoint
43568                             .getFiles(this.info.fileId)
43569                             .$promise
43570                             .then(function (result) {
43571                             if (result.status === 'success') {
43572                                 controller.files = result.info;
43573                                 angular.forEach(result.info, function (file) {
43574                                     if (file.fileType === '0') {
43575                                         var o = file;
43576                                         if (controller.info.path === '/') {
43577                                             o.path = '/' + file.name;
43578                                         }
43579                                         else {
43580                                             o.path = controller.info.path + '/' + file.name;
43581                                         }
43582                                         controller.add()(o, controller.list);
43583                                     }
43584                                 });
43585                             }
43586                             ;
43587                         });
43588                     }
43589                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
43590                     return DirectoryController;
43591                 })();
43592                 directives.DirectoryController = DirectoryController;
43593             })(directives = app.directives || (app.directives = {}));
43594         })(app || (app = {}));
43595         var app;
43596         (function (app) {
43597             var controllers;
43598             (function (controllers) {
43599                 var Execution = (function () {
43600                     function Execution(MyModal, $scope) {
43601                         this.MyModal = MyModal;
43602                         this.$scope = $scope;
43603                         this.commandInfoList = [];
43604                     }
43605                     ;
43606                     Execution.prototype.add = function () {
43607                         this.$scope.$broadcast('close');
43608                         var commandInfoList = this.commandInfoList;
43609                         var commandInstance = this.MyModal.selectCommand();
43610                         commandInstance
43611                             .result
43612                             .then(function (command) {
43613                             commandInfoList.push(new app.declares.CommandInfo(command));
43614                         });
43615                     };
43616                     Execution.prototype.open = function () {
43617                         var result = this.MyModal.open('SelectCommand');
43618                         console.log(result);
43619                     };
43620                     Execution.prototype.remove = function (index, list) {
43621                         list.splice(index, 1);
43622                     };
43623                     Execution.prototype.close = function () {
43624                         console.log("close");
43625                     };
43626                     Execution.$inject = ['MyModal', '$scope'];
43627                     return Execution;
43628                 })();
43629                 controllers.Execution = Execution;
43630             })(controllers = app.controllers || (app.controllers = {}));
43631         })(app || (app = {}));
43632         var app;
43633         (function (app) {
43634             var controllers;
43635             (function (controllers) {
43636                 var Workspace = (function () {
43637                     function Workspace($scope, APIEndPoint, MyModal) {
43638                         this.$scope = $scope;
43639                         this.APIEndPoint = APIEndPoint;
43640                         this.MyModal = MyModal;
43641                         this.directoryList = [];
43642                         var controller = this;
43643                         var directoryList = this.directoryList;
43644                         var o = {
43645                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
43646                             name: '',
43647                             parentId: '',
43648                             fileType: '',
43649                             createdAt: '',
43650                             updatedAt: '',
43651                             path: '/'
43652                         };
43653                         directoryList.push(o);
43654                     }
43655                     Workspace.prototype.addDirectory = function (info, directoryList) {
43656                         directoryList.push(info);
43657                     };
43658                     Workspace.prototype.debug = function () {
43659                         this.MyModal.preview();
43660                     };
43661                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
43662                     return Workspace;
43663                 })();
43664                 controllers.Workspace = Workspace;
43665             })(controllers = app.controllers || (app.controllers = {}));
43666         })(app || (app = {}));
43667         var app;
43668         (function (app) {
43669             var controllers;
43670             (function (controllers) {
43671                 var History = (function () {
43672                     function History($scope) {
43673                         this.page = "History";
43674                     }
43675                     History.$inject = ['$scope'];
43676                     return History;
43677                 })();
43678                 controllers.History = History;
43679             })(controllers = app.controllers || (app.controllers = {}));
43680         })(app || (app = {}));
43681         var app;
43682         (function (app) {
43683             var controllers;
43684             (function (controllers) {
43685                 var SelectCommand = (function () {
43686                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
43687                         this.APIEndPoint = APIEndPoint;
43688                         this.$modalInstance = $modalInstance;
43689                         var controller = this;
43690                         this.APIEndPoint
43691                             .getTags()
43692                             .$promise.then(function (result) {
43693                             controller.tags = result.info;
43694                         });
43695                         this.APIEndPoint
43696                             .getCommands()
43697                             .$promise.then(function (result) {
43698                             controller.commands = result.info;
43699                         });
43700                         this.currentTag = 'all';
43701                     }
43702                     SelectCommand.prototype.changeTag = function (tag) {
43703                         this.currentTag = tag;
43704                     };
43705                     SelectCommand.prototype.selectCommand = function (command) {
43706                         this.$modalInstance.close(command);
43707                     };
43708                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
43709                     return SelectCommand;
43710                 })();
43711                 controllers.SelectCommand = SelectCommand;
43712             })(controllers = app.controllers || (app.controllers = {}));
43713         })(app || (app = {}));
43714         var app;
43715         (function (app) {
43716             var controllers;
43717             (function (controllers) {
43718                 var Preview = (function () {
43719                     function Preview($scope, APIEndPoint, $modalInstance) {
43720                         this.APIEndPoint = APIEndPoint;
43721                         this.$modalInstance = $modalInstance;
43722                         var controller = this;
43723                         console.log('preview');
43724                     }
43725                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
43726                     return Preview;
43727                 })();
43728                 controllers.Preview = Preview;
43729             })(controllers = app.controllers || (app.controllers = {}));
43730         })(app || (app = {}));
43731         var filters;
43732         (function (filters) {
43733             function Tag() {
43734                 return function (commands, tag) {
43735                     var result = [];
43736                     angular.forEach(commands, function (command) {
43737                         var flag = false;
43738                         angular.forEach(command.tags, function (value) {
43739                             if (tag === value)
43740                                 flag = true;
43741                         });
43742                         if (flag)
43743                             result.push(command);
43744                     });
43745                     return result;
43746                 };
43747             }
43748             filters.Tag = Tag;
43749         })(filters || (filters = {}));
43750         var app;
43751         (function (app) {
43752             'use strict';
43753             var appName = 'zephyr';
43754             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43755             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43756                 $urlRouterProvider.otherwise('/execution');
43757                 $locationProvider.html5Mode({
43758                     enabled: true,
43759                     requireBase: false
43760                 });
43761                 $stateProvider
43762                     .state('execution', {
43763                     url: '/execution',
43764                     templateUrl: 'templates/execution.html',
43765                     controller: 'executionController',
43766                     controllerAs: 'c'
43767                 })
43768                     .state('workspace', {
43769                     url: '/workspace',
43770                     templateUrl: 'templates/workspace.html',
43771                     controller: 'workspaceController',
43772                     controllerAs: 'c'
43773                 })
43774                     .state('history', {
43775                     url: '/history',
43776                     templateUrl: 'templates/history.html',
43777                     controller: 'historyController',
43778                     controllerAs: 'c'
43779                 });
43780             });
43781             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43782             app.zephyr.filter('Tag', filters.Tag);
43783             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
43784             app.zephyr.controller('previewController', app.controllers.Preview);
43785             app.zephyr.service('MyModal', app.services.MyModal);
43786             app.zephyr.controller('executionController', app.controllers.Execution);
43787             app.zephyr.controller('workspaceController', app.controllers.Workspace);
43788             app.zephyr.controller('historyController', app.controllers.History);
43789             app.zephyr.controller('commandController', app.directives.CommandController);
43790             app.zephyr.controller('optionController', app.directives.OptionController);
43791             app.zephyr.controller('directoryController', app.directives.DirectoryController);
43792             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43793             app.zephyr.directive('command', app.directives.Command.Factory());
43794             app.zephyr.directive('option', app.directives.Option.Factory());
43795             app.zephyr.directive('directory', app.directives.Directory.Factory());
43796         })(app || (app = {}));
43797
43798
43799 /***/ },
43800 /* 13 */
43801 /***/ function(module, exports) {
43802
43803         var app;
43804         (function (app) {
43805             var declares;
43806             (function (declares) {
43807                 var CommandInfo = (function () {
43808                     function CommandInfo(name) {
43809                         this.name = name;
43810                     }
43811                     return CommandInfo;
43812                 })();
43813                 declares.CommandInfo = CommandInfo;
43814             })(declares = app.declares || (app.declares = {}));
43815         })(app || (app = {}));
43816         var app;
43817         (function (app) {
43818             var services;
43819             (function (services) {
43820                 var APIEndPoint = (function () {
43821                     function APIEndPoint($resource, $http) {
43822                         this.$resource = $resource;
43823                         this.$http = $http;
43824                     }
43825                     APIEndPoint.prototype.resource = function (endPoint, data) {
43826                         var customAction = {
43827                             method: 'GET',
43828                             isArray: false
43829                         };
43830                         var execute = {
43831                             method: 'POST',
43832                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
43833                         };
43834                         return this.$resource(endPoint, {}, { execute: execute });
43835                     };
43836                     APIEndPoint.prototype.getOptionControlFile = function (command) {
43837                         var endPoint = '/api/v1/optionControlFile/' + command;
43838                         return this.resource(endPoint, {}).get();
43839                     };
43840                     APIEndPoint.prototype.getFiles = function (fileId) {
43841                         var endPoint = '/api/v1/workspace';
43842                         if (fileId) {
43843                             endPoint += '/' + fileId;
43844                         }
43845                         return this.resource(endPoint, {}).get();
43846                     };
43847                     APIEndPoint.prototype.getDirectories = function () {
43848                         var endPoint = '/api/v1/all/workspace/directory';
43849                         return this.resource(endPoint, {}).get();
43850                     };
43851                     APIEndPoint.prototype.getTags = function () {
43852                         var endPoint = '/api/v1/tagList';
43853                         return this.resource(endPoint, {}).get();
43854                     };
43855                     APIEndPoint.prototype.getCommands = function () {
43856                         var endPoint = '/api/v1/commandList';
43857                         return this.resource(endPoint, {}).get();
43858                     };
43859                     APIEndPoint.prototype.execute = function (data) {
43860                         var endPoint = '/api/v1/execution';
43861                         var fd = new FormData();
43862                         fd.append('data', data);
43863                         return this.$http.post(endPoint, fd, {
43864                             headers: { 'Content-Type': undefined },
43865                             transformRequest: angular.identity
43866                         });
43867                     };
43868                     return APIEndPoint;
43869                 })();
43870                 services.APIEndPoint = APIEndPoint;
43871             })(services = app.services || (app.services = {}));
43872         })(app || (app = {}));
43873         var app;
43874         (function (app) {
43875             var services;
43876             (function (services) {
43877                 var MyModal = (function () {
43878                     function MyModal($uibModal) {
43879                         this.$uibModal = $uibModal;
43880                         this.modalOption = {
43881                             backdrop: true,
43882                             controller: null,
43883                             templateUrl: null,
43884                             size: null
43885                         };
43886                     }
43887                     MyModal.prototype.open = function (modalName) {
43888                         if (modalName === 'SelectCommand') {
43889                             this.modalOption.templateUrl = 'templates/select-command.html';
43890                             this.modalOption.size = 'lg';
43891                         }
43892                         return this.$uibModal.open(this.modalOption);
43893                     };
43894                     MyModal.prototype.selectCommand = function () {
43895                         this.modalOption.templateUrl = 'templates/select-command.html';
43896                         this.modalOption.controller = 'selectCommandController';
43897                         this.modalOption.controllerAs = 'c';
43898                         this.modalOption.size = 'lg';
43899                         return this.$uibModal.open(this.modalOption);
43900                     };
43901                     MyModal.prototype.preview = function () {
43902                         this.modalOption.templateUrl = 'templates/preview.html';
43903                         this.modalOption.controller = 'previewController';
43904                         this.modalOption.controllerAs = 'c';
43905                         this.modalOption.size = 'lg';
43906                         return this.$uibModal.open(this.modalOption);
43907                     };
43908                     MyModal.$inject = ['$uibModal'];
43909                     return MyModal;
43910                 })();
43911                 services.MyModal = MyModal;
43912             })(services = app.services || (app.services = {}));
43913         })(app || (app = {}));
43914         var app;
43915         (function (app) {
43916             var directives;
43917             (function (directives) {
43918                 var Command = (function () {
43919                     function Command() {
43920                         this.restrict = 'E';
43921                         this.replace = true;
43922                         this.scope = true;
43923                         this.controller = 'commandController';
43924                         this.controllerAs = 'ctrl';
43925                         this.bindToController = {
43926                             index: '=',
43927                             name: '=',
43928                             remove: '&',
43929                             list: '='
43930                         };
43931                         this.templateUrl = 'templates/command.html';
43932                     }
43933                     Command.Factory = function () {
43934                         var directive = function () {
43935                             return new Command();
43936                         };
43937                         directive.$inject = [];
43938                         return directive;
43939                     };
43940                     return Command;
43941                 })();
43942                 directives.Command = Command;
43943                 var CommandController = (function () {
43944                     function CommandController(APIEndPoint, $scope, MyModal) {
43945                         this.APIEndPoint = APIEndPoint;
43946                         this.$scope = $scope;
43947                         this.MyModal = MyModal;
43948                         var controller = this;
43949                         this.APIEndPoint
43950                             .getOptionControlFile('mrcImageNoiseAdd')
43951                             .$promise
43952                             .then(function (result) {
43953                             controller.options = result.info;
43954                         });
43955                         this.APIEndPoint
43956                             .getDirectories()
43957                             .$promise
43958                             .then(function (result) {
43959                             controller.dirs = result.info;
43960                         });
43961                         this.heading = "[" + this.index + "]: dcdFilePring";
43962                         this.isOpen = true;
43963                         this.$scope.$on('close', function () {
43964                             controller.isOpen = false;
43965                         });
43966                     }
43967                     CommandController.prototype.submit = function () {
43968                         var opt = [];
43969                         angular.forEach(this.options, function (option) {
43970                             var obj = {
43971                                 name: option.option,
43972                                 arguments: []
43973                             };
43974                             angular.forEach(option.arg, function (arg) {
43975                                 if (arg.input) {
43976                                     if (typeof arg.input === 'object') {
43977                                         obj.arguments.push(arg.input.name);
43978                                     }
43979                                     else {
43980                                         obj.arguments.push(arg.input);
43981                                     }
43982                                 }
43983                             });
43984                             if (obj.arguments.length > 0) {
43985                                 opt.push(obj);
43986                             }
43987                         });
43988                         var execObj = {
43989                             command: this.name,
43990                             workspace: this.workspace.fileId,
43991                             options: opt
43992                         };
43993                         this.APIEndPoint
43994                             .execute(JSON.stringify(execObj))
43995                             .then(function (result) {
43996                             console.log(result);
43997                         });
43998                     };
43999                     CommandController.prototype.removeMySelf = function (index) {
44000                         this.remove()(index, this.list);
44001                     };
44002                     CommandController.prototype.reloadFiles = function () {
44003                         var _this = this;
44004                         var fileId = this.workspace.fileId;
44005                         this.APIEndPoint
44006                             .getFiles(fileId)
44007                             .$promise
44008                             .then(function (result) {
44009                             var status = result.status;
44010                             if (status === 'success') {
44011                                 _this.files = result.info;
44012                             }
44013                             else {
44014                                 console.log(result.message);
44015                             }
44016                         });
44017                     };
44018                     CommandController.prototype.debug = function () {
44019                         this.MyModal.preview();
44020                     };
44021                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
44022                     return CommandController;
44023                 })();
44024                 directives.CommandController = CommandController;
44025             })(directives = app.directives || (app.directives = {}));
44026         })(app || (app = {}));
44027         var app;
44028         (function (app) {
44029             var directives;
44030             (function (directives) {
44031                 var HeaderMenu = (function () {
44032                     function HeaderMenu() {
44033                         this.restrict = 'E';
44034                         this.replace = true;
44035                         this.templateUrl = 'templates/header-menu.html';
44036                     }
44037                     HeaderMenu.Factory = function () {
44038                         var directive = function () {
44039                             return new HeaderMenu();
44040                         };
44041                         return directive;
44042                     };
44043                     return HeaderMenu;
44044                 })();
44045                 directives.HeaderMenu = HeaderMenu;
44046             })(directives = app.directives || (app.directives = {}));
44047         })(app || (app = {}));
44048         var app;
44049         (function (app) {
44050             var directives;
44051             (function (directives) {
44052                 var Option = (function () {
44053                     function Option() {
44054                         this.restrict = 'E';
44055                         this.replace = true;
44056                         this.controller = 'optionController';
44057                         this.bindToController = {
44058                             info: '=',
44059                             files: '='
44060                         };
44061                         this.scope = true;
44062                         this.templateUrl = 'templates/option.html';
44063                         this.controllerAs = 'ctrl';
44064                     }
44065                     Option.Factory = function () {
44066                         var directive = function () {
44067                             return new Option();
44068                         };
44069                         directive.$inject = [];
44070                         return directive;
44071                     };
44072                     return Option;
44073                 })();
44074                 directives.Option = Option;
44075                 var OptionController = (function () {
44076                     function OptionController() {
44077                         var controller = this;
44078                         angular.forEach(controller.info.arg, function (arg) {
44079                             if (arg.initialValue) {
44080                                 if (arg.formType === 'number') {
44081                                     arg.input = parseInt(arg.initialValue);
44082                                 }
44083                                 else {
44084                                     arg.input = arg.initialValue;
44085                                 }
44086                             }
44087                         });
44088                     }
44089                     OptionController.$inject = [];
44090                     return OptionController;
44091                 })();
44092                 directives.OptionController = OptionController;
44093             })(directives = app.directives || (app.directives = {}));
44094         })(app || (app = {}));
44095         var app;
44096         (function (app) {
44097             var directives;
44098             (function (directives) {
44099                 var Directory = (function () {
44100                     function Directory() {
44101                         this.restrict = 'E';
44102                         this.replace = true;
44103                         this.controller = 'directoryController';
44104                         this.controllerAs = 'ctrl';
44105                         this.bindToController = {
44106                             info: '=',
44107                             add: '&',
44108                             list: '=',
44109                             files: '='
44110                         };
44111                         this.templateUrl = 'templates/directory.html';
44112                     }
44113                     Directory.Factory = function () {
44114                         var directive = function () {
44115                             return new Directory();
44116                         };
44117                         return directive;
44118                     };
44119                     return Directory;
44120                 })();
44121                 directives.Directory = Directory;
44122                 var DirectoryController = (function () {
44123                     function DirectoryController(APIEndPoint, $scope) {
44124                         this.APIEndPoint = APIEndPoint;
44125                         this.$scope = $scope;
44126                         var controller = this;
44127                         this.APIEndPoint
44128                             .getFiles(this.info.fileId)
44129                             .$promise
44130                             .then(function (result) {
44131                             if (result.status === 'success') {
44132                                 controller.files = result.info;
44133                                 angular.forEach(result.info, function (file) {
44134                                     if (file.fileType === '0') {
44135                                         var o = file;
44136                                         if (controller.info.path === '/') {
44137                                             o.path = '/' + file.name;
44138                                         }
44139                                         else {
44140                                             o.path = controller.info.path + '/' + file.name;
44141                                         }
44142                                         controller.add()(o, controller.list);
44143                                     }
44144                                 });
44145                             }
44146                             ;
44147                         });
44148                     }
44149                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44150                     return DirectoryController;
44151                 })();
44152                 directives.DirectoryController = DirectoryController;
44153             })(directives = app.directives || (app.directives = {}));
44154         })(app || (app = {}));
44155         var app;
44156         (function (app) {
44157             var controllers;
44158             (function (controllers) {
44159                 var Execution = (function () {
44160                     function Execution(MyModal, $scope) {
44161                         this.MyModal = MyModal;
44162                         this.$scope = $scope;
44163                         this.commandInfoList = [];
44164                     }
44165                     ;
44166                     Execution.prototype.add = function () {
44167                         this.$scope.$broadcast('close');
44168                         var commandInfoList = this.commandInfoList;
44169                         var commandInstance = this.MyModal.selectCommand();
44170                         commandInstance
44171                             .result
44172                             .then(function (command) {
44173                             commandInfoList.push(new app.declares.CommandInfo(command));
44174                         });
44175                     };
44176                     Execution.prototype.open = function () {
44177                         var result = this.MyModal.open('SelectCommand');
44178                         console.log(result);
44179                     };
44180                     Execution.prototype.remove = function (index, list) {
44181                         list.splice(index, 1);
44182                     };
44183                     Execution.prototype.close = function () {
44184                         console.log("close");
44185                     };
44186                     Execution.$inject = ['MyModal', '$scope'];
44187                     return Execution;
44188                 })();
44189                 controllers.Execution = Execution;
44190             })(controllers = app.controllers || (app.controllers = {}));
44191         })(app || (app = {}));
44192         var app;
44193         (function (app) {
44194             var controllers;
44195             (function (controllers) {
44196                 var Workspace = (function () {
44197                     function Workspace($scope, APIEndPoint, MyModal) {
44198                         this.$scope = $scope;
44199                         this.APIEndPoint = APIEndPoint;
44200                         this.MyModal = MyModal;
44201                         this.directoryList = [];
44202                         var controller = this;
44203                         var directoryList = this.directoryList;
44204                         var o = {
44205                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44206                             name: '',
44207                             parentId: '',
44208                             fileType: '',
44209                             createdAt: '',
44210                             updatedAt: '',
44211                             path: '/'
44212                         };
44213                         directoryList.push(o);
44214                     }
44215                     Workspace.prototype.addDirectory = function (info, directoryList) {
44216                         directoryList.push(info);
44217                     };
44218                     Workspace.prototype.debug = function () {
44219                         this.MyModal.preview();
44220                     };
44221                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
44222                     return Workspace;
44223                 })();
44224                 controllers.Workspace = Workspace;
44225             })(controllers = app.controllers || (app.controllers = {}));
44226         })(app || (app = {}));
44227         var app;
44228         (function (app) {
44229             var controllers;
44230             (function (controllers) {
44231                 var History = (function () {
44232                     function History($scope) {
44233                         this.page = "History";
44234                     }
44235                     History.$inject = ['$scope'];
44236                     return History;
44237                 })();
44238                 controllers.History = History;
44239             })(controllers = app.controllers || (app.controllers = {}));
44240         })(app || (app = {}));
44241         var app;
44242         (function (app) {
44243             var controllers;
44244             (function (controllers) {
44245                 var SelectCommand = (function () {
44246                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
44247                         this.APIEndPoint = APIEndPoint;
44248                         this.$modalInstance = $modalInstance;
44249                         var controller = this;
44250                         this.APIEndPoint
44251                             .getTags()
44252                             .$promise.then(function (result) {
44253                             controller.tags = result.info;
44254                         });
44255                         this.APIEndPoint
44256                             .getCommands()
44257                             .$promise.then(function (result) {
44258                             controller.commands = result.info;
44259                         });
44260                         this.currentTag = 'all';
44261                     }
44262                     SelectCommand.prototype.changeTag = function (tag) {
44263                         this.currentTag = tag;
44264                     };
44265                     SelectCommand.prototype.selectCommand = function (command) {
44266                         this.$modalInstance.close(command);
44267                     };
44268                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
44269                     return SelectCommand;
44270                 })();
44271                 controllers.SelectCommand = SelectCommand;
44272             })(controllers = app.controllers || (app.controllers = {}));
44273         })(app || (app = {}));
44274         var app;
44275         (function (app) {
44276             var controllers;
44277             (function (controllers) {
44278                 var Preview = (function () {
44279                     function Preview($scope, APIEndPoint, $modalInstance) {
44280                         this.APIEndPoint = APIEndPoint;
44281                         this.$modalInstance = $modalInstance;
44282                         var controller = this;
44283                         console.log('preview');
44284                     }
44285                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
44286                     return Preview;
44287                 })();
44288                 controllers.Preview = Preview;
44289             })(controllers = app.controllers || (app.controllers = {}));
44290         })(app || (app = {}));
44291         var filters;
44292         (function (filters) {
44293             function Tag() {
44294                 return function (commands, tag) {
44295                     var result = [];
44296                     angular.forEach(commands, function (command) {
44297                         var flag = false;
44298                         angular.forEach(command.tags, function (value) {
44299                             if (tag === value)
44300                                 flag = true;
44301                         });
44302                         if (flag)
44303                             result.push(command);
44304                     });
44305                     return result;
44306                 };
44307             }
44308             filters.Tag = Tag;
44309         })(filters || (filters = {}));
44310         var app;
44311         (function (app) {
44312             'use strict';
44313             var appName = 'zephyr';
44314             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44315             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44316                 $urlRouterProvider.otherwise('/execution');
44317                 $locationProvider.html5Mode({
44318                     enabled: true,
44319                     requireBase: false
44320                 });
44321                 $stateProvider
44322                     .state('execution', {
44323                     url: '/execution',
44324                     templateUrl: 'templates/execution.html',
44325                     controller: 'executionController',
44326                     controllerAs: 'c'
44327                 })
44328                     .state('workspace', {
44329                     url: '/workspace',
44330                     templateUrl: 'templates/workspace.html',
44331                     controller: 'workspaceController',
44332                     controllerAs: 'c'
44333                 })
44334                     .state('history', {
44335                     url: '/history',
44336                     templateUrl: 'templates/history.html',
44337                     controller: 'historyController',
44338                     controllerAs: 'c'
44339                 });
44340             });
44341             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44342             app.zephyr.filter('Tag', filters.Tag);
44343             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
44344             app.zephyr.controller('previewController', app.controllers.Preview);
44345             app.zephyr.service('MyModal', app.services.MyModal);
44346             app.zephyr.controller('executionController', app.controllers.Execution);
44347             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44348             app.zephyr.controller('historyController', app.controllers.History);
44349             app.zephyr.controller('commandController', app.directives.CommandController);
44350             app.zephyr.controller('optionController', app.directives.OptionController);
44351             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44352             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44353             app.zephyr.directive('command', app.directives.Command.Factory());
44354             app.zephyr.directive('option', app.directives.Option.Factory());
44355             app.zephyr.directive('directory', app.directives.Directory.Factory());
44356         })(app || (app = {}));
44357
44358
44359 /***/ },
44360 /* 14 */
44361 /***/ function(module, exports) {
44362
44363         var app;
44364         (function (app) {
44365             var declares;
44366             (function (declares) {
44367                 var CommandInfo = (function () {
44368                     function CommandInfo(name) {
44369                         this.name = name;
44370                     }
44371                     return CommandInfo;
44372                 })();
44373                 declares.CommandInfo = CommandInfo;
44374             })(declares = app.declares || (app.declares = {}));
44375         })(app || (app = {}));
44376         var app;
44377         (function (app) {
44378             var services;
44379             (function (services) {
44380                 var APIEndPoint = (function () {
44381                     function APIEndPoint($resource, $http) {
44382                         this.$resource = $resource;
44383                         this.$http = $http;
44384                     }
44385                     APIEndPoint.prototype.resource = function (endPoint, data) {
44386                         var customAction = {
44387                             method: 'GET',
44388                             isArray: false
44389                         };
44390                         var execute = {
44391                             method: 'POST',
44392                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
44393                         };
44394                         return this.$resource(endPoint, {}, { execute: execute });
44395                     };
44396                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44397                         var endPoint = '/api/v1/optionControlFile/' + command;
44398                         return this.resource(endPoint, {}).get();
44399                     };
44400                     APIEndPoint.prototype.getFiles = function (fileId) {
44401                         var endPoint = '/api/v1/workspace';
44402                         if (fileId) {
44403                             endPoint += '/' + fileId;
44404                         }
44405                         return this.resource(endPoint, {}).get();
44406                     };
44407                     APIEndPoint.prototype.getDirectories = function () {
44408                         var endPoint = '/api/v1/all/workspace/directory';
44409                         return this.resource(endPoint, {}).get();
44410                     };
44411                     APIEndPoint.prototype.getTags = function () {
44412                         var endPoint = '/api/v1/tagList';
44413                         return this.resource(endPoint, {}).get();
44414                     };
44415                     APIEndPoint.prototype.getCommands = function () {
44416                         var endPoint = '/api/v1/commandList';
44417                         return this.resource(endPoint, {}).get();
44418                     };
44419                     APIEndPoint.prototype.execute = function (data) {
44420                         var endPoint = '/api/v1/execution';
44421                         var fd = new FormData();
44422                         fd.append('data', data);
44423                         return this.$http.post(endPoint, fd, {
44424                             headers: { 'Content-Type': undefined },
44425                             transformRequest: angular.identity
44426                         });
44427                     };
44428                     return APIEndPoint;
44429                 })();
44430                 services.APIEndPoint = APIEndPoint;
44431             })(services = app.services || (app.services = {}));
44432         })(app || (app = {}));
44433         var app;
44434         (function (app) {
44435             var services;
44436             (function (services) {
44437                 var MyModal = (function () {
44438                     function MyModal($uibModal) {
44439                         this.$uibModal = $uibModal;
44440                         this.modalOption = {
44441                             backdrop: true,
44442                             controller: null,
44443                             templateUrl: null,
44444                             size: null
44445                         };
44446                     }
44447                     MyModal.prototype.open = function (modalName) {
44448                         if (modalName === 'SelectCommand') {
44449                             this.modalOption.templateUrl = 'templates/select-command.html';
44450                             this.modalOption.size = 'lg';
44451                         }
44452                         return this.$uibModal.open(this.modalOption);
44453                     };
44454                     MyModal.prototype.selectCommand = function () {
44455                         this.modalOption.templateUrl = 'templates/select-command.html';
44456                         this.modalOption.controller = 'selectCommandController';
44457                         this.modalOption.controllerAs = 'c';
44458                         this.modalOption.size = 'lg';
44459                         return this.$uibModal.open(this.modalOption);
44460                     };
44461                     MyModal.prototype.preview = function () {
44462                         this.modalOption.templateUrl = 'templates/preview.html';
44463                         this.modalOption.controller = 'previewController';
44464                         this.modalOption.controllerAs = 'c';
44465                         this.modalOption.size = 'lg';
44466                         return this.$uibModal.open(this.modalOption);
44467                     };
44468                     MyModal.$inject = ['$uibModal'];
44469                     return MyModal;
44470                 })();
44471                 services.MyModal = MyModal;
44472             })(services = app.services || (app.services = {}));
44473         })(app || (app = {}));
44474         var app;
44475         (function (app) {
44476             var directives;
44477             (function (directives) {
44478                 var Command = (function () {
44479                     function Command() {
44480                         this.restrict = 'E';
44481                         this.replace = true;
44482                         this.scope = true;
44483                         this.controller = 'commandController';
44484                         this.controllerAs = 'ctrl';
44485                         this.bindToController = {
44486                             index: '=',
44487                             name: '=',
44488                             remove: '&',
44489                             list: '='
44490                         };
44491                         this.templateUrl = 'templates/command.html';
44492                     }
44493                     Command.Factory = function () {
44494                         var directive = function () {
44495                             return new Command();
44496                         };
44497                         directive.$inject = [];
44498                         return directive;
44499                     };
44500                     return Command;
44501                 })();
44502                 directives.Command = Command;
44503                 var CommandController = (function () {
44504                     function CommandController(APIEndPoint, $scope, MyModal) {
44505                         this.APIEndPoint = APIEndPoint;
44506                         this.$scope = $scope;
44507                         this.MyModal = MyModal;
44508                         var controller = this;
44509                         this.APIEndPoint
44510                             .getOptionControlFile('mrcImageNoiseAdd')
44511                             .$promise
44512                             .then(function (result) {
44513                             controller.options = result.info;
44514                         });
44515                         this.APIEndPoint
44516                             .getDirectories()
44517                             .$promise
44518                             .then(function (result) {
44519                             controller.dirs = result.info;
44520                         });
44521                         this.heading = "[" + this.index + "]: dcdFilePring";
44522                         this.isOpen = true;
44523                         this.$scope.$on('close', function () {
44524                             controller.isOpen = false;
44525                         });
44526                     }
44527                     CommandController.prototype.submit = function () {
44528                         var opt = [];
44529                         angular.forEach(this.options, function (option) {
44530                             var obj = {
44531                                 name: option.option,
44532                                 arguments: []
44533                             };
44534                             angular.forEach(option.arg, function (arg) {
44535                                 if (arg.input) {
44536                                     if (typeof arg.input === 'object') {
44537                                         obj.arguments.push(arg.input.name);
44538                                     }
44539                                     else {
44540                                         obj.arguments.push(arg.input);
44541                                     }
44542                                 }
44543                             });
44544                             if (obj.arguments.length > 0) {
44545                                 opt.push(obj);
44546                             }
44547                         });
44548                         var execObj = {
44549                             command: this.name,
44550                             workspace: this.workspace.fileId,
44551                             options: opt
44552                         };
44553                         this.APIEndPoint
44554                             .execute(JSON.stringify(execObj))
44555                             .then(function (result) {
44556                             console.log(result);
44557                         });
44558                     };
44559                     CommandController.prototype.removeMySelf = function (index) {
44560                         this.remove()(index, this.list);
44561                     };
44562                     CommandController.prototype.reloadFiles = function () {
44563                         var _this = this;
44564                         var fileId = this.workspace.fileId;
44565                         this.APIEndPoint
44566                             .getFiles(fileId)
44567                             .$promise
44568                             .then(function (result) {
44569                             var status = result.status;
44570                             if (status === 'success') {
44571                                 _this.files = result.info;
44572                             }
44573                             else {
44574                                 console.log(result.message);
44575                             }
44576                         });
44577                     };
44578                     CommandController.prototype.debug = function () {
44579                         this.MyModal.preview();
44580                     };
44581                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
44582                     return CommandController;
44583                 })();
44584                 directives.CommandController = CommandController;
44585             })(directives = app.directives || (app.directives = {}));
44586         })(app || (app = {}));
44587         var app;
44588         (function (app) {
44589             var directives;
44590             (function (directives) {
44591                 var HeaderMenu = (function () {
44592                     function HeaderMenu() {
44593                         this.restrict = 'E';
44594                         this.replace = true;
44595                         this.templateUrl = 'templates/header-menu.html';
44596                     }
44597                     HeaderMenu.Factory = function () {
44598                         var directive = function () {
44599                             return new HeaderMenu();
44600                         };
44601                         return directive;
44602                     };
44603                     return HeaderMenu;
44604                 })();
44605                 directives.HeaderMenu = HeaderMenu;
44606             })(directives = app.directives || (app.directives = {}));
44607         })(app || (app = {}));
44608         var app;
44609         (function (app) {
44610             var directives;
44611             (function (directives) {
44612                 var Option = (function () {
44613                     function Option() {
44614                         this.restrict = 'E';
44615                         this.replace = true;
44616                         this.controller = 'optionController';
44617                         this.bindToController = {
44618                             info: '=',
44619                             files: '='
44620                         };
44621                         this.scope = true;
44622                         this.templateUrl = 'templates/option.html';
44623                         this.controllerAs = 'ctrl';
44624                     }
44625                     Option.Factory = function () {
44626                         var directive = function () {
44627                             return new Option();
44628                         };
44629                         directive.$inject = [];
44630                         return directive;
44631                     };
44632                     return Option;
44633                 })();
44634                 directives.Option = Option;
44635                 var OptionController = (function () {
44636                     function OptionController() {
44637                         var controller = this;
44638                         angular.forEach(controller.info.arg, function (arg) {
44639                             if (arg.initialValue) {
44640                                 if (arg.formType === 'number') {
44641                                     arg.input = parseInt(arg.initialValue);
44642                                 }
44643                                 else {
44644                                     arg.input = arg.initialValue;
44645                                 }
44646                             }
44647                         });
44648                     }
44649                     OptionController.$inject = [];
44650                     return OptionController;
44651                 })();
44652                 directives.OptionController = OptionController;
44653             })(directives = app.directives || (app.directives = {}));
44654         })(app || (app = {}));
44655         var app;
44656         (function (app) {
44657             var directives;
44658             (function (directives) {
44659                 var Directory = (function () {
44660                     function Directory() {
44661                         this.restrict = 'E';
44662                         this.replace = true;
44663                         this.controller = 'directoryController';
44664                         this.controllerAs = 'ctrl';
44665                         this.bindToController = {
44666                             info: '=',
44667                             add: '&',
44668                             list: '=',
44669                             files: '='
44670                         };
44671                         this.templateUrl = 'templates/directory.html';
44672                     }
44673                     Directory.Factory = function () {
44674                         var directive = function () {
44675                             return new Directory();
44676                         };
44677                         return directive;
44678                     };
44679                     return Directory;
44680                 })();
44681                 directives.Directory = Directory;
44682                 var DirectoryController = (function () {
44683                     function DirectoryController(APIEndPoint, $scope) {
44684                         this.APIEndPoint = APIEndPoint;
44685                         this.$scope = $scope;
44686                         var controller = this;
44687                         this.APIEndPoint
44688                             .getFiles(this.info.fileId)
44689                             .$promise
44690                             .then(function (result) {
44691                             if (result.status === 'success') {
44692                                 controller.files = result.info;
44693                                 angular.forEach(result.info, function (file) {
44694                                     if (file.fileType === '0') {
44695                                         var o = file;
44696                                         if (controller.info.path === '/') {
44697                                             o.path = '/' + file.name;
44698                                         }
44699                                         else {
44700                                             o.path = controller.info.path + '/' + file.name;
44701                                         }
44702                                         controller.add()(o, controller.list);
44703                                     }
44704                                 });
44705                             }
44706                             ;
44707                         });
44708                     }
44709                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
44710                     return DirectoryController;
44711                 })();
44712                 directives.DirectoryController = DirectoryController;
44713             })(directives = app.directives || (app.directives = {}));
44714         })(app || (app = {}));
44715         var app;
44716         (function (app) {
44717             var controllers;
44718             (function (controllers) {
44719                 var Execution = (function () {
44720                     function Execution(MyModal, $scope) {
44721                         this.MyModal = MyModal;
44722                         this.$scope = $scope;
44723                         this.commandInfoList = [];
44724                     }
44725                     ;
44726                     Execution.prototype.add = function () {
44727                         this.$scope.$broadcast('close');
44728                         var commandInfoList = this.commandInfoList;
44729                         var commandInstance = this.MyModal.selectCommand();
44730                         commandInstance
44731                             .result
44732                             .then(function (command) {
44733                             commandInfoList.push(new app.declares.CommandInfo(command));
44734                         });
44735                     };
44736                     Execution.prototype.open = function () {
44737                         var result = this.MyModal.open('SelectCommand');
44738                         console.log(result);
44739                     };
44740                     Execution.prototype.remove = function (index, list) {
44741                         list.splice(index, 1);
44742                     };
44743                     Execution.prototype.close = function () {
44744                         console.log("close");
44745                     };
44746                     Execution.$inject = ['MyModal', '$scope'];
44747                     return Execution;
44748                 })();
44749                 controllers.Execution = Execution;
44750             })(controllers = app.controllers || (app.controllers = {}));
44751         })(app || (app = {}));
44752         var app;
44753         (function (app) {
44754             var controllers;
44755             (function (controllers) {
44756                 var Workspace = (function () {
44757                     function Workspace($scope, APIEndPoint, MyModal) {
44758                         this.$scope = $scope;
44759                         this.APIEndPoint = APIEndPoint;
44760                         this.MyModal = MyModal;
44761                         this.directoryList = [];
44762                         var controller = this;
44763                         var directoryList = this.directoryList;
44764                         var o = {
44765                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
44766                             name: '',
44767                             parentId: '',
44768                             fileType: '',
44769                             createdAt: '',
44770                             updatedAt: '',
44771                             path: '/'
44772                         };
44773                         directoryList.push(o);
44774                     }
44775                     Workspace.prototype.addDirectory = function (info, directoryList) {
44776                         directoryList.push(info);
44777                     };
44778                     Workspace.prototype.debug = function () {
44779                         this.MyModal.preview();
44780                     };
44781                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
44782                     return Workspace;
44783                 })();
44784                 controllers.Workspace = Workspace;
44785             })(controllers = app.controllers || (app.controllers = {}));
44786         })(app || (app = {}));
44787         var app;
44788         (function (app) {
44789             var controllers;
44790             (function (controllers) {
44791                 var History = (function () {
44792                     function History($scope) {
44793                         this.page = "History";
44794                     }
44795                     History.$inject = ['$scope'];
44796                     return History;
44797                 })();
44798                 controllers.History = History;
44799             })(controllers = app.controllers || (app.controllers = {}));
44800         })(app || (app = {}));
44801         var app;
44802         (function (app) {
44803             var controllers;
44804             (function (controllers) {
44805                 var SelectCommand = (function () {
44806                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
44807                         this.APIEndPoint = APIEndPoint;
44808                         this.$modalInstance = $modalInstance;
44809                         var controller = this;
44810                         this.APIEndPoint
44811                             .getTags()
44812                             .$promise.then(function (result) {
44813                             controller.tags = result.info;
44814                         });
44815                         this.APIEndPoint
44816                             .getCommands()
44817                             .$promise.then(function (result) {
44818                             controller.commands = result.info;
44819                         });
44820                         this.currentTag = 'all';
44821                     }
44822                     SelectCommand.prototype.changeTag = function (tag) {
44823                         this.currentTag = tag;
44824                     };
44825                     SelectCommand.prototype.selectCommand = function (command) {
44826                         this.$modalInstance.close(command);
44827                     };
44828                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
44829                     return SelectCommand;
44830                 })();
44831                 controllers.SelectCommand = SelectCommand;
44832             })(controllers = app.controllers || (app.controllers = {}));
44833         })(app || (app = {}));
44834         var app;
44835         (function (app) {
44836             var controllers;
44837             (function (controllers) {
44838                 var Preview = (function () {
44839                     function Preview($scope, APIEndPoint, $modalInstance) {
44840                         this.APIEndPoint = APIEndPoint;
44841                         this.$modalInstance = $modalInstance;
44842                         var controller = this;
44843                         console.log('preview');
44844                     }
44845                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
44846                     return Preview;
44847                 })();
44848                 controllers.Preview = Preview;
44849             })(controllers = app.controllers || (app.controllers = {}));
44850         })(app || (app = {}));
44851         var filters;
44852         (function (filters) {
44853             function Tag() {
44854                 return function (commands, tag) {
44855                     var result = [];
44856                     angular.forEach(commands, function (command) {
44857                         var flag = false;
44858                         angular.forEach(command.tags, function (value) {
44859                             if (tag === value)
44860                                 flag = true;
44861                         });
44862                         if (flag)
44863                             result.push(command);
44864                     });
44865                     return result;
44866                 };
44867             }
44868             filters.Tag = Tag;
44869         })(filters || (filters = {}));
44870         var app;
44871         (function (app) {
44872             'use strict';
44873             var appName = 'zephyr';
44874             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44875             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44876                 $urlRouterProvider.otherwise('/execution');
44877                 $locationProvider.html5Mode({
44878                     enabled: true,
44879                     requireBase: false
44880                 });
44881                 $stateProvider
44882                     .state('execution', {
44883                     url: '/execution',
44884                     templateUrl: 'templates/execution.html',
44885                     controller: 'executionController',
44886                     controllerAs: 'c'
44887                 })
44888                     .state('workspace', {
44889                     url: '/workspace',
44890                     templateUrl: 'templates/workspace.html',
44891                     controller: 'workspaceController',
44892                     controllerAs: 'c'
44893                 })
44894                     .state('history', {
44895                     url: '/history',
44896                     templateUrl: 'templates/history.html',
44897                     controller: 'historyController',
44898                     controllerAs: 'c'
44899                 });
44900             });
44901             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44902             app.zephyr.filter('Tag', filters.Tag);
44903             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
44904             app.zephyr.controller('previewController', app.controllers.Preview);
44905             app.zephyr.service('MyModal', app.services.MyModal);
44906             app.zephyr.controller('executionController', app.controllers.Execution);
44907             app.zephyr.controller('workspaceController', app.controllers.Workspace);
44908             app.zephyr.controller('historyController', app.controllers.History);
44909             app.zephyr.controller('commandController', app.directives.CommandController);
44910             app.zephyr.controller('optionController', app.directives.OptionController);
44911             app.zephyr.controller('directoryController', app.directives.DirectoryController);
44912             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44913             app.zephyr.directive('command', app.directives.Command.Factory());
44914             app.zephyr.directive('option', app.directives.Option.Factory());
44915             app.zephyr.directive('directory', app.directives.Directory.Factory());
44916         })(app || (app = {}));
44917
44918
44919 /***/ },
44920 /* 15 */
44921 /***/ function(module, exports) {
44922
44923         var app;
44924         (function (app) {
44925             var declares;
44926             (function (declares) {
44927                 var CommandInfo = (function () {
44928                     function CommandInfo(name) {
44929                         this.name = name;
44930                     }
44931                     return CommandInfo;
44932                 })();
44933                 declares.CommandInfo = CommandInfo;
44934             })(declares = app.declares || (app.declares = {}));
44935         })(app || (app = {}));
44936         var app;
44937         (function (app) {
44938             var services;
44939             (function (services) {
44940                 var APIEndPoint = (function () {
44941                     function APIEndPoint($resource, $http) {
44942                         this.$resource = $resource;
44943                         this.$http = $http;
44944                     }
44945                     APIEndPoint.prototype.resource = function (endPoint, data) {
44946                         var customAction = {
44947                             method: 'GET',
44948                             isArray: false
44949                         };
44950                         var execute = {
44951                             method: 'POST',
44952                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
44953                         };
44954                         return this.$resource(endPoint, {}, { execute: execute });
44955                     };
44956                     APIEndPoint.prototype.getOptionControlFile = function (command) {
44957                         var endPoint = '/api/v1/optionControlFile/' + command;
44958                         return this.resource(endPoint, {}).get();
44959                     };
44960                     APIEndPoint.prototype.getFiles = function (fileId) {
44961                         var endPoint = '/api/v1/workspace';
44962                         if (fileId) {
44963                             endPoint += '/' + fileId;
44964                         }
44965                         return this.resource(endPoint, {}).get();
44966                     };
44967                     APIEndPoint.prototype.getDirectories = function () {
44968                         var endPoint = '/api/v1/all/workspace/directory';
44969                         return this.resource(endPoint, {}).get();
44970                     };
44971                     APIEndPoint.prototype.getTags = function () {
44972                         var endPoint = '/api/v1/tagList';
44973                         return this.resource(endPoint, {}).get();
44974                     };
44975                     APIEndPoint.prototype.getCommands = function () {
44976                         var endPoint = '/api/v1/commandList';
44977                         return this.resource(endPoint, {}).get();
44978                     };
44979                     APIEndPoint.prototype.execute = function (data) {
44980                         var endPoint = '/api/v1/execution';
44981                         var fd = new FormData();
44982                         fd.append('data', data);
44983                         return this.$http.post(endPoint, fd, {
44984                             headers: { 'Content-Type': undefined },
44985                             transformRequest: angular.identity
44986                         });
44987                     };
44988                     return APIEndPoint;
44989                 })();
44990                 services.APIEndPoint = APIEndPoint;
44991             })(services = app.services || (app.services = {}));
44992         })(app || (app = {}));
44993         var app;
44994         (function (app) {
44995             var services;
44996             (function (services) {
44997                 var MyModal = (function () {
44998                     function MyModal($uibModal) {
44999                         this.$uibModal = $uibModal;
45000                         this.modalOption = {
45001                             backdrop: true,
45002                             controller: null,
45003                             templateUrl: null,
45004                             size: null
45005                         };
45006                     }
45007                     MyModal.prototype.open = function (modalName) {
45008                         if (modalName === 'SelectCommand') {
45009                             this.modalOption.templateUrl = 'templates/select-command.html';
45010                             this.modalOption.size = 'lg';
45011                         }
45012                         return this.$uibModal.open(this.modalOption);
45013                     };
45014                     MyModal.prototype.selectCommand = function () {
45015                         this.modalOption.templateUrl = 'templates/select-command.html';
45016                         this.modalOption.controller = 'selectCommandController';
45017                         this.modalOption.controllerAs = 'c';
45018                         this.modalOption.size = 'lg';
45019                         return this.$uibModal.open(this.modalOption);
45020                     };
45021                     MyModal.prototype.preview = function () {
45022                         this.modalOption.templateUrl = 'templates/preview.html';
45023                         this.modalOption.controller = 'previewController';
45024                         this.modalOption.controllerAs = 'c';
45025                         this.modalOption.size = 'lg';
45026                         return this.$uibModal.open(this.modalOption);
45027                     };
45028                     MyModal.$inject = ['$uibModal'];
45029                     return MyModal;
45030                 })();
45031                 services.MyModal = MyModal;
45032             })(services = app.services || (app.services = {}));
45033         })(app || (app = {}));
45034         var app;
45035         (function (app) {
45036             var directives;
45037             (function (directives) {
45038                 var Command = (function () {
45039                     function Command() {
45040                         this.restrict = 'E';
45041                         this.replace = true;
45042                         this.scope = true;
45043                         this.controller = 'commandController';
45044                         this.controllerAs = 'ctrl';
45045                         this.bindToController = {
45046                             index: '=',
45047                             name: '=',
45048                             remove: '&',
45049                             list: '='
45050                         };
45051                         this.templateUrl = 'templates/command.html';
45052                     }
45053                     Command.Factory = function () {
45054                         var directive = function () {
45055                             return new Command();
45056                         };
45057                         directive.$inject = [];
45058                         return directive;
45059                     };
45060                     return Command;
45061                 })();
45062                 directives.Command = Command;
45063                 var CommandController = (function () {
45064                     function CommandController(APIEndPoint, $scope, MyModal) {
45065                         this.APIEndPoint = APIEndPoint;
45066                         this.$scope = $scope;
45067                         this.MyModal = MyModal;
45068                         var controller = this;
45069                         this.APIEndPoint
45070                             .getOptionControlFile('mrcImageNoiseAdd')
45071                             .$promise
45072                             .then(function (result) {
45073                             controller.options = result.info;
45074                         });
45075                         this.APIEndPoint
45076                             .getDirectories()
45077                             .$promise
45078                             .then(function (result) {
45079                             controller.dirs = result.info;
45080                         });
45081                         this.heading = "[" + this.index + "]: dcdFilePring";
45082                         this.isOpen = true;
45083                         this.$scope.$on('close', function () {
45084                             controller.isOpen = false;
45085                         });
45086                     }
45087                     CommandController.prototype.submit = function () {
45088                         var opt = [];
45089                         angular.forEach(this.options, function (option) {
45090                             var obj = {
45091                                 name: option.option,
45092                                 arguments: []
45093                             };
45094                             angular.forEach(option.arg, function (arg) {
45095                                 if (arg.input) {
45096                                     if (typeof arg.input === 'object') {
45097                                         obj.arguments.push(arg.input.name);
45098                                     }
45099                                     else {
45100                                         obj.arguments.push(arg.input);
45101                                     }
45102                                 }
45103                             });
45104                             if (obj.arguments.length > 0) {
45105                                 opt.push(obj);
45106                             }
45107                         });
45108                         var execObj = {
45109                             command: this.name,
45110                             workspace: this.workspace.fileId,
45111                             options: opt
45112                         };
45113                         this.APIEndPoint
45114                             .execute(JSON.stringify(execObj))
45115                             .then(function (result) {
45116                             console.log(result);
45117                         });
45118                     };
45119                     CommandController.prototype.removeMySelf = function (index) {
45120                         this.remove()(index, this.list);
45121                     };
45122                     CommandController.prototype.reloadFiles = function () {
45123                         var _this = this;
45124                         var fileId = this.workspace.fileId;
45125                         this.APIEndPoint
45126                             .getFiles(fileId)
45127                             .$promise
45128                             .then(function (result) {
45129                             var status = result.status;
45130                             if (status === 'success') {
45131                                 _this.files = result.info;
45132                             }
45133                             else {
45134                                 console.log(result.message);
45135                             }
45136                         });
45137                     };
45138                     CommandController.prototype.debug = function () {
45139                         this.MyModal.preview();
45140                     };
45141                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
45142                     return CommandController;
45143                 })();
45144                 directives.CommandController = CommandController;
45145             })(directives = app.directives || (app.directives = {}));
45146         })(app || (app = {}));
45147         var app;
45148         (function (app) {
45149             var directives;
45150             (function (directives) {
45151                 var HeaderMenu = (function () {
45152                     function HeaderMenu() {
45153                         this.restrict = 'E';
45154                         this.replace = true;
45155                         this.templateUrl = 'templates/header-menu.html';
45156                     }
45157                     HeaderMenu.Factory = function () {
45158                         var directive = function () {
45159                             return new HeaderMenu();
45160                         };
45161                         return directive;
45162                     };
45163                     return HeaderMenu;
45164                 })();
45165                 directives.HeaderMenu = HeaderMenu;
45166             })(directives = app.directives || (app.directives = {}));
45167         })(app || (app = {}));
45168         var app;
45169         (function (app) {
45170             var directives;
45171             (function (directives) {
45172                 var Option = (function () {
45173                     function Option() {
45174                         this.restrict = 'E';
45175                         this.replace = true;
45176                         this.controller = 'optionController';
45177                         this.bindToController = {
45178                             info: '=',
45179                             files: '='
45180                         };
45181                         this.scope = true;
45182                         this.templateUrl = 'templates/option.html';
45183                         this.controllerAs = 'ctrl';
45184                     }
45185                     Option.Factory = function () {
45186                         var directive = function () {
45187                             return new Option();
45188                         };
45189                         directive.$inject = [];
45190                         return directive;
45191                     };
45192                     return Option;
45193                 })();
45194                 directives.Option = Option;
45195                 var OptionController = (function () {
45196                     function OptionController() {
45197                         var controller = this;
45198                         angular.forEach(controller.info.arg, function (arg) {
45199                             if (arg.initialValue) {
45200                                 if (arg.formType === 'number') {
45201                                     arg.input = parseInt(arg.initialValue);
45202                                 }
45203                                 else {
45204                                     arg.input = arg.initialValue;
45205                                 }
45206                             }
45207                         });
45208                     }
45209                     OptionController.$inject = [];
45210                     return OptionController;
45211                 })();
45212                 directives.OptionController = OptionController;
45213             })(directives = app.directives || (app.directives = {}));
45214         })(app || (app = {}));
45215         var app;
45216         (function (app) {
45217             var directives;
45218             (function (directives) {
45219                 var Directory = (function () {
45220                     function Directory() {
45221                         this.restrict = 'E';
45222                         this.replace = true;
45223                         this.controller = 'directoryController';
45224                         this.controllerAs = 'ctrl';
45225                         this.bindToController = {
45226                             info: '=',
45227                             add: '&',
45228                             list: '=',
45229                             files: '='
45230                         };
45231                         this.templateUrl = 'templates/directory.html';
45232                     }
45233                     Directory.Factory = function () {
45234                         var directive = function () {
45235                             return new Directory();
45236                         };
45237                         return directive;
45238                     };
45239                     return Directory;
45240                 })();
45241                 directives.Directory = Directory;
45242                 var DirectoryController = (function () {
45243                     function DirectoryController(APIEndPoint, $scope) {
45244                         this.APIEndPoint = APIEndPoint;
45245                         this.$scope = $scope;
45246                         var controller = this;
45247                         this.APIEndPoint
45248                             .getFiles(this.info.fileId)
45249                             .$promise
45250                             .then(function (result) {
45251                             if (result.status === 'success') {
45252                                 controller.files = result.info;
45253                                 angular.forEach(result.info, function (file) {
45254                                     if (file.fileType === '0') {
45255                                         var o = file;
45256                                         if (controller.info.path === '/') {
45257                                             o.path = '/' + file.name;
45258                                         }
45259                                         else {
45260                                             o.path = controller.info.path + '/' + file.name;
45261                                         }
45262                                         controller.add()(o, controller.list);
45263                                     }
45264                                 });
45265                             }
45266                             ;
45267                         });
45268                     }
45269                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45270                     return DirectoryController;
45271                 })();
45272                 directives.DirectoryController = DirectoryController;
45273             })(directives = app.directives || (app.directives = {}));
45274         })(app || (app = {}));
45275         var app;
45276         (function (app) {
45277             var controllers;
45278             (function (controllers) {
45279                 var Execution = (function () {
45280                     function Execution(MyModal, $scope) {
45281                         this.MyModal = MyModal;
45282                         this.$scope = $scope;
45283                         this.commandInfoList = [];
45284                     }
45285                     ;
45286                     Execution.prototype.add = function () {
45287                         this.$scope.$broadcast('close');
45288                         var commandInfoList = this.commandInfoList;
45289                         var commandInstance = this.MyModal.selectCommand();
45290                         commandInstance
45291                             .result
45292                             .then(function (command) {
45293                             commandInfoList.push(new app.declares.CommandInfo(command));
45294                         });
45295                     };
45296                     Execution.prototype.open = function () {
45297                         var result = this.MyModal.open('SelectCommand');
45298                         console.log(result);
45299                     };
45300                     Execution.prototype.remove = function (index, list) {
45301                         list.splice(index, 1);
45302                     };
45303                     Execution.prototype.close = function () {
45304                         console.log("close");
45305                     };
45306                     Execution.$inject = ['MyModal', '$scope'];
45307                     return Execution;
45308                 })();
45309                 controllers.Execution = Execution;
45310             })(controllers = app.controllers || (app.controllers = {}));
45311         })(app || (app = {}));
45312         var app;
45313         (function (app) {
45314             var controllers;
45315             (function (controllers) {
45316                 var Workspace = (function () {
45317                     function Workspace($scope, APIEndPoint, MyModal) {
45318                         this.$scope = $scope;
45319                         this.APIEndPoint = APIEndPoint;
45320                         this.MyModal = MyModal;
45321                         this.directoryList = [];
45322                         var controller = this;
45323                         var directoryList = this.directoryList;
45324                         var o = {
45325                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45326                             name: '',
45327                             parentId: '',
45328                             fileType: '',
45329                             createdAt: '',
45330                             updatedAt: '',
45331                             path: '/'
45332                         };
45333                         directoryList.push(o);
45334                     }
45335                     Workspace.prototype.addDirectory = function (info, directoryList) {
45336                         directoryList.push(info);
45337                     };
45338                     Workspace.prototype.debug = function () {
45339                         this.MyModal.preview();
45340                     };
45341                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
45342                     return Workspace;
45343                 })();
45344                 controllers.Workspace = Workspace;
45345             })(controllers = app.controllers || (app.controllers = {}));
45346         })(app || (app = {}));
45347         var app;
45348         (function (app) {
45349             var controllers;
45350             (function (controllers) {
45351                 var History = (function () {
45352                     function History($scope) {
45353                         this.page = "History";
45354                     }
45355                     History.$inject = ['$scope'];
45356                     return History;
45357                 })();
45358                 controllers.History = History;
45359             })(controllers = app.controllers || (app.controllers = {}));
45360         })(app || (app = {}));
45361         var app;
45362         (function (app) {
45363             var controllers;
45364             (function (controllers) {
45365                 var SelectCommand = (function () {
45366                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
45367                         this.APIEndPoint = APIEndPoint;
45368                         this.$modalInstance = $modalInstance;
45369                         var controller = this;
45370                         this.APIEndPoint
45371                             .getTags()
45372                             .$promise.then(function (result) {
45373                             controller.tags = result.info;
45374                         });
45375                         this.APIEndPoint
45376                             .getCommands()
45377                             .$promise.then(function (result) {
45378                             controller.commands = result.info;
45379                         });
45380                         this.currentTag = 'all';
45381                     }
45382                     SelectCommand.prototype.changeTag = function (tag) {
45383                         this.currentTag = tag;
45384                     };
45385                     SelectCommand.prototype.selectCommand = function (command) {
45386                         this.$modalInstance.close(command);
45387                     };
45388                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45389                     return SelectCommand;
45390                 })();
45391                 controllers.SelectCommand = SelectCommand;
45392             })(controllers = app.controllers || (app.controllers = {}));
45393         })(app || (app = {}));
45394         var app;
45395         (function (app) {
45396             var controllers;
45397             (function (controllers) {
45398                 var Preview = (function () {
45399                     function Preview($scope, APIEndPoint, $modalInstance) {
45400                         this.APIEndPoint = APIEndPoint;
45401                         this.$modalInstance = $modalInstance;
45402                         var controller = this;
45403                         console.log('preview');
45404                     }
45405                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45406                     return Preview;
45407                 })();
45408                 controllers.Preview = Preview;
45409             })(controllers = app.controllers || (app.controllers = {}));
45410         })(app || (app = {}));
45411         var filters;
45412         (function (filters) {
45413             function Tag() {
45414                 return function (commands, tag) {
45415                     var result = [];
45416                     angular.forEach(commands, function (command) {
45417                         var flag = false;
45418                         angular.forEach(command.tags, function (value) {
45419                             if (tag === value)
45420                                 flag = true;
45421                         });
45422                         if (flag)
45423                             result.push(command);
45424                     });
45425                     return result;
45426                 };
45427             }
45428             filters.Tag = Tag;
45429         })(filters || (filters = {}));
45430         var app;
45431         (function (app) {
45432             'use strict';
45433             var appName = 'zephyr';
45434             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45435             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45436                 $urlRouterProvider.otherwise('/execution');
45437                 $locationProvider.html5Mode({
45438                     enabled: true,
45439                     requireBase: false
45440                 });
45441                 $stateProvider
45442                     .state('execution', {
45443                     url: '/execution',
45444                     templateUrl: 'templates/execution.html',
45445                     controller: 'executionController',
45446                     controllerAs: 'c'
45447                 })
45448                     .state('workspace', {
45449                     url: '/workspace',
45450                     templateUrl: 'templates/workspace.html',
45451                     controller: 'workspaceController',
45452                     controllerAs: 'c'
45453                 })
45454                     .state('history', {
45455                     url: '/history',
45456                     templateUrl: 'templates/history.html',
45457                     controller: 'historyController',
45458                     controllerAs: 'c'
45459                 });
45460             });
45461             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
45462             app.zephyr.filter('Tag', filters.Tag);
45463             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
45464             app.zephyr.controller('previewController', app.controllers.Preview);
45465             app.zephyr.service('MyModal', app.services.MyModal);
45466             app.zephyr.controller('executionController', app.controllers.Execution);
45467             app.zephyr.controller('workspaceController', app.controllers.Workspace);
45468             app.zephyr.controller('historyController', app.controllers.History);
45469             app.zephyr.controller('commandController', app.directives.CommandController);
45470             app.zephyr.controller('optionController', app.directives.OptionController);
45471             app.zephyr.controller('directoryController', app.directives.DirectoryController);
45472             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
45473             app.zephyr.directive('command', app.directives.Command.Factory());
45474             app.zephyr.directive('option', app.directives.Option.Factory());
45475             app.zephyr.directive('directory', app.directives.Directory.Factory());
45476         })(app || (app = {}));
45477
45478
45479 /***/ },
45480 /* 16 */
45481 /***/ function(module, exports) {
45482
45483         var app;
45484         (function (app) {
45485             var declares;
45486             (function (declares) {
45487                 var CommandInfo = (function () {
45488                     function CommandInfo(name) {
45489                         this.name = name;
45490                     }
45491                     return CommandInfo;
45492                 })();
45493                 declares.CommandInfo = CommandInfo;
45494             })(declares = app.declares || (app.declares = {}));
45495         })(app || (app = {}));
45496         var app;
45497         (function (app) {
45498             var services;
45499             (function (services) {
45500                 var APIEndPoint = (function () {
45501                     function APIEndPoint($resource, $http) {
45502                         this.$resource = $resource;
45503                         this.$http = $http;
45504                     }
45505                     APIEndPoint.prototype.resource = function (endPoint, data) {
45506                         var customAction = {
45507                             method: 'GET',
45508                             isArray: false
45509                         };
45510                         var execute = {
45511                             method: 'POST',
45512                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
45513                         };
45514                         return this.$resource(endPoint, {}, { execute: execute });
45515                     };
45516                     APIEndPoint.prototype.getOptionControlFile = function (command) {
45517                         var endPoint = '/api/v1/optionControlFile/' + command;
45518                         return this.resource(endPoint, {}).get();
45519                     };
45520                     APIEndPoint.prototype.getFiles = function (fileId) {
45521                         var endPoint = '/api/v1/workspace';
45522                         if (fileId) {
45523                             endPoint += '/' + fileId;
45524                         }
45525                         return this.resource(endPoint, {}).get();
45526                     };
45527                     APIEndPoint.prototype.getDirectories = function () {
45528                         var endPoint = '/api/v1/all/workspace/directory';
45529                         return this.resource(endPoint, {}).get();
45530                     };
45531                     APIEndPoint.prototype.getTags = function () {
45532                         var endPoint = '/api/v1/tagList';
45533                         return this.resource(endPoint, {}).get();
45534                     };
45535                     APIEndPoint.prototype.getCommands = function () {
45536                         var endPoint = '/api/v1/commandList';
45537                         return this.resource(endPoint, {}).get();
45538                     };
45539                     APIEndPoint.prototype.execute = function (data) {
45540                         var endPoint = '/api/v1/execution';
45541                         var fd = new FormData();
45542                         fd.append('data', data);
45543                         return this.$http.post(endPoint, fd, {
45544                             headers: { 'Content-Type': undefined },
45545                             transformRequest: angular.identity
45546                         });
45547                     };
45548                     return APIEndPoint;
45549                 })();
45550                 services.APIEndPoint = APIEndPoint;
45551             })(services = app.services || (app.services = {}));
45552         })(app || (app = {}));
45553         var app;
45554         (function (app) {
45555             var services;
45556             (function (services) {
45557                 var MyModal = (function () {
45558                     function MyModal($uibModal) {
45559                         this.$uibModal = $uibModal;
45560                         this.modalOption = {
45561                             backdrop: true,
45562                             controller: null,
45563                             templateUrl: null,
45564                             size: null
45565                         };
45566                     }
45567                     MyModal.prototype.open = function (modalName) {
45568                         if (modalName === 'SelectCommand') {
45569                             this.modalOption.templateUrl = 'templates/select-command.html';
45570                             this.modalOption.size = 'lg';
45571                         }
45572                         return this.$uibModal.open(this.modalOption);
45573                     };
45574                     MyModal.prototype.selectCommand = function () {
45575                         this.modalOption.templateUrl = 'templates/select-command.html';
45576                         this.modalOption.controller = 'selectCommandController';
45577                         this.modalOption.controllerAs = 'c';
45578                         this.modalOption.size = 'lg';
45579                         return this.$uibModal.open(this.modalOption);
45580                     };
45581                     MyModal.prototype.preview = function () {
45582                         this.modalOption.templateUrl = 'templates/preview.html';
45583                         this.modalOption.controller = 'previewController';
45584                         this.modalOption.controllerAs = 'c';
45585                         this.modalOption.size = 'lg';
45586                         return this.$uibModal.open(this.modalOption);
45587                     };
45588                     MyModal.$inject = ['$uibModal'];
45589                     return MyModal;
45590                 })();
45591                 services.MyModal = MyModal;
45592             })(services = app.services || (app.services = {}));
45593         })(app || (app = {}));
45594         var app;
45595         (function (app) {
45596             var directives;
45597             (function (directives) {
45598                 var Command = (function () {
45599                     function Command() {
45600                         this.restrict = 'E';
45601                         this.replace = true;
45602                         this.scope = true;
45603                         this.controller = 'commandController';
45604                         this.controllerAs = 'ctrl';
45605                         this.bindToController = {
45606                             index: '=',
45607                             name: '=',
45608                             remove: '&',
45609                             list: '='
45610                         };
45611                         this.templateUrl = 'templates/command.html';
45612                     }
45613                     Command.Factory = function () {
45614                         var directive = function () {
45615                             return new Command();
45616                         };
45617                         directive.$inject = [];
45618                         return directive;
45619                     };
45620                     return Command;
45621                 })();
45622                 directives.Command = Command;
45623                 var CommandController = (function () {
45624                     function CommandController(APIEndPoint, $scope, MyModal) {
45625                         this.APIEndPoint = APIEndPoint;
45626                         this.$scope = $scope;
45627                         this.MyModal = MyModal;
45628                         var controller = this;
45629                         this.APIEndPoint
45630                             .getOptionControlFile('mrcImageNoiseAdd')
45631                             .$promise
45632                             .then(function (result) {
45633                             controller.options = result.info;
45634                         });
45635                         this.APIEndPoint
45636                             .getDirectories()
45637                             .$promise
45638                             .then(function (result) {
45639                             controller.dirs = result.info;
45640                         });
45641                         this.heading = "[" + this.index + "]: dcdFilePring";
45642                         this.isOpen = true;
45643                         this.$scope.$on('close', function () {
45644                             controller.isOpen = false;
45645                         });
45646                     }
45647                     CommandController.prototype.submit = function () {
45648                         var opt = [];
45649                         angular.forEach(this.options, function (option) {
45650                             var obj = {
45651                                 name: option.option,
45652                                 arguments: []
45653                             };
45654                             angular.forEach(option.arg, function (arg) {
45655                                 if (arg.input) {
45656                                     if (typeof arg.input === 'object') {
45657                                         obj.arguments.push(arg.input.name);
45658                                     }
45659                                     else {
45660                                         obj.arguments.push(arg.input);
45661                                     }
45662                                 }
45663                             });
45664                             if (obj.arguments.length > 0) {
45665                                 opt.push(obj);
45666                             }
45667                         });
45668                         var execObj = {
45669                             command: this.name,
45670                             workspace: this.workspace.fileId,
45671                             options: opt
45672                         };
45673                         this.APIEndPoint
45674                             .execute(JSON.stringify(execObj))
45675                             .then(function (result) {
45676                             console.log(result);
45677                         });
45678                     };
45679                     CommandController.prototype.removeMySelf = function (index) {
45680                         this.remove()(index, this.list);
45681                     };
45682                     CommandController.prototype.reloadFiles = function () {
45683                         var _this = this;
45684                         var fileId = this.workspace.fileId;
45685                         this.APIEndPoint
45686                             .getFiles(fileId)
45687                             .$promise
45688                             .then(function (result) {
45689                             var status = result.status;
45690                             if (status === 'success') {
45691                                 _this.files = result.info;
45692                             }
45693                             else {
45694                                 console.log(result.message);
45695                             }
45696                         });
45697                     };
45698                     CommandController.prototype.debug = function () {
45699                         this.MyModal.preview();
45700                     };
45701                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
45702                     return CommandController;
45703                 })();
45704                 directives.CommandController = CommandController;
45705             })(directives = app.directives || (app.directives = {}));
45706         })(app || (app = {}));
45707         var app;
45708         (function (app) {
45709             var directives;
45710             (function (directives) {
45711                 var HeaderMenu = (function () {
45712                     function HeaderMenu() {
45713                         this.restrict = 'E';
45714                         this.replace = true;
45715                         this.templateUrl = 'templates/header-menu.html';
45716                     }
45717                     HeaderMenu.Factory = function () {
45718                         var directive = function () {
45719                             return new HeaderMenu();
45720                         };
45721                         return directive;
45722                     };
45723                     return HeaderMenu;
45724                 })();
45725                 directives.HeaderMenu = HeaderMenu;
45726             })(directives = app.directives || (app.directives = {}));
45727         })(app || (app = {}));
45728         var app;
45729         (function (app) {
45730             var directives;
45731             (function (directives) {
45732                 var Option = (function () {
45733                     function Option() {
45734                         this.restrict = 'E';
45735                         this.replace = true;
45736                         this.controller = 'optionController';
45737                         this.bindToController = {
45738                             info: '=',
45739                             files: '='
45740                         };
45741                         this.scope = true;
45742                         this.templateUrl = 'templates/option.html';
45743                         this.controllerAs = 'ctrl';
45744                     }
45745                     Option.Factory = function () {
45746                         var directive = function () {
45747                             return new Option();
45748                         };
45749                         directive.$inject = [];
45750                         return directive;
45751                     };
45752                     return Option;
45753                 })();
45754                 directives.Option = Option;
45755                 var OptionController = (function () {
45756                     function OptionController() {
45757                         var controller = this;
45758                         angular.forEach(controller.info.arg, function (arg) {
45759                             if (arg.initialValue) {
45760                                 if (arg.formType === 'number') {
45761                                     arg.input = parseInt(arg.initialValue);
45762                                 }
45763                                 else {
45764                                     arg.input = arg.initialValue;
45765                                 }
45766                             }
45767                         });
45768                     }
45769                     OptionController.$inject = [];
45770                     return OptionController;
45771                 })();
45772                 directives.OptionController = OptionController;
45773             })(directives = app.directives || (app.directives = {}));
45774         })(app || (app = {}));
45775         var app;
45776         (function (app) {
45777             var directives;
45778             (function (directives) {
45779                 var Directory = (function () {
45780                     function Directory() {
45781                         this.restrict = 'E';
45782                         this.replace = true;
45783                         this.controller = 'directoryController';
45784                         this.controllerAs = 'ctrl';
45785                         this.bindToController = {
45786                             info: '=',
45787                             add: '&',
45788                             list: '=',
45789                             files: '='
45790                         };
45791                         this.templateUrl = 'templates/directory.html';
45792                     }
45793                     Directory.Factory = function () {
45794                         var directive = function () {
45795                             return new Directory();
45796                         };
45797                         return directive;
45798                     };
45799                     return Directory;
45800                 })();
45801                 directives.Directory = Directory;
45802                 var DirectoryController = (function () {
45803                     function DirectoryController(APIEndPoint, $scope) {
45804                         this.APIEndPoint = APIEndPoint;
45805                         this.$scope = $scope;
45806                         var controller = this;
45807                         this.APIEndPoint
45808                             .getFiles(this.info.fileId)
45809                             .$promise
45810                             .then(function (result) {
45811                             if (result.status === 'success') {
45812                                 controller.files = result.info;
45813                                 angular.forEach(result.info, function (file) {
45814                                     if (file.fileType === '0') {
45815                                         var o = file;
45816                                         if (controller.info.path === '/') {
45817                                             o.path = '/' + file.name;
45818                                         }
45819                                         else {
45820                                             o.path = controller.info.path + '/' + file.name;
45821                                         }
45822                                         controller.add()(o, controller.list);
45823                                     }
45824                                 });
45825                             }
45826                             ;
45827                         });
45828                     }
45829                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
45830                     return DirectoryController;
45831                 })();
45832                 directives.DirectoryController = DirectoryController;
45833             })(directives = app.directives || (app.directives = {}));
45834         })(app || (app = {}));
45835         var app;
45836         (function (app) {
45837             var controllers;
45838             (function (controllers) {
45839                 var Execution = (function () {
45840                     function Execution(MyModal, $scope) {
45841                         this.MyModal = MyModal;
45842                         this.$scope = $scope;
45843                         this.commandInfoList = [];
45844                     }
45845                     ;
45846                     Execution.prototype.add = function () {
45847                         this.$scope.$broadcast('close');
45848                         var commandInfoList = this.commandInfoList;
45849                         var commandInstance = this.MyModal.selectCommand();
45850                         commandInstance
45851                             .result
45852                             .then(function (command) {
45853                             commandInfoList.push(new app.declares.CommandInfo(command));
45854                         });
45855                     };
45856                     Execution.prototype.open = function () {
45857                         var result = this.MyModal.open('SelectCommand');
45858                         console.log(result);
45859                     };
45860                     Execution.prototype.remove = function (index, list) {
45861                         list.splice(index, 1);
45862                     };
45863                     Execution.prototype.close = function () {
45864                         console.log("close");
45865                     };
45866                     Execution.$inject = ['MyModal', '$scope'];
45867                     return Execution;
45868                 })();
45869                 controllers.Execution = Execution;
45870             })(controllers = app.controllers || (app.controllers = {}));
45871         })(app || (app = {}));
45872         var app;
45873         (function (app) {
45874             var controllers;
45875             (function (controllers) {
45876                 var Workspace = (function () {
45877                     function Workspace($scope, APIEndPoint, MyModal) {
45878                         this.$scope = $scope;
45879                         this.APIEndPoint = APIEndPoint;
45880                         this.MyModal = MyModal;
45881                         this.directoryList = [];
45882                         var controller = this;
45883                         var directoryList = this.directoryList;
45884                         var o = {
45885                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
45886                             name: '',
45887                             parentId: '',
45888                             fileType: '',
45889                             createdAt: '',
45890                             updatedAt: '',
45891                             path: '/'
45892                         };
45893                         directoryList.push(o);
45894                     }
45895                     Workspace.prototype.addDirectory = function (info, directoryList) {
45896                         directoryList.push(info);
45897                     };
45898                     Workspace.prototype.debug = function () {
45899                         this.MyModal.preview();
45900                     };
45901                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
45902                     return Workspace;
45903                 })();
45904                 controllers.Workspace = Workspace;
45905             })(controllers = app.controllers || (app.controllers = {}));
45906         })(app || (app = {}));
45907         var app;
45908         (function (app) {
45909             var controllers;
45910             (function (controllers) {
45911                 var History = (function () {
45912                     function History($scope) {
45913                         this.page = "History";
45914                     }
45915                     History.$inject = ['$scope'];
45916                     return History;
45917                 })();
45918                 controllers.History = History;
45919             })(controllers = app.controllers || (app.controllers = {}));
45920         })(app || (app = {}));
45921         var app;
45922         (function (app) {
45923             var controllers;
45924             (function (controllers) {
45925                 var SelectCommand = (function () {
45926                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
45927                         this.APIEndPoint = APIEndPoint;
45928                         this.$modalInstance = $modalInstance;
45929                         var controller = this;
45930                         this.APIEndPoint
45931                             .getTags()
45932                             .$promise.then(function (result) {
45933                             controller.tags = result.info;
45934                         });
45935                         this.APIEndPoint
45936                             .getCommands()
45937                             .$promise.then(function (result) {
45938                             controller.commands = result.info;
45939                         });
45940                         this.currentTag = 'all';
45941                     }
45942                     SelectCommand.prototype.changeTag = function (tag) {
45943                         this.currentTag = tag;
45944                     };
45945                     SelectCommand.prototype.selectCommand = function (command) {
45946                         this.$modalInstance.close(command);
45947                     };
45948                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45949                     return SelectCommand;
45950                 })();
45951                 controllers.SelectCommand = SelectCommand;
45952             })(controllers = app.controllers || (app.controllers = {}));
45953         })(app || (app = {}));
45954         var app;
45955         (function (app) {
45956             var controllers;
45957             (function (controllers) {
45958                 var Preview = (function () {
45959                     function Preview($scope, APIEndPoint, $modalInstance) {
45960                         this.APIEndPoint = APIEndPoint;
45961                         this.$modalInstance = $modalInstance;
45962                         var controller = this;
45963                         console.log('preview');
45964                     }
45965                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
45966                     return Preview;
45967                 })();
45968                 controllers.Preview = Preview;
45969             })(controllers = app.controllers || (app.controllers = {}));
45970         })(app || (app = {}));
45971         var filters;
45972         (function (filters) {
45973             function Tag() {
45974                 return function (commands, tag) {
45975                     var result = [];
45976                     angular.forEach(commands, function (command) {
45977                         var flag = false;
45978                         angular.forEach(command.tags, function (value) {
45979                             if (tag === value)
45980                                 flag = true;
45981                         });
45982                         if (flag)
45983                             result.push(command);
45984                     });
45985                     return result;
45986                 };
45987             }
45988             filters.Tag = Tag;
45989         })(filters || (filters = {}));
45990         var app;
45991         (function (app) {
45992             'use strict';
45993             var appName = 'zephyr';
45994             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
45995             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
45996                 $urlRouterProvider.otherwise('/execution');
45997                 $locationProvider.html5Mode({
45998                     enabled: true,
45999                     requireBase: false
46000                 });
46001                 $stateProvider
46002                     .state('execution', {
46003                     url: '/execution',
46004                     templateUrl: 'templates/execution.html',
46005                     controller: 'executionController',
46006                     controllerAs: 'c'
46007                 })
46008                     .state('workspace', {
46009                     url: '/workspace',
46010                     templateUrl: 'templates/workspace.html',
46011                     controller: 'workspaceController',
46012                     controllerAs: 'c'
46013                 })
46014                     .state('history', {
46015                     url: '/history',
46016                     templateUrl: 'templates/history.html',
46017                     controller: 'historyController',
46018                     controllerAs: 'c'
46019                 });
46020             });
46021             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
46022             app.zephyr.filter('Tag', filters.Tag);
46023             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
46024             app.zephyr.controller('previewController', app.controllers.Preview);
46025             app.zephyr.service('MyModal', app.services.MyModal);
46026             app.zephyr.controller('executionController', app.controllers.Execution);
46027             app.zephyr.controller('workspaceController', app.controllers.Workspace);
46028             app.zephyr.controller('historyController', app.controllers.History);
46029             app.zephyr.controller('commandController', app.directives.CommandController);
46030             app.zephyr.controller('optionController', app.directives.OptionController);
46031             app.zephyr.controller('directoryController', app.directives.DirectoryController);
46032             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
46033             app.zephyr.directive('command', app.directives.Command.Factory());
46034             app.zephyr.directive('option', app.directives.Option.Factory());
46035             app.zephyr.directive('directory', app.directives.Directory.Factory());
46036         })(app || (app = {}));
46037
46038
46039 /***/ },
46040 /* 17 */
46041 /***/ function(module, exports) {
46042
46043         var app;
46044         (function (app) {
46045             var declares;
46046             (function (declares) {
46047                 var CommandInfo = (function () {
46048                     function CommandInfo(name) {
46049                         this.name = name;
46050                     }
46051                     return CommandInfo;
46052                 })();
46053                 declares.CommandInfo = CommandInfo;
46054             })(declares = app.declares || (app.declares = {}));
46055         })(app || (app = {}));
46056         var app;
46057         (function (app) {
46058             var services;
46059             (function (services) {
46060                 var APIEndPoint = (function () {
46061                     function APIEndPoint($resource, $http) {
46062                         this.$resource = $resource;
46063                         this.$http = $http;
46064                     }
46065                     APIEndPoint.prototype.resource = function (endPoint, data) {
46066                         var customAction = {
46067                             method: 'GET',
46068                             isArray: false
46069                         };
46070                         var execute = {
46071                             method: 'POST',
46072                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
46073                         };
46074                         return this.$resource(endPoint, {}, { execute: execute });
46075                     };
46076                     APIEndPoint.prototype.getOptionControlFile = function (command) {
46077                         var endPoint = '/api/v1/optionControlFile/' + command;
46078                         return this.resource(endPoint, {}).get();
46079                     };
46080                     APIEndPoint.prototype.getFiles = function (fileId) {
46081                         var endPoint = '/api/v1/workspace';
46082                         if (fileId) {
46083                             endPoint += '/' + fileId;
46084                         }
46085                         return this.resource(endPoint, {}).get();
46086                     };
46087                     APIEndPoint.prototype.getDirectories = function () {
46088                         var endPoint = '/api/v1/all/workspace/directory';
46089                         return this.resource(endPoint, {}).get();
46090                     };
46091                     APIEndPoint.prototype.getTags = function () {
46092                         var endPoint = '/api/v1/tagList';
46093                         return this.resource(endPoint, {}).get();
46094                     };
46095                     APIEndPoint.prototype.getCommands = function () {
46096                         var endPoint = '/api/v1/commandList';
46097                         return this.resource(endPoint, {}).get();
46098                     };
46099                     APIEndPoint.prototype.execute = function (data) {
46100                         var endPoint = '/api/v1/execution';
46101                         var fd = new FormData();
46102                         fd.append('data', data);
46103                         return this.$http.post(endPoint, fd, {
46104                             headers: { 'Content-Type': undefined },
46105                             transformRequest: angular.identity
46106                         });
46107                     };
46108                     return APIEndPoint;
46109                 })();
46110                 services.APIEndPoint = APIEndPoint;
46111             })(services = app.services || (app.services = {}));
46112         })(app || (app = {}));
46113         var app;
46114         (function (app) {
46115             var services;
46116             (function (services) {
46117                 var MyModal = (function () {
46118                     function MyModal($uibModal) {
46119                         this.$uibModal = $uibModal;
46120                         this.modalOption = {
46121                             backdrop: true,
46122                             controller: null,
46123                             templateUrl: null,
46124                             size: null
46125                         };
46126                     }
46127                     MyModal.prototype.open = function (modalName) {
46128                         if (modalName === 'SelectCommand') {
46129                             this.modalOption.templateUrl = 'templates/select-command.html';
46130                             this.modalOption.size = 'lg';
46131                         }
46132                         return this.$uibModal.open(this.modalOption);
46133                     };
46134                     MyModal.prototype.selectCommand = function () {
46135                         this.modalOption.templateUrl = 'templates/select-command.html';
46136                         this.modalOption.controller = 'selectCommandController';
46137                         this.modalOption.controllerAs = 'c';
46138                         this.modalOption.size = 'lg';
46139                         return this.$uibModal.open(this.modalOption);
46140                     };
46141                     MyModal.prototype.preview = function () {
46142                         this.modalOption.templateUrl = 'templates/preview.html';
46143                         this.modalOption.controller = 'previewController';
46144                         this.modalOption.controllerAs = 'c';
46145                         this.modalOption.size = 'lg';
46146                         return this.$uibModal.open(this.modalOption);
46147                     };
46148                     MyModal.$inject = ['$uibModal'];
46149                     return MyModal;
46150                 })();
46151                 services.MyModal = MyModal;
46152             })(services = app.services || (app.services = {}));
46153         })(app || (app = {}));
46154         var app;
46155         (function (app) {
46156             var directives;
46157             (function (directives) {
46158                 var Command = (function () {
46159                     function Command() {
46160                         this.restrict = 'E';
46161                         this.replace = true;
46162                         this.scope = true;
46163                         this.controller = 'commandController';
46164                         this.controllerAs = 'ctrl';
46165                         this.bindToController = {
46166                             index: '=',
46167                             name: '=',
46168                             remove: '&',
46169                             list: '='
46170                         };
46171                         this.templateUrl = 'templates/command.html';
46172                     }
46173                     Command.Factory = function () {
46174                         var directive = function () {
46175                             return new Command();
46176                         };
46177                         directive.$inject = [];
46178                         return directive;
46179                     };
46180                     return Command;
46181                 })();
46182                 directives.Command = Command;
46183                 var CommandController = (function () {
46184                     function CommandController(APIEndPoint, $scope, MyModal) {
46185                         this.APIEndPoint = APIEndPoint;
46186                         this.$scope = $scope;
46187                         this.MyModal = MyModal;
46188                         var controller = this;
46189                         this.APIEndPoint
46190                             .getOptionControlFile('mrcImageNoiseAdd')
46191                             .$promise
46192                             .then(function (result) {
46193                             controller.options = result.info;
46194                         });
46195                         this.APIEndPoint
46196                             .getDirectories()
46197                             .$promise
46198                             .then(function (result) {
46199                             controller.dirs = result.info;
46200                         });
46201                         this.heading = "[" + this.index + "]: dcdFilePring";
46202                         this.isOpen = true;
46203                         this.$scope.$on('close', function () {
46204                             controller.isOpen = false;
46205                         });
46206                     }
46207                     CommandController.prototype.submit = function () {
46208                         var opt = [];
46209                         angular.forEach(this.options, function (option) {
46210                             var obj = {
46211                                 name: option.option,
46212                                 arguments: []
46213                             };
46214                             angular.forEach(option.arg, function (arg) {
46215                                 if (arg.input) {
46216                                     if (typeof arg.input === 'object') {
46217                                         obj.arguments.push(arg.input.name);
46218                                     }
46219                                     else {
46220                                         obj.arguments.push(arg.input);
46221                                     }
46222                                 }
46223                             });
46224                             if (obj.arguments.length > 0) {
46225                                 opt.push(obj);
46226                             }
46227                         });
46228                         var execObj = {
46229                             command: this.name,
46230                             workspace: this.workspace.fileId,
46231                             options: opt
46232                         };
46233                         this.APIEndPoint
46234                             .execute(JSON.stringify(execObj))
46235                             .then(function (result) {
46236                             console.log(result);
46237                         });
46238                     };
46239                     CommandController.prototype.removeMySelf = function (index) {
46240                         this.remove()(index, this.list);
46241                     };
46242                     CommandController.prototype.reloadFiles = function () {
46243                         var _this = this;
46244                         var fileId = this.workspace.fileId;
46245                         this.APIEndPoint
46246                             .getFiles(fileId)
46247                             .$promise
46248                             .then(function (result) {
46249                             var status = result.status;
46250                             if (status === 'success') {
46251                                 _this.files = result.info;
46252                             }
46253                             else {
46254                                 console.log(result.message);
46255                             }
46256                         });
46257                     };
46258                     CommandController.prototype.debug = function () {
46259                         this.MyModal.preview();
46260                     };
46261                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
46262                     return CommandController;
46263                 })();
46264                 directives.CommandController = CommandController;
46265             })(directives = app.directives || (app.directives = {}));
46266         })(app || (app = {}));
46267         var app;
46268         (function (app) {
46269             var directives;
46270             (function (directives) {
46271                 var HeaderMenu = (function () {
46272                     function HeaderMenu() {
46273                         this.restrict = 'E';
46274                         this.replace = true;
46275                         this.templateUrl = 'templates/header-menu.html';
46276                     }
46277                     HeaderMenu.Factory = function () {
46278                         var directive = function () {
46279                             return new HeaderMenu();
46280                         };
46281                         return directive;
46282                     };
46283                     return HeaderMenu;
46284                 })();
46285                 directives.HeaderMenu = HeaderMenu;
46286             })(directives = app.directives || (app.directives = {}));
46287         })(app || (app = {}));
46288         var app;
46289         (function (app) {
46290             var directives;
46291             (function (directives) {
46292                 var Option = (function () {
46293                     function Option() {
46294                         this.restrict = 'E';
46295                         this.replace = true;
46296                         this.controller = 'optionController';
46297                         this.bindToController = {
46298                             info: '=',
46299                             files: '='
46300                         };
46301                         this.scope = true;
46302                         this.templateUrl = 'templates/option.html';
46303                         this.controllerAs = 'ctrl';
46304                     }
46305                     Option.Factory = function () {
46306                         var directive = function () {
46307                             return new Option();
46308                         };
46309                         directive.$inject = [];
46310                         return directive;
46311                     };
46312                     return Option;
46313                 })();
46314                 directives.Option = Option;
46315                 var OptionController = (function () {
46316                     function OptionController() {
46317                         var controller = this;
46318                         angular.forEach(controller.info.arg, function (arg) {
46319                             if (arg.initialValue) {
46320                                 if (arg.formType === 'number') {
46321                                     arg.input = parseInt(arg.initialValue);
46322                                 }
46323                                 else {
46324                                     arg.input = arg.initialValue;
46325                                 }
46326                             }
46327                         });
46328                     }
46329                     OptionController.$inject = [];
46330                     return OptionController;
46331                 })();
46332                 directives.OptionController = OptionController;
46333             })(directives = app.directives || (app.directives = {}));
46334         })(app || (app = {}));
46335         var app;
46336         (function (app) {
46337             var directives;
46338             (function (directives) {
46339                 var Directory = (function () {
46340                     function Directory() {
46341                         this.restrict = 'E';
46342                         this.replace = true;
46343                         this.controller = 'directoryController';
46344                         this.controllerAs = 'ctrl';
46345                         this.bindToController = {
46346                             info: '=',
46347                             add: '&',
46348                             list: '=',
46349                             files: '='
46350                         };
46351                         this.templateUrl = 'templates/directory.html';
46352                     }
46353                     Directory.Factory = function () {
46354                         var directive = function () {
46355                             return new Directory();
46356                         };
46357                         return directive;
46358                     };
46359                     return Directory;
46360                 })();
46361                 directives.Directory = Directory;
46362                 var DirectoryController = (function () {
46363                     function DirectoryController(APIEndPoint, $scope) {
46364                         this.APIEndPoint = APIEndPoint;
46365                         this.$scope = $scope;
46366                         var controller = this;
46367                         this.APIEndPoint
46368                             .getFiles(this.info.fileId)
46369                             .$promise
46370                             .then(function (result) {
46371                             if (result.status === 'success') {
46372                                 controller.files = result.info;
46373                                 angular.forEach(result.info, function (file) {
46374                                     if (file.fileType === '0') {
46375                                         var o = file;
46376                                         if (controller.info.path === '/') {
46377                                             o.path = '/' + file.name;
46378                                         }
46379                                         else {
46380                                             o.path = controller.info.path + '/' + file.name;
46381                                         }
46382                                         controller.add()(o, controller.list);
46383                                     }
46384                                 });
46385                             }
46386                             ;
46387                         });
46388                     }
46389                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
46390                     return DirectoryController;
46391                 })();
46392                 directives.DirectoryController = DirectoryController;
46393             })(directives = app.directives || (app.directives = {}));
46394         })(app || (app = {}));
46395         var app;
46396         (function (app) {
46397             var controllers;
46398             (function (controllers) {
46399                 var Execution = (function () {
46400                     function Execution(MyModal, $scope) {
46401                         this.MyModal = MyModal;
46402                         this.$scope = $scope;
46403                         this.commandInfoList = [];
46404                     }
46405                     ;
46406                     Execution.prototype.add = function () {
46407                         this.$scope.$broadcast('close');
46408                         var commandInfoList = this.commandInfoList;
46409                         var commandInstance = this.MyModal.selectCommand();
46410                         commandInstance
46411                             .result
46412                             .then(function (command) {
46413                             commandInfoList.push(new app.declares.CommandInfo(command));
46414                         });
46415                     };
46416                     Execution.prototype.open = function () {
46417                         var result = this.MyModal.open('SelectCommand');
46418                         console.log(result);
46419                     };
46420                     Execution.prototype.remove = function (index, list) {
46421                         list.splice(index, 1);
46422                     };
46423                     Execution.prototype.close = function () {
46424                         console.log("close");
46425                     };
46426                     Execution.$inject = ['MyModal', '$scope'];
46427                     return Execution;
46428                 })();
46429                 controllers.Execution = Execution;
46430             })(controllers = app.controllers || (app.controllers = {}));
46431         })(app || (app = {}));
46432         var app;
46433         (function (app) {
46434             var controllers;
46435             (function (controllers) {
46436                 var Workspace = (function () {
46437                     function Workspace($scope, APIEndPoint, MyModal) {
46438                         this.$scope = $scope;
46439                         this.APIEndPoint = APIEndPoint;
46440                         this.MyModal = MyModal;
46441                         this.directoryList = [];
46442                         var controller = this;
46443                         var directoryList = this.directoryList;
46444                         var o = {
46445                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
46446                             name: '',
46447                             parentId: '',
46448                             fileType: '',
46449                             createdAt: '',
46450                             updatedAt: '',
46451                             path: '/'
46452                         };
46453                         directoryList.push(o);
46454                     }
46455                     Workspace.prototype.addDirectory = function (info, directoryList) {
46456                         directoryList.push(info);
46457                     };
46458                     Workspace.prototype.debug = function () {
46459                         this.MyModal.preview();
46460                     };
46461                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
46462                     return Workspace;
46463                 })();
46464                 controllers.Workspace = Workspace;
46465             })(controllers = app.controllers || (app.controllers = {}));
46466         })(app || (app = {}));
46467         var app;
46468         (function (app) {
46469             var controllers;
46470             (function (controllers) {
46471                 var History = (function () {
46472                     function History($scope) {
46473                         this.page = "History";
46474                     }
46475                     History.$inject = ['$scope'];
46476                     return History;
46477                 })();
46478                 controllers.History = History;
46479             })(controllers = app.controllers || (app.controllers = {}));
46480         })(app || (app = {}));
46481         var app;
46482         (function (app) {
46483             var controllers;
46484             (function (controllers) {
46485                 var SelectCommand = (function () {
46486                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
46487                         this.APIEndPoint = APIEndPoint;
46488                         this.$modalInstance = $modalInstance;
46489                         var controller = this;
46490                         this.APIEndPoint
46491                             .getTags()
46492                             .$promise.then(function (result) {
46493                             controller.tags = result.info;
46494                         });
46495                         this.APIEndPoint
46496                             .getCommands()
46497                             .$promise.then(function (result) {
46498                             controller.commands = result.info;
46499                         });
46500                         this.currentTag = 'all';
46501                     }
46502                     SelectCommand.prototype.changeTag = function (tag) {
46503                         this.currentTag = tag;
46504                     };
46505                     SelectCommand.prototype.selectCommand = function (command) {
46506                         this.$modalInstance.close(command);
46507                     };
46508                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
46509                     return SelectCommand;
46510                 })();
46511                 controllers.SelectCommand = SelectCommand;
46512             })(controllers = app.controllers || (app.controllers = {}));
46513         })(app || (app = {}));
46514         var app;
46515         (function (app) {
46516             var controllers;
46517             (function (controllers) {
46518                 var Preview = (function () {
46519                     function Preview($scope, APIEndPoint, $modalInstance) {
46520                         this.APIEndPoint = APIEndPoint;
46521                         this.$modalInstance = $modalInstance;
46522                         var controller = this;
46523                         console.log('preview');
46524                     }
46525                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
46526                     return Preview;
46527                 })();
46528                 controllers.Preview = Preview;
46529             })(controllers = app.controllers || (app.controllers = {}));
46530         })(app || (app = {}));
46531         var filters;
46532         (function (filters) {
46533             function Tag() {
46534                 return function (commands, tag) {
46535                     var result = [];
46536                     angular.forEach(commands, function (command) {
46537                         var flag = false;
46538                         angular.forEach(command.tags, function (value) {
46539                             if (tag === value)
46540                                 flag = true;
46541                         });
46542                         if (flag)
46543                             result.push(command);
46544                     });
46545                     return result;
46546                 };
46547             }
46548             filters.Tag = Tag;
46549         })(filters || (filters = {}));
46550         var app;
46551         (function (app) {
46552             'use strict';
46553             var appName = 'zephyr';
46554             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
46555             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
46556                 $urlRouterProvider.otherwise('/execution');
46557                 $locationProvider.html5Mode({
46558                     enabled: true,
46559                     requireBase: false
46560                 });
46561                 $stateProvider
46562                     .state('execution', {
46563                     url: '/execution',
46564                     templateUrl: 'templates/execution.html',
46565                     controller: 'executionController',
46566                     controllerAs: 'c'
46567                 })
46568                     .state('workspace', {
46569                     url: '/workspace',
46570                     templateUrl: 'templates/workspace.html',
46571                     controller: 'workspaceController',
46572                     controllerAs: 'c'
46573                 })
46574                     .state('history', {
46575                     url: '/history',
46576                     templateUrl: 'templates/history.html',
46577                     controller: 'historyController',
46578                     controllerAs: 'c'
46579                 });
46580             });
46581             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
46582             app.zephyr.filter('Tag', filters.Tag);
46583             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
46584             app.zephyr.controller('previewController', app.controllers.Preview);
46585             app.zephyr.service('MyModal', app.services.MyModal);
46586             app.zephyr.controller('executionController', app.controllers.Execution);
46587             app.zephyr.controller('workspaceController', app.controllers.Workspace);
46588             app.zephyr.controller('historyController', app.controllers.History);
46589             app.zephyr.controller('commandController', app.directives.CommandController);
46590             app.zephyr.controller('optionController', app.directives.OptionController);
46591             app.zephyr.controller('directoryController', app.directives.DirectoryController);
46592             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
46593             app.zephyr.directive('command', app.directives.Command.Factory());
46594             app.zephyr.directive('option', app.directives.Option.Factory());
46595             app.zephyr.directive('directory', app.directives.Directory.Factory());
46596         })(app || (app = {}));
46597
46598
46599 /***/ },
46600 /* 18 */
46601 /***/ function(module, exports) {
46602
46603         var app;
46604         (function (app) {
46605             var declares;
46606             (function (declares) {
46607                 var CommandInfo = (function () {
46608                     function CommandInfo(name) {
46609                         this.name = name;
46610                     }
46611                     return CommandInfo;
46612                 })();
46613                 declares.CommandInfo = CommandInfo;
46614             })(declares = app.declares || (app.declares = {}));
46615         })(app || (app = {}));
46616         var app;
46617         (function (app) {
46618             var services;
46619             (function (services) {
46620                 var APIEndPoint = (function () {
46621                     function APIEndPoint($resource, $http) {
46622                         this.$resource = $resource;
46623                         this.$http = $http;
46624                     }
46625                     APIEndPoint.prototype.resource = function (endPoint, data) {
46626                         var customAction = {
46627                             method: 'GET',
46628                             isArray: false
46629                         };
46630                         var execute = {
46631                             method: 'POST',
46632                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
46633                         };
46634                         return this.$resource(endPoint, {}, { execute: execute });
46635                     };
46636                     APIEndPoint.prototype.getOptionControlFile = function (command) {
46637                         var endPoint = '/api/v1/optionControlFile/' + command;
46638                         return this.resource(endPoint, {}).get();
46639                     };
46640                     APIEndPoint.prototype.getFiles = function (fileId) {
46641                         var endPoint = '/api/v1/workspace';
46642                         if (fileId) {
46643                             endPoint += '/' + fileId;
46644                         }
46645                         return this.resource(endPoint, {}).get();
46646                     };
46647                     APIEndPoint.prototype.getDirectories = function () {
46648                         var endPoint = '/api/v1/all/workspace/directory';
46649                         return this.resource(endPoint, {}).get();
46650                     };
46651                     APIEndPoint.prototype.getTags = function () {
46652                         var endPoint = '/api/v1/tagList';
46653                         return this.resource(endPoint, {}).get();
46654                     };
46655                     APIEndPoint.prototype.getCommands = function () {
46656                         var endPoint = '/api/v1/commandList';
46657                         return this.resource(endPoint, {}).get();
46658                     };
46659                     APIEndPoint.prototype.execute = function (data) {
46660                         var endPoint = '/api/v1/execution';
46661                         var fd = new FormData();
46662                         fd.append('data', data);
46663                         return this.$http.post(endPoint, fd, {
46664                             headers: { 'Content-Type': undefined },
46665                             transformRequest: angular.identity
46666                         });
46667                     };
46668                     return APIEndPoint;
46669                 })();
46670                 services.APIEndPoint = APIEndPoint;
46671             })(services = app.services || (app.services = {}));
46672         })(app || (app = {}));
46673         var app;
46674         (function (app) {
46675             var services;
46676             (function (services) {
46677                 var MyModal = (function () {
46678                     function MyModal($uibModal) {
46679                         this.$uibModal = $uibModal;
46680                         this.modalOption = {
46681                             backdrop: true,
46682                             controller: null,
46683                             templateUrl: null,
46684                             size: null
46685                         };
46686                     }
46687                     MyModal.prototype.open = function (modalName) {
46688                         if (modalName === 'SelectCommand') {
46689                             this.modalOption.templateUrl = 'templates/select-command.html';
46690                             this.modalOption.size = 'lg';
46691                         }
46692                         return this.$uibModal.open(this.modalOption);
46693                     };
46694                     MyModal.prototype.selectCommand = function () {
46695                         this.modalOption.templateUrl = 'templates/select-command.html';
46696                         this.modalOption.controller = 'selectCommandController';
46697                         this.modalOption.controllerAs = 'c';
46698                         this.modalOption.size = 'lg';
46699                         return this.$uibModal.open(this.modalOption);
46700                     };
46701                     MyModal.prototype.preview = function () {
46702                         this.modalOption.templateUrl = 'templates/preview.html';
46703                         this.modalOption.controller = 'previewController';
46704                         this.modalOption.controllerAs = 'c';
46705                         this.modalOption.size = 'lg';
46706                         return this.$uibModal.open(this.modalOption);
46707                     };
46708                     MyModal.$inject = ['$uibModal'];
46709                     return MyModal;
46710                 })();
46711                 services.MyModal = MyModal;
46712             })(services = app.services || (app.services = {}));
46713         })(app || (app = {}));
46714         var app;
46715         (function (app) {
46716             var directives;
46717             (function (directives) {
46718                 var Command = (function () {
46719                     function Command() {
46720                         this.restrict = 'E';
46721                         this.replace = true;
46722                         this.scope = true;
46723                         this.controller = 'commandController';
46724                         this.controllerAs = 'ctrl';
46725                         this.bindToController = {
46726                             index: '=',
46727                             name: '=',
46728                             remove: '&',
46729                             list: '='
46730                         };
46731                         this.templateUrl = 'templates/command.html';
46732                     }
46733                     Command.Factory = function () {
46734                         var directive = function () {
46735                             return new Command();
46736                         };
46737                         directive.$inject = [];
46738                         return directive;
46739                     };
46740                     return Command;
46741                 })();
46742                 directives.Command = Command;
46743                 var CommandController = (function () {
46744                     function CommandController(APIEndPoint, $scope, MyModal) {
46745                         this.APIEndPoint = APIEndPoint;
46746                         this.$scope = $scope;
46747                         this.MyModal = MyModal;
46748                         var controller = this;
46749                         this.APIEndPoint
46750                             .getOptionControlFile('mrcImageNoiseAdd')
46751                             .$promise
46752                             .then(function (result) {
46753                             controller.options = result.info;
46754                         });
46755                         this.APIEndPoint
46756                             .getDirectories()
46757                             .$promise
46758                             .then(function (result) {
46759                             controller.dirs = result.info;
46760                         });
46761                         this.heading = "[" + this.index + "]: dcdFilePring";
46762                         this.isOpen = true;
46763                         this.$scope.$on('close', function () {
46764                             controller.isOpen = false;
46765                         });
46766                     }
46767                     CommandController.prototype.submit = function () {
46768                         var opt = [];
46769                         angular.forEach(this.options, function (option) {
46770                             var obj = {
46771                                 name: option.option,
46772                                 arguments: []
46773                             };
46774                             angular.forEach(option.arg, function (arg) {
46775                                 if (arg.input) {
46776                                     if (typeof arg.input === 'object') {
46777                                         obj.arguments.push(arg.input.name);
46778                                     }
46779                                     else {
46780                                         obj.arguments.push(arg.input);
46781                                     }
46782                                 }
46783                             });
46784                             if (obj.arguments.length > 0) {
46785                                 opt.push(obj);
46786                             }
46787                         });
46788                         var execObj = {
46789                             command: this.name,
46790                             workspace: this.workspace.fileId,
46791                             options: opt
46792                         };
46793                         this.APIEndPoint
46794                             .execute(JSON.stringify(execObj))
46795                             .then(function (result) {
46796                             console.log(result);
46797                         });
46798                     };
46799                     CommandController.prototype.removeMySelf = function (index) {
46800                         this.remove()(index, this.list);
46801                     };
46802                     CommandController.prototype.reloadFiles = function () {
46803                         var _this = this;
46804                         var fileId = this.workspace.fileId;
46805                         this.APIEndPoint
46806                             .getFiles(fileId)
46807                             .$promise
46808                             .then(function (result) {
46809                             var status = result.status;
46810                             if (status === 'success') {
46811                                 _this.files = result.info;
46812                             }
46813                             else {
46814                                 console.log(result.message);
46815                             }
46816                         });
46817                     };
46818                     CommandController.prototype.debug = function () {
46819                         this.MyModal.preview();
46820                     };
46821                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
46822                     return CommandController;
46823                 })();
46824                 directives.CommandController = CommandController;
46825             })(directives = app.directives || (app.directives = {}));
46826         })(app || (app = {}));
46827         var app;
46828         (function (app) {
46829             var directives;
46830             (function (directives) {
46831                 var HeaderMenu = (function () {
46832                     function HeaderMenu() {
46833                         this.restrict = 'E';
46834                         this.replace = true;
46835                         this.templateUrl = 'templates/header-menu.html';
46836                     }
46837                     HeaderMenu.Factory = function () {
46838                         var directive = function () {
46839                             return new HeaderMenu();
46840                         };
46841                         return directive;
46842                     };
46843                     return HeaderMenu;
46844                 })();
46845                 directives.HeaderMenu = HeaderMenu;
46846             })(directives = app.directives || (app.directives = {}));
46847         })(app || (app = {}));
46848         var app;
46849         (function (app) {
46850             var directives;
46851             (function (directives) {
46852                 var Option = (function () {
46853                     function Option() {
46854                         this.restrict = 'E';
46855                         this.replace = true;
46856                         this.controller = 'optionController';
46857                         this.bindToController = {
46858                             info: '=',
46859                             files: '='
46860                         };
46861                         this.scope = true;
46862                         this.templateUrl = 'templates/option.html';
46863                         this.controllerAs = 'ctrl';
46864                     }
46865                     Option.Factory = function () {
46866                         var directive = function () {
46867                             return new Option();
46868                         };
46869                         directive.$inject = [];
46870                         return directive;
46871                     };
46872                     return Option;
46873                 })();
46874                 directives.Option = Option;
46875                 var OptionController = (function () {
46876                     function OptionController() {
46877                         var controller = this;
46878                         angular.forEach(controller.info.arg, function (arg) {
46879                             if (arg.initialValue) {
46880                                 if (arg.formType === 'number') {
46881                                     arg.input = parseInt(arg.initialValue);
46882                                 }
46883                                 else {
46884                                     arg.input = arg.initialValue;
46885                                 }
46886                             }
46887                         });
46888                     }
46889                     OptionController.$inject = [];
46890                     return OptionController;
46891                 })();
46892                 directives.OptionController = OptionController;
46893             })(directives = app.directives || (app.directives = {}));
46894         })(app || (app = {}));
46895         var app;
46896         (function (app) {
46897             var directives;
46898             (function (directives) {
46899                 var Directory = (function () {
46900                     function Directory() {
46901                         this.restrict = 'E';
46902                         this.replace = true;
46903                         this.controller = 'directoryController';
46904                         this.controllerAs = 'ctrl';
46905                         this.bindToController = {
46906                             info: '=',
46907                             add: '&',
46908                             list: '=',
46909                             files: '='
46910                         };
46911                         this.templateUrl = 'templates/directory.html';
46912                     }
46913                     Directory.Factory = function () {
46914                         var directive = function () {
46915                             return new Directory();
46916                         };
46917                         return directive;
46918                     };
46919                     return Directory;
46920                 })();
46921                 directives.Directory = Directory;
46922                 var DirectoryController = (function () {
46923                     function DirectoryController(APIEndPoint, $scope) {
46924                         this.APIEndPoint = APIEndPoint;
46925                         this.$scope = $scope;
46926                         var controller = this;
46927                         this.APIEndPoint
46928                             .getFiles(this.info.fileId)
46929                             .$promise
46930                             .then(function (result) {
46931                             if (result.status === 'success') {
46932                                 controller.files = result.info;
46933                                 angular.forEach(result.info, function (file) {
46934                                     if (file.fileType === '0') {
46935                                         var o = file;
46936                                         if (controller.info.path === '/') {
46937                                             o.path = '/' + file.name;
46938                                         }
46939                                         else {
46940                                             o.path = controller.info.path + '/' + file.name;
46941                                         }
46942                                         controller.add()(o, controller.list);
46943                                     }
46944                                 });
46945                             }
46946                             ;
46947                         });
46948                     }
46949                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
46950                     return DirectoryController;
46951                 })();
46952                 directives.DirectoryController = DirectoryController;
46953             })(directives = app.directives || (app.directives = {}));
46954         })(app || (app = {}));
46955         var app;
46956         (function (app) {
46957             var controllers;
46958             (function (controllers) {
46959                 var Execution = (function () {
46960                     function Execution(MyModal, $scope) {
46961                         this.MyModal = MyModal;
46962                         this.$scope = $scope;
46963                         this.commandInfoList = [];
46964                     }
46965                     ;
46966                     Execution.prototype.add = function () {
46967                         this.$scope.$broadcast('close');
46968                         var commandInfoList = this.commandInfoList;
46969                         var commandInstance = this.MyModal.selectCommand();
46970                         commandInstance
46971                             .result
46972                             .then(function (command) {
46973                             commandInfoList.push(new app.declares.CommandInfo(command));
46974                         });
46975                     };
46976                     Execution.prototype.open = function () {
46977                         var result = this.MyModal.open('SelectCommand');
46978                         console.log(result);
46979                     };
46980                     Execution.prototype.remove = function (index, list) {
46981                         list.splice(index, 1);
46982                     };
46983                     Execution.prototype.close = function () {
46984                         console.log("close");
46985                     };
46986                     Execution.$inject = ['MyModal', '$scope'];
46987                     return Execution;
46988                 })();
46989                 controllers.Execution = Execution;
46990             })(controllers = app.controllers || (app.controllers = {}));
46991         })(app || (app = {}));
46992         var app;
46993         (function (app) {
46994             var controllers;
46995             (function (controllers) {
46996                 var Workspace = (function () {
46997                     function Workspace($scope, APIEndPoint, MyModal) {
46998                         this.$scope = $scope;
46999                         this.APIEndPoint = APIEndPoint;
47000                         this.MyModal = MyModal;
47001                         this.directoryList = [];
47002                         var controller = this;
47003                         var directoryList = this.directoryList;
47004                         var o = {
47005                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
47006                             name: '',
47007                             parentId: '',
47008                             fileType: '',
47009                             createdAt: '',
47010                             updatedAt: '',
47011                             path: '/'
47012                         };
47013                         directoryList.push(o);
47014                     }
47015                     Workspace.prototype.addDirectory = function (info, directoryList) {
47016                         directoryList.push(info);
47017                     };
47018                     Workspace.prototype.debug = function () {
47019                         this.MyModal.preview();
47020                     };
47021                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
47022                     return Workspace;
47023                 })();
47024                 controllers.Workspace = Workspace;
47025             })(controllers = app.controllers || (app.controllers = {}));
47026         })(app || (app = {}));
47027         var app;
47028         (function (app) {
47029             var controllers;
47030             (function (controllers) {
47031                 var History = (function () {
47032                     function History($scope) {
47033                         this.page = "History";
47034                     }
47035                     History.$inject = ['$scope'];
47036                     return History;
47037                 })();
47038                 controllers.History = History;
47039             })(controllers = app.controllers || (app.controllers = {}));
47040         })(app || (app = {}));
47041         var app;
47042         (function (app) {
47043             var controllers;
47044             (function (controllers) {
47045                 var SelectCommand = (function () {
47046                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
47047                         this.APIEndPoint = APIEndPoint;
47048                         this.$modalInstance = $modalInstance;
47049                         var controller = this;
47050                         this.APIEndPoint
47051                             .getTags()
47052                             .$promise.then(function (result) {
47053                             controller.tags = result.info;
47054                         });
47055                         this.APIEndPoint
47056                             .getCommands()
47057                             .$promise.then(function (result) {
47058                             controller.commands = result.info;
47059                         });
47060                         this.currentTag = 'all';
47061                     }
47062                     SelectCommand.prototype.changeTag = function (tag) {
47063                         this.currentTag = tag;
47064                     };
47065                     SelectCommand.prototype.selectCommand = function (command) {
47066                         this.$modalInstance.close(command);
47067                     };
47068                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47069                     return SelectCommand;
47070                 })();
47071                 controllers.SelectCommand = SelectCommand;
47072             })(controllers = app.controllers || (app.controllers = {}));
47073         })(app || (app = {}));
47074         var app;
47075         (function (app) {
47076             var controllers;
47077             (function (controllers) {
47078                 var Preview = (function () {
47079                     function Preview($scope, APIEndPoint, $modalInstance) {
47080                         this.APIEndPoint = APIEndPoint;
47081                         this.$modalInstance = $modalInstance;
47082                         var controller = this;
47083                         console.log('preview');
47084                     }
47085                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47086                     return Preview;
47087                 })();
47088                 controllers.Preview = Preview;
47089             })(controllers = app.controllers || (app.controllers = {}));
47090         })(app || (app = {}));
47091         var filters;
47092         (function (filters) {
47093             function Tag() {
47094                 return function (commands, tag) {
47095                     var result = [];
47096                     angular.forEach(commands, function (command) {
47097                         var flag = false;
47098                         angular.forEach(command.tags, function (value) {
47099                             if (tag === value)
47100                                 flag = true;
47101                         });
47102                         if (flag)
47103                             result.push(command);
47104                     });
47105                     return result;
47106                 };
47107             }
47108             filters.Tag = Tag;
47109         })(filters || (filters = {}));
47110         var app;
47111         (function (app) {
47112             'use strict';
47113             var appName = 'zephyr';
47114             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
47115             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
47116                 $urlRouterProvider.otherwise('/execution');
47117                 $locationProvider.html5Mode({
47118                     enabled: true,
47119                     requireBase: false
47120                 });
47121                 $stateProvider
47122                     .state('execution', {
47123                     url: '/execution',
47124                     templateUrl: 'templates/execution.html',
47125                     controller: 'executionController',
47126                     controllerAs: 'c'
47127                 })
47128                     .state('workspace', {
47129                     url: '/workspace',
47130                     templateUrl: 'templates/workspace.html',
47131                     controller: 'workspaceController',
47132                     controllerAs: 'c'
47133                 })
47134                     .state('history', {
47135                     url: '/history',
47136                     templateUrl: 'templates/history.html',
47137                     controller: 'historyController',
47138                     controllerAs: 'c'
47139                 });
47140             });
47141             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
47142             app.zephyr.filter('Tag', filters.Tag);
47143             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
47144             app.zephyr.controller('previewController', app.controllers.Preview);
47145             app.zephyr.service('MyModal', app.services.MyModal);
47146             app.zephyr.controller('executionController', app.controllers.Execution);
47147             app.zephyr.controller('workspaceController', app.controllers.Workspace);
47148             app.zephyr.controller('historyController', app.controllers.History);
47149             app.zephyr.controller('commandController', app.directives.CommandController);
47150             app.zephyr.controller('optionController', app.directives.OptionController);
47151             app.zephyr.controller('directoryController', app.directives.DirectoryController);
47152             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
47153             app.zephyr.directive('command', app.directives.Command.Factory());
47154             app.zephyr.directive('option', app.directives.Option.Factory());
47155             app.zephyr.directive('directory', app.directives.Directory.Factory());
47156         })(app || (app = {}));
47157
47158
47159 /***/ },
47160 /* 19 */
47161 /***/ function(module, exports) {
47162
47163         var app;
47164         (function (app) {
47165             var declares;
47166             (function (declares) {
47167                 var CommandInfo = (function () {
47168                     function CommandInfo(name) {
47169                         this.name = name;
47170                     }
47171                     return CommandInfo;
47172                 })();
47173                 declares.CommandInfo = CommandInfo;
47174             })(declares = app.declares || (app.declares = {}));
47175         })(app || (app = {}));
47176         var app;
47177         (function (app) {
47178             var services;
47179             (function (services) {
47180                 var APIEndPoint = (function () {
47181                     function APIEndPoint($resource, $http) {
47182                         this.$resource = $resource;
47183                         this.$http = $http;
47184                     }
47185                     APIEndPoint.prototype.resource = function (endPoint, data) {
47186                         var customAction = {
47187                             method: 'GET',
47188                             isArray: false
47189                         };
47190                         var execute = {
47191                             method: 'POST',
47192                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
47193                         };
47194                         return this.$resource(endPoint, {}, { execute: execute });
47195                     };
47196                     APIEndPoint.prototype.getOptionControlFile = function (command) {
47197                         var endPoint = '/api/v1/optionControlFile/' + command;
47198                         return this.resource(endPoint, {}).get();
47199                     };
47200                     APIEndPoint.prototype.getFiles = function (fileId) {
47201                         var endPoint = '/api/v1/workspace';
47202                         if (fileId) {
47203                             endPoint += '/' + fileId;
47204                         }
47205                         return this.resource(endPoint, {}).get();
47206                     };
47207                     APIEndPoint.prototype.getDirectories = function () {
47208                         var endPoint = '/api/v1/all/workspace/directory';
47209                         return this.resource(endPoint, {}).get();
47210                     };
47211                     APIEndPoint.prototype.getTags = function () {
47212                         var endPoint = '/api/v1/tagList';
47213                         return this.resource(endPoint, {}).get();
47214                     };
47215                     APIEndPoint.prototype.getCommands = function () {
47216                         var endPoint = '/api/v1/commandList';
47217                         return this.resource(endPoint, {}).get();
47218                     };
47219                     APIEndPoint.prototype.execute = function (data) {
47220                         var endPoint = '/api/v1/execution';
47221                         var fd = new FormData();
47222                         fd.append('data', data);
47223                         return this.$http.post(endPoint, fd, {
47224                             headers: { 'Content-Type': undefined },
47225                             transformRequest: angular.identity
47226                         });
47227                     };
47228                     return APIEndPoint;
47229                 })();
47230                 services.APIEndPoint = APIEndPoint;
47231             })(services = app.services || (app.services = {}));
47232         })(app || (app = {}));
47233         var app;
47234         (function (app) {
47235             var services;
47236             (function (services) {
47237                 var MyModal = (function () {
47238                     function MyModal($uibModal) {
47239                         this.$uibModal = $uibModal;
47240                         this.modalOption = {
47241                             backdrop: true,
47242                             controller: null,
47243                             templateUrl: null,
47244                             size: null
47245                         };
47246                     }
47247                     MyModal.prototype.open = function (modalName) {
47248                         if (modalName === 'SelectCommand') {
47249                             this.modalOption.templateUrl = 'templates/select-command.html';
47250                             this.modalOption.size = 'lg';
47251                         }
47252                         return this.$uibModal.open(this.modalOption);
47253                     };
47254                     MyModal.prototype.selectCommand = function () {
47255                         this.modalOption.templateUrl = 'templates/select-command.html';
47256                         this.modalOption.controller = 'selectCommandController';
47257                         this.modalOption.controllerAs = 'c';
47258                         this.modalOption.size = 'lg';
47259                         return this.$uibModal.open(this.modalOption);
47260                     };
47261                     MyModal.prototype.preview = function () {
47262                         this.modalOption.templateUrl = 'templates/preview.html';
47263                         this.modalOption.controller = 'previewController';
47264                         this.modalOption.controllerAs = 'c';
47265                         this.modalOption.size = 'lg';
47266                         return this.$uibModal.open(this.modalOption);
47267                     };
47268                     MyModal.$inject = ['$uibModal'];
47269                     return MyModal;
47270                 })();
47271                 services.MyModal = MyModal;
47272             })(services = app.services || (app.services = {}));
47273         })(app || (app = {}));
47274         var app;
47275         (function (app) {
47276             var directives;
47277             (function (directives) {
47278                 var Command = (function () {
47279                     function Command() {
47280                         this.restrict = 'E';
47281                         this.replace = true;
47282                         this.scope = true;
47283                         this.controller = 'commandController';
47284                         this.controllerAs = 'ctrl';
47285                         this.bindToController = {
47286                             index: '=',
47287                             name: '=',
47288                             remove: '&',
47289                             list: '='
47290                         };
47291                         this.templateUrl = 'templates/command.html';
47292                     }
47293                     Command.Factory = function () {
47294                         var directive = function () {
47295                             return new Command();
47296                         };
47297                         directive.$inject = [];
47298                         return directive;
47299                     };
47300                     return Command;
47301                 })();
47302                 directives.Command = Command;
47303                 var CommandController = (function () {
47304                     function CommandController(APIEndPoint, $scope, MyModal) {
47305                         this.APIEndPoint = APIEndPoint;
47306                         this.$scope = $scope;
47307                         this.MyModal = MyModal;
47308                         var controller = this;
47309                         this.APIEndPoint
47310                             .getOptionControlFile('mrcImageNoiseAdd')
47311                             .$promise
47312                             .then(function (result) {
47313                             controller.options = result.info;
47314                         });
47315                         this.APIEndPoint
47316                             .getDirectories()
47317                             .$promise
47318                             .then(function (result) {
47319                             controller.dirs = result.info;
47320                         });
47321                         this.heading = "[" + this.index + "]: dcdFilePring";
47322                         this.isOpen = true;
47323                         this.$scope.$on('close', function () {
47324                             controller.isOpen = false;
47325                         });
47326                     }
47327                     CommandController.prototype.submit = function () {
47328                         var opt = [];
47329                         angular.forEach(this.options, function (option) {
47330                             var obj = {
47331                                 name: option.option,
47332                                 arguments: []
47333                             };
47334                             angular.forEach(option.arg, function (arg) {
47335                                 if (arg.input) {
47336                                     if (typeof arg.input === 'object') {
47337                                         obj.arguments.push(arg.input.name);
47338                                     }
47339                                     else {
47340                                         obj.arguments.push(arg.input);
47341                                     }
47342                                 }
47343                             });
47344                             if (obj.arguments.length > 0) {
47345                                 opt.push(obj);
47346                             }
47347                         });
47348                         var execObj = {
47349                             command: this.name,
47350                             workspace: this.workspace.fileId,
47351                             options: opt
47352                         };
47353                         this.APIEndPoint
47354                             .execute(JSON.stringify(execObj))
47355                             .then(function (result) {
47356                             console.log(result);
47357                         });
47358                     };
47359                     CommandController.prototype.removeMySelf = function (index) {
47360                         this.remove()(index, this.list);
47361                     };
47362                     CommandController.prototype.reloadFiles = function () {
47363                         var _this = this;
47364                         var fileId = this.workspace.fileId;
47365                         this.APIEndPoint
47366                             .getFiles(fileId)
47367                             .$promise
47368                             .then(function (result) {
47369                             var status = result.status;
47370                             if (status === 'success') {
47371                                 _this.files = result.info;
47372                             }
47373                             else {
47374                                 console.log(result.message);
47375                             }
47376                         });
47377                     };
47378                     CommandController.prototype.debug = function () {
47379                         this.MyModal.preview();
47380                     };
47381                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
47382                     return CommandController;
47383                 })();
47384                 directives.CommandController = CommandController;
47385             })(directives = app.directives || (app.directives = {}));
47386         })(app || (app = {}));
47387         var app;
47388         (function (app) {
47389             var directives;
47390             (function (directives) {
47391                 var HeaderMenu = (function () {
47392                     function HeaderMenu() {
47393                         this.restrict = 'E';
47394                         this.replace = true;
47395                         this.templateUrl = 'templates/header-menu.html';
47396                     }
47397                     HeaderMenu.Factory = function () {
47398                         var directive = function () {
47399                             return new HeaderMenu();
47400                         };
47401                         return directive;
47402                     };
47403                     return HeaderMenu;
47404                 })();
47405                 directives.HeaderMenu = HeaderMenu;
47406             })(directives = app.directives || (app.directives = {}));
47407         })(app || (app = {}));
47408         var app;
47409         (function (app) {
47410             var directives;
47411             (function (directives) {
47412                 var Option = (function () {
47413                     function Option() {
47414                         this.restrict = 'E';
47415                         this.replace = true;
47416                         this.controller = 'optionController';
47417                         this.bindToController = {
47418                             info: '=',
47419                             files: '='
47420                         };
47421                         this.scope = true;
47422                         this.templateUrl = 'templates/option.html';
47423                         this.controllerAs = 'ctrl';
47424                     }
47425                     Option.Factory = function () {
47426                         var directive = function () {
47427                             return new Option();
47428                         };
47429                         directive.$inject = [];
47430                         return directive;
47431                     };
47432                     return Option;
47433                 })();
47434                 directives.Option = Option;
47435                 var OptionController = (function () {
47436                     function OptionController() {
47437                         var controller = this;
47438                         angular.forEach(controller.info.arg, function (arg) {
47439                             if (arg.initialValue) {
47440                                 if (arg.formType === 'number') {
47441                                     arg.input = parseInt(arg.initialValue);
47442                                 }
47443                                 else {
47444                                     arg.input = arg.initialValue;
47445                                 }
47446                             }
47447                         });
47448                     }
47449                     OptionController.$inject = [];
47450                     return OptionController;
47451                 })();
47452                 directives.OptionController = OptionController;
47453             })(directives = app.directives || (app.directives = {}));
47454         })(app || (app = {}));
47455         var app;
47456         (function (app) {
47457             var directives;
47458             (function (directives) {
47459                 var Directory = (function () {
47460                     function Directory() {
47461                         this.restrict = 'E';
47462                         this.replace = true;
47463                         this.controller = 'directoryController';
47464                         this.controllerAs = 'ctrl';
47465                         this.bindToController = {
47466                             info: '=',
47467                             add: '&',
47468                             list: '=',
47469                             files: '='
47470                         };
47471                         this.templateUrl = 'templates/directory.html';
47472                     }
47473                     Directory.Factory = function () {
47474                         var directive = function () {
47475                             return new Directory();
47476                         };
47477                         return directive;
47478                     };
47479                     return Directory;
47480                 })();
47481                 directives.Directory = Directory;
47482                 var DirectoryController = (function () {
47483                     function DirectoryController(APIEndPoint, $scope) {
47484                         this.APIEndPoint = APIEndPoint;
47485                         this.$scope = $scope;
47486                         var controller = this;
47487                         this.APIEndPoint
47488                             .getFiles(this.info.fileId)
47489                             .$promise
47490                             .then(function (result) {
47491                             if (result.status === 'success') {
47492                                 controller.files = result.info;
47493                                 angular.forEach(result.info, function (file) {
47494                                     if (file.fileType === '0') {
47495                                         var o = file;
47496                                         if (controller.info.path === '/') {
47497                                             o.path = '/' + file.name;
47498                                         }
47499                                         else {
47500                                             o.path = controller.info.path + '/' + file.name;
47501                                         }
47502                                         controller.add()(o, controller.list);
47503                                     }
47504                                 });
47505                             }
47506                             ;
47507                         });
47508                     }
47509                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
47510                     return DirectoryController;
47511                 })();
47512                 directives.DirectoryController = DirectoryController;
47513             })(directives = app.directives || (app.directives = {}));
47514         })(app || (app = {}));
47515         var app;
47516         (function (app) {
47517             var controllers;
47518             (function (controllers) {
47519                 var Execution = (function () {
47520                     function Execution(MyModal, $scope) {
47521                         this.MyModal = MyModal;
47522                         this.$scope = $scope;
47523                         this.commandInfoList = [];
47524                     }
47525                     ;
47526                     Execution.prototype.add = function () {
47527                         this.$scope.$broadcast('close');
47528                         var commandInfoList = this.commandInfoList;
47529                         var commandInstance = this.MyModal.selectCommand();
47530                         commandInstance
47531                             .result
47532                             .then(function (command) {
47533                             commandInfoList.push(new app.declares.CommandInfo(command));
47534                         });
47535                     };
47536                     Execution.prototype.open = function () {
47537                         var result = this.MyModal.open('SelectCommand');
47538                         console.log(result);
47539                     };
47540                     Execution.prototype.remove = function (index, list) {
47541                         list.splice(index, 1);
47542                     };
47543                     Execution.prototype.close = function () {
47544                         console.log("close");
47545                     };
47546                     Execution.$inject = ['MyModal', '$scope'];
47547                     return Execution;
47548                 })();
47549                 controllers.Execution = Execution;
47550             })(controllers = app.controllers || (app.controllers = {}));
47551         })(app || (app = {}));
47552         var app;
47553         (function (app) {
47554             var controllers;
47555             (function (controllers) {
47556                 var Workspace = (function () {
47557                     function Workspace($scope, APIEndPoint, MyModal) {
47558                         this.$scope = $scope;
47559                         this.APIEndPoint = APIEndPoint;
47560                         this.MyModal = MyModal;
47561                         this.directoryList = [];
47562                         var controller = this;
47563                         var directoryList = this.directoryList;
47564                         var o = {
47565                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
47566                             name: '',
47567                             parentId: '',
47568                             fileType: '',
47569                             createdAt: '',
47570                             updatedAt: '',
47571                             path: '/'
47572                         };
47573                         directoryList.push(o);
47574                     }
47575                     Workspace.prototype.addDirectory = function (info, directoryList) {
47576                         directoryList.push(info);
47577                     };
47578                     Workspace.prototype.debug = function () {
47579                         this.MyModal.preview();
47580                     };
47581                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
47582                     return Workspace;
47583                 })();
47584                 controllers.Workspace = Workspace;
47585             })(controllers = app.controllers || (app.controllers = {}));
47586         })(app || (app = {}));
47587         var app;
47588         (function (app) {
47589             var controllers;
47590             (function (controllers) {
47591                 var History = (function () {
47592                     function History($scope) {
47593                         this.page = "History";
47594                     }
47595                     History.$inject = ['$scope'];
47596                     return History;
47597                 })();
47598                 controllers.History = History;
47599             })(controllers = app.controllers || (app.controllers = {}));
47600         })(app || (app = {}));
47601         var app;
47602         (function (app) {
47603             var controllers;
47604             (function (controllers) {
47605                 var SelectCommand = (function () {
47606                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
47607                         this.APIEndPoint = APIEndPoint;
47608                         this.$modalInstance = $modalInstance;
47609                         var controller = this;
47610                         this.APIEndPoint
47611                             .getTags()
47612                             .$promise.then(function (result) {
47613                             controller.tags = result.info;
47614                         });
47615                         this.APIEndPoint
47616                             .getCommands()
47617                             .$promise.then(function (result) {
47618                             controller.commands = result.info;
47619                         });
47620                         this.currentTag = 'all';
47621                     }
47622                     SelectCommand.prototype.changeTag = function (tag) {
47623                         this.currentTag = tag;
47624                     };
47625                     SelectCommand.prototype.selectCommand = function (command) {
47626                         this.$modalInstance.close(command);
47627                     };
47628                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47629                     return SelectCommand;
47630                 })();
47631                 controllers.SelectCommand = SelectCommand;
47632             })(controllers = app.controllers || (app.controllers = {}));
47633         })(app || (app = {}));
47634         var app;
47635         (function (app) {
47636             var controllers;
47637             (function (controllers) {
47638                 var Preview = (function () {
47639                     function Preview($scope, APIEndPoint, $modalInstance) {
47640                         this.APIEndPoint = APIEndPoint;
47641                         this.$modalInstance = $modalInstance;
47642                         var controller = this;
47643                         console.log('preview');
47644                     }
47645                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
47646                     return Preview;
47647                 })();
47648                 controllers.Preview = Preview;
47649             })(controllers = app.controllers || (app.controllers = {}));
47650         })(app || (app = {}));
47651         var filters;
47652         (function (filters) {
47653             function Tag() {
47654                 return function (commands, tag) {
47655                     var result = [];
47656                     angular.forEach(commands, function (command) {
47657                         var flag = false;
47658                         angular.forEach(command.tags, function (value) {
47659                             if (tag === value)
47660                                 flag = true;
47661                         });
47662                         if (flag)
47663                             result.push(command);
47664                     });
47665                     return result;
47666                 };
47667             }
47668             filters.Tag = Tag;
47669         })(filters || (filters = {}));
47670         var app;
47671         (function (app) {
47672             'use strict';
47673             var appName = 'zephyr';
47674             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
47675             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
47676                 $urlRouterProvider.otherwise('/execution');
47677                 $locationProvider.html5Mode({
47678                     enabled: true,
47679                     requireBase: false
47680                 });
47681                 $stateProvider
47682                     .state('execution', {
47683                     url: '/execution',
47684                     templateUrl: 'templates/execution.html',
47685                     controller: 'executionController',
47686                     controllerAs: 'c'
47687                 })
47688                     .state('workspace', {
47689                     url: '/workspace',
47690                     templateUrl: 'templates/workspace.html',
47691                     controller: 'workspaceController',
47692                     controllerAs: 'c'
47693                 })
47694                     .state('history', {
47695                     url: '/history',
47696                     templateUrl: 'templates/history.html',
47697                     controller: 'historyController',
47698                     controllerAs: 'c'
47699                 });
47700             });
47701             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
47702             app.zephyr.filter('Tag', filters.Tag);
47703             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
47704             app.zephyr.controller('previewController', app.controllers.Preview);
47705             app.zephyr.service('MyModal', app.services.MyModal);
47706             app.zephyr.controller('executionController', app.controllers.Execution);
47707             app.zephyr.controller('workspaceController', app.controllers.Workspace);
47708             app.zephyr.controller('historyController', app.controllers.History);
47709             app.zephyr.controller('commandController', app.directives.CommandController);
47710             app.zephyr.controller('optionController', app.directives.OptionController);
47711             app.zephyr.controller('directoryController', app.directives.DirectoryController);
47712             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
47713             app.zephyr.directive('command', app.directives.Command.Factory());
47714             app.zephyr.directive('option', app.directives.Option.Factory());
47715             app.zephyr.directive('directory', app.directives.Directory.Factory());
47716         })(app || (app = {}));
47717
47718
47719 /***/ },
47720 /* 20 */
47721 /***/ function(module, exports) {
47722
47723         var app;
47724         (function (app) {
47725             var declares;
47726             (function (declares) {
47727                 var CommandInfo = (function () {
47728                     function CommandInfo(name) {
47729                         this.name = name;
47730                     }
47731                     return CommandInfo;
47732                 })();
47733                 declares.CommandInfo = CommandInfo;
47734             })(declares = app.declares || (app.declares = {}));
47735         })(app || (app = {}));
47736         var app;
47737         (function (app) {
47738             var services;
47739             (function (services) {
47740                 var APIEndPoint = (function () {
47741                     function APIEndPoint($resource, $http) {
47742                         this.$resource = $resource;
47743                         this.$http = $http;
47744                     }
47745                     APIEndPoint.prototype.resource = function (endPoint, data) {
47746                         var customAction = {
47747                             method: 'GET',
47748                             isArray: false
47749                         };
47750                         var execute = {
47751                             method: 'POST',
47752                             headers: { 'Content-Type': undefined, enctype: 'multipart/form-data' }
47753                         };
47754                         return this.$resource(endPoint, {}, { execute: execute });
47755                     };
47756                     APIEndPoint.prototype.getOptionControlFile = function (command) {
47757                         var endPoint = '/api/v1/optionControlFile/' + command;
47758                         return this.resource(endPoint, {}).get();
47759                     };
47760                     APIEndPoint.prototype.getFiles = function (fileId) {
47761                         var endPoint = '/api/v1/workspace';
47762                         if (fileId) {
47763                             endPoint += '/' + fileId;
47764                         }
47765                         return this.resource(endPoint, {}).get();
47766                     };
47767                     APIEndPoint.prototype.getDirectories = function () {
47768                         var endPoint = '/api/v1/all/workspace/directory';
47769                         return this.resource(endPoint, {}).get();
47770                     };
47771                     APIEndPoint.prototype.getTags = function () {
47772                         var endPoint = '/api/v1/tagList';
47773                         return this.resource(endPoint, {}).get();
47774                     };
47775                     APIEndPoint.prototype.getCommands = function () {
47776                         var endPoint = '/api/v1/commandList';
47777                         return this.resource(endPoint, {}).get();
47778                     };
47779                     APIEndPoint.prototype.execute = function (data) {
47780                         var endPoint = '/api/v1/execution';
47781                         var fd = new FormData();
47782                         fd.append('data', data);
47783                         return this.$http.post(endPoint, fd, {
47784                             headers: { 'Content-Type': undefined },
47785                             transformRequest: angular.identity
47786                         });
47787                     };
47788                     return APIEndPoint;
47789                 })();
47790                 services.APIEndPoint = APIEndPoint;
47791             })(services = app.services || (app.services = {}));
47792         })(app || (app = {}));
47793         var app;
47794         (function (app) {
47795             var services;
47796             (function (services) {
47797                 var MyModal = (function () {
47798                     function MyModal($uibModal) {
47799                         this.$uibModal = $uibModal;
47800                         this.modalOption = {
47801                             backdrop: true,
47802                             controller: null,
47803                             templateUrl: null,
47804                             size: null
47805                         };
47806                     }
47807                     MyModal.prototype.open = function (modalName) {
47808                         if (modalName === 'SelectCommand') {
47809                             this.modalOption.templateUrl = 'templates/select-command.html';
47810                             this.modalOption.size = 'lg';
47811                         }
47812                         return this.$uibModal.open(this.modalOption);
47813                     };
47814                     MyModal.prototype.selectCommand = function () {
47815                         this.modalOption.templateUrl = 'templates/select-command.html';
47816                         this.modalOption.controller = 'selectCommandController';
47817                         this.modalOption.controllerAs = 'c';
47818                         this.modalOption.size = 'lg';
47819                         return this.$uibModal.open(this.modalOption);
47820                     };
47821                     MyModal.prototype.preview = function () {
47822                         this.modalOption.templateUrl = 'templates/preview.html';
47823                         this.modalOption.controller = 'previewController';
47824                         this.modalOption.controllerAs = 'c';
47825                         this.modalOption.size = 'lg';
47826                         return this.$uibModal.open(this.modalOption);
47827                     };
47828                     MyModal.$inject = ['$uibModal'];
47829                     return MyModal;
47830                 })();
47831                 services.MyModal = MyModal;
47832             })(services = app.services || (app.services = {}));
47833         })(app || (app = {}));
47834         var app;
47835         (function (app) {
47836             var directives;
47837             (function (directives) {
47838                 var Command = (function () {
47839                     function Command() {
47840                         this.restrict = 'E';
47841                         this.replace = true;
47842                         this.scope = true;
47843                         this.controller = 'commandController';
47844                         this.controllerAs = 'ctrl';
47845                         this.bindToController = {
47846                             index: '=',
47847                             name: '=',
47848                             remove: '&',
47849                             list: '='
47850                         };
47851                         this.templateUrl = 'templates/command.html';
47852                     }
47853                     Command.Factory = function () {
47854                         var directive = function () {
47855                             return new Command();
47856                         };
47857                         directive.$inject = [];
47858                         return directive;
47859                     };
47860                     return Command;
47861                 })();
47862                 directives.Command = Command;
47863                 var CommandController = (function () {
47864                     function CommandController(APIEndPoint, $scope, MyModal) {
47865                         this.APIEndPoint = APIEndPoint;
47866                         this.$scope = $scope;
47867                         this.MyModal = MyModal;
47868                         var controller = this;
47869                         this.APIEndPoint
47870                             .getOptionControlFile('mrcImageNoiseAdd')
47871                             .$promise
47872                             .then(function (result) {
47873                             controller.options = result.info;
47874                         });
47875                         this.APIEndPoint
47876                             .getDirectories()
47877                             .$promise
47878                             .then(function (result) {
47879                             controller.dirs = result.info;
47880                         });
47881                         this.heading = "[" + this.index + "]: dcdFilePring";
47882                         this.isOpen = true;
47883                         this.$scope.$on('close', function () {
47884                             controller.isOpen = false;
47885                         });
47886                     }
47887                     CommandController.prototype.submit = function () {
47888                         var opt = [];
47889                         angular.forEach(this.options, function (option) {
47890                             var obj = {
47891                                 name: option.option,
47892                                 arguments: []
47893                             };
47894                             angular.forEach(option.arg, function (arg) {
47895                                 if (arg.input) {
47896                                     if (typeof arg.input === 'object') {
47897                                         obj.arguments.push(arg.input.name);
47898                                     }
47899                                     else {
47900                                         obj.arguments.push(arg.input);
47901                                     }
47902                                 }
47903                             });
47904                             if (obj.arguments.length > 0) {
47905                                 opt.push(obj);
47906                             }
47907                         });
47908                         var execObj = {
47909                             command: this.name,
47910                             workspace: this.workspace.fileId,
47911                             options: opt
47912                         };
47913                         this.APIEndPoint
47914                             .execute(JSON.stringify(execObj))
47915                             .then(function (result) {
47916                             console.log(result);
47917                         });
47918                     };
47919                     CommandController.prototype.removeMySelf = function (index) {
47920                         this.remove()(index, this.list);
47921                     };
47922                     CommandController.prototype.reloadFiles = function () {
47923                         var _this = this;
47924                         var fileId = this.workspace.fileId;
47925                         this.APIEndPoint
47926                             .getFiles(fileId)
47927                             .$promise
47928                             .then(function (result) {
47929                             var status = result.status;
47930                             if (status === 'success') {
47931                                 _this.files = result.info;
47932                             }
47933                             else {
47934                                 console.log(result.message);
47935                             }
47936                         });
47937                     };
47938                     CommandController.prototype.debug = function () {
47939                         this.MyModal.preview();
47940                     };
47941                     CommandController.$inject = ['APIEndPoint', '$scope', 'MyModal'];
47942                     return CommandController;
47943                 })();
47944                 directives.CommandController = CommandController;
47945             })(directives = app.directives || (app.directives = {}));
47946         })(app || (app = {}));
47947         var app;
47948         (function (app) {
47949             var directives;
47950             (function (directives) {
47951                 var HeaderMenu = (function () {
47952                     function HeaderMenu() {
47953                         this.restrict = 'E';
47954                         this.replace = true;
47955                         this.templateUrl = 'templates/header-menu.html';
47956                     }
47957                     HeaderMenu.Factory = function () {
47958                         var directive = function () {
47959                             return new HeaderMenu();
47960                         };
47961                         return directive;
47962                     };
47963                     return HeaderMenu;
47964                 })();
47965                 directives.HeaderMenu = HeaderMenu;
47966             })(directives = app.directives || (app.directives = {}));
47967         })(app || (app = {}));
47968         var app;
47969         (function (app) {
47970             var directives;
47971             (function (directives) {
47972                 var Option = (function () {
47973                     function Option() {
47974                         this.restrict = 'E';
47975                         this.replace = true;
47976                         this.controller = 'optionController';
47977                         this.bindToController = {
47978                             info: '=',
47979                             files: '='
47980                         };
47981                         this.scope = true;
47982                         this.templateUrl = 'templates/option.html';
47983                         this.controllerAs = 'ctrl';
47984                     }
47985                     Option.Factory = function () {
47986                         var directive = function () {
47987                             return new Option();
47988                         };
47989                         directive.$inject = [];
47990                         return directive;
47991                     };
47992                     return Option;
47993                 })();
47994                 directives.Option = Option;
47995                 var OptionController = (function () {
47996                     function OptionController() {
47997                         var controller = this;
47998                         angular.forEach(controller.info.arg, function (arg) {
47999                             if (arg.initialValue) {
48000                                 if (arg.formType === 'number') {
48001                                     arg.input = parseInt(arg.initialValue);
48002                                 }
48003                                 else {
48004                                     arg.input = arg.initialValue;
48005                                 }
48006                             }
48007                         });
48008                     }
48009                     OptionController.$inject = [];
48010                     return OptionController;
48011                 })();
48012                 directives.OptionController = OptionController;
48013             })(directives = app.directives || (app.directives = {}));
48014         })(app || (app = {}));
48015         var app;
48016         (function (app) {
48017             var directives;
48018             (function (directives) {
48019                 var Directory = (function () {
48020                     function Directory() {
48021                         this.restrict = 'E';
48022                         this.replace = true;
48023                         this.controller = 'directoryController';
48024                         this.controllerAs = 'ctrl';
48025                         this.bindToController = {
48026                             info: '=',
48027                             add: '&',
48028                             list: '=',
48029                             files: '='
48030                         };
48031                         this.templateUrl = 'templates/directory.html';
48032                     }
48033                     Directory.Factory = function () {
48034                         var directive = function () {
48035                             return new Directory();
48036                         };
48037                         return directive;
48038                     };
48039                     return Directory;
48040                 })();
48041                 directives.Directory = Directory;
48042                 var DirectoryController = (function () {
48043                     function DirectoryController(APIEndPoint, $scope) {
48044                         this.APIEndPoint = APIEndPoint;
48045                         this.$scope = $scope;
48046                         var controller = this;
48047                         this.APIEndPoint
48048                             .getFiles(this.info.fileId)
48049                             .$promise
48050                             .then(function (result) {
48051                             if (result.status === 'success') {
48052                                 controller.files = result.info;
48053                                 angular.forEach(result.info, function (file) {
48054                                     if (file.fileType === '0') {
48055                                         var o = file;
48056                                         if (controller.info.path === '/') {
48057                                             o.path = '/' + file.name;
48058                                         }
48059                                         else {
48060                                             o.path = controller.info.path + '/' + file.name;
48061                                         }
48062                                         controller.add()(o, controller.list);
48063                                     }
48064                                 });
48065                             }
48066                             ;
48067                         });
48068                     }
48069                     DirectoryController.$inject = ['APIEndPoint', '$scope'];
48070                     return DirectoryController;
48071                 })();
48072                 directives.DirectoryController = DirectoryController;
48073             })(directives = app.directives || (app.directives = {}));
48074         })(app || (app = {}));
48075         var app;
48076         (function (app) {
48077             var controllers;
48078             (function (controllers) {
48079                 var Execution = (function () {
48080                     function Execution(MyModal, $scope) {
48081                         this.MyModal = MyModal;
48082                         this.$scope = $scope;
48083                         this.commandInfoList = [];
48084                     }
48085                     ;
48086                     Execution.prototype.add = function () {
48087                         this.$scope.$broadcast('close');
48088                         var commandInfoList = this.commandInfoList;
48089                         var commandInstance = this.MyModal.selectCommand();
48090                         commandInstance
48091                             .result
48092                             .then(function (command) {
48093                             commandInfoList.push(new app.declares.CommandInfo(command));
48094                         });
48095                     };
48096                     Execution.prototype.open = function () {
48097                         var result = this.MyModal.open('SelectCommand');
48098                         console.log(result);
48099                     };
48100                     Execution.prototype.remove = function (index, list) {
48101                         list.splice(index, 1);
48102                     };
48103                     Execution.prototype.close = function () {
48104                         console.log("close");
48105                     };
48106                     Execution.$inject = ['MyModal', '$scope'];
48107                     return Execution;
48108                 })();
48109                 controllers.Execution = Execution;
48110             })(controllers = app.controllers || (app.controllers = {}));
48111         })(app || (app = {}));
48112         var app;
48113         (function (app) {
48114             var controllers;
48115             (function (controllers) {
48116                 var Workspace = (function () {
48117                     function Workspace($scope, APIEndPoint, MyModal) {
48118                         this.$scope = $scope;
48119                         this.APIEndPoint = APIEndPoint;
48120                         this.MyModal = MyModal;
48121                         this.directoryList = [];
48122                         var controller = this;
48123                         var directoryList = this.directoryList;
48124                         var o = {
48125                             fileId: '1f83f620-c1ed-11e5-9657-7942989daa00',
48126                             name: '',
48127                             parentId: '',
48128                             fileType: '',
48129                             createdAt: '',
48130                             updatedAt: '',
48131                             path: '/'
48132                         };
48133                         directoryList.push(o);
48134                     }
48135                     Workspace.prototype.addDirectory = function (info, directoryList) {
48136                         directoryList.push(info);
48137                     };
48138                     Workspace.prototype.debug = function () {
48139                         this.MyModal.preview();
48140                     };
48141                     Workspace.$inject = ['$scope', 'APIEndPoint', 'MyModal'];
48142                     return Workspace;
48143                 })();
48144                 controllers.Workspace = Workspace;
48145             })(controllers = app.controllers || (app.controllers = {}));
48146         })(app || (app = {}));
48147         var app;
48148         (function (app) {
48149             var controllers;
48150             (function (controllers) {
48151                 var History = (function () {
48152                     function History($scope) {
48153                         this.page = "History";
48154                     }
48155                     History.$inject = ['$scope'];
48156                     return History;
48157                 })();
48158                 controllers.History = History;
48159             })(controllers = app.controllers || (app.controllers = {}));
48160         })(app || (app = {}));
48161         var app;
48162         (function (app) {
48163             var controllers;
48164             (function (controllers) {
48165                 var SelectCommand = (function () {
48166                     function SelectCommand($scope, APIEndPoint, $modalInstance) {
48167                         this.APIEndPoint = APIEndPoint;
48168                         this.$modalInstance = $modalInstance;
48169                         var controller = this;
48170                         this.APIEndPoint
48171                             .getTags()
48172                             .$promise.then(function (result) {
48173                             controller.tags = result.info;
48174                         });
48175                         this.APIEndPoint
48176                             .getCommands()
48177                             .$promise.then(function (result) {
48178                             controller.commands = result.info;
48179                         });
48180                         this.currentTag = 'all';
48181                     }
48182                     SelectCommand.prototype.changeTag = function (tag) {
48183                         this.currentTag = tag;
48184                     };
48185                     SelectCommand.prototype.selectCommand = function (command) {
48186                         this.$modalInstance.close(command);
48187                     };
48188                     SelectCommand.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
48189                     return SelectCommand;
48190                 })();
48191                 controllers.SelectCommand = SelectCommand;
48192             })(controllers = app.controllers || (app.controllers = {}));
48193         })(app || (app = {}));
48194         var app;
48195         (function (app) {
48196             var controllers;
48197             (function (controllers) {
48198                 var Preview = (function () {
48199                     function Preview($scope, APIEndPoint, $modalInstance) {
48200                         this.APIEndPoint = APIEndPoint;
48201                         this.$modalInstance = $modalInstance;
48202                         var controller = this;
48203                         console.log('preview');
48204                     }
48205                     Preview.$inject = ['$scope', 'APIEndPoint', '$uibModalInstance'];
48206                     return Preview;
48207                 })();
48208                 controllers.Preview = Preview;
48209             })(controllers = app.controllers || (app.controllers = {}));
48210         })(app || (app = {}));
48211         var filters;
48212         (function (filters) {
48213             function Tag() {
48214                 return function (commands, tag) {
48215                     var result = [];
48216                     angular.forEach(commands, function (command) {
48217                         var flag = false;
48218                         angular.forEach(command.tags, function (value) {
48219                             if (tag === value)
48220                                 flag = true;
48221                         });
48222                         if (flag)
48223                             result.push(command);
48224                     });
48225                     return result;
48226                 };
48227             }
48228             filters.Tag = Tag;
48229         })(filters || (filters = {}));
48230         var app;
48231         (function (app) {
48232             'use strict';
48233             var appName = 'zephyr';
48234             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
48235             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
48236                 $urlRouterProvider.otherwise('/execution');
48237                 $locationProvider.html5Mode({
48238                     enabled: true,
48239                     requireBase: false
48240                 });
48241                 $stateProvider
48242                     .state('execution', {
48243                     url: '/execution',
48244                     templateUrl: 'templates/execution.html',
48245                     controller: 'executionController',
48246                     controllerAs: 'c'
48247                 })
48248                     .state('workspace', {
48249                     url: '/workspace',
48250                     templateUrl: 'templates/workspace.html',
48251                     controller: 'workspaceController',
48252                     controllerAs: 'c'
48253                 })
48254                     .state('history', {
48255                     url: '/history',
48256                     templateUrl: 'templates/history.html',
48257                     controller: 'historyController',
48258                     controllerAs: 'c'
48259                 });
48260             });
48261             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
48262             app.zephyr.filter('Tag', filters.Tag);
48263             app.zephyr.controller('selectCommandController', app.controllers.SelectCommand);
48264             app.zephyr.controller('previewController', app.controllers.Preview);
48265             app.zephyr.service('MyModal', app.services.MyModal);
48266             app.zephyr.controller('executionController', app.controllers.Execution);
48267             app.zephyr.controller('workspaceController', app.controllers.Workspace);
48268             app.zephyr.controller('historyController', app.controllers.History);
48269             app.zephyr.controller('commandController', app.directives.CommandController);
48270             app.zephyr.controller('optionController', app.directives.OptionController);
48271             app.zephyr.controller('directoryController', app.directives.DirectoryController);
48272             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
48273             app.zephyr.directive('command', app.directives.Command.Factory());
48274             app.zephyr.directive('option', app.directives.Option.Factory());
48275             app.zephyr.directive('directory', app.directives.Directory.Factory());
48276         })(app || (app = {}));
48277
48278
48279 /***/ }
48280 /******/ ]);